/usr/share/perl5/LaTeXML/Package.pm is in latexml 0.7.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 | # /=====================================================================\ #
# | LaTeXML::Package | #
# | Exports of Defining forms for Package writers | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package;
use strict;
use Exporter;
use LaTeXML::Global;
use LaTeXML::Definition;
use LaTeXML::Parameters;
use LaTeXML::Util::Pathname;
use Text::Balanced;
use base qw(Exporter);
our @EXPORT = (qw(&DefExpandable
&DefMacro &DefMacroI
&DefPrimitive &DefPrimitiveI &DefRegister &DefRegisterI
&DefConstructor &DefConstructorI
&dualize_arglist
&DefMath &DefMathI &DefEnvironment &DefEnvironmentI
&convertLaTeXArgs),
# Class, Package and File loading.
qw(&RequirePackage &LoadClass &FindFile
&DeclareOption &PassOptions &ProcessOptions &ExecuteOptions
&AddToMacro),
# Counter support
qw(&NewCounter &CounterValue &StepCounter &RefStepCounter &RefStepID &ResetCounter
&GenerateID),
# Document Model
qw(&Tag &DocType &RelaxNGSchema &RegisterNamespace),
# Document Rewriting
qw(&DefRewrite &DefMathRewrite
&DefLigature &DefMathLigature),
# Mid-level support for writing definitions.
qw(&Expand &Invocation &Digest
&RawTeX &Let),
# Support for structured/argument readers
qw(&ReadParameters &DefParameterType &DefColumnType),
# Access to State
qw(&LookupValue &AssignValue
&PushValue &PopValue &UnshiftValue &ShiftValue
&LookupCatcode &AssignCatcode
&LookupMeaning &LookupDefinition &InstallDefinition),
# Random low-level token or string operations.
qw(&CleanLabel &CleanIndexKey &CleanBibKey &CleanURL
&UTF
&roman &Roman),
# Math & font state.
qw(&MergeFont),
qw(&CheckOptions),
@LaTeXML::Global::EXPORT);
#**********************************************************************
# Initially, I thought LaTeXML Packages should try to be like perl modules:
# once loaded, you didn't need to re-load them, only `initialize' them to
# install their definitions into the current stomach. I tried to achieve
# that through various package tricks.
# But ultimately, most of a package _is_ installing defns in the stomach,
# and it's probably better to allow a more TeX-like evaluation of definitions
# in order, so \let and such work as expected.
# So, it got simpler!
# Still, it would be nice if there were `compiled' forms of .ltxml files!
#**********************************************************************
sub UTF {
my($code)=@_;
pack('U',$code); }
sub coerceCS {
my($cs)=@_;
$cs = T_CS($cs) unless ref $cs;
$cs = T_CS(ToString($cs)) unless ref $cs eq 'LaTeXML::Token';
$cs;}
sub parsePrototype {
my($proto)=@_;
my $oproto = $proto;
my $cs;
if($proto =~ s/^\\csname\s+(.*)\\endcsname//){
$cs = T_CS('\\'.$1); }
elsif($proto =~ s/^(\\[a-zA-Z@]+)//){ # Match a cs
$cs = T_CS($1); }
elsif($proto =~ s/^(\\.)//){ # Match a single char cs, env name,...
$cs = T_CS($1); }
elsif($proto =~ s/^(.)//){ # Match an active char
($cs) = TokenizeInternal($1)->unlist; }
else {
Fatal(":misdefined:$proto Definition prototype doesn't have proper control sequence: \"$proto\""); }
$proto =~ s/^\s*//;
($cs, parseParameters($proto,$cs)); }
# Convert a LaTeX-style argument spec to our Package form.
# Ie. given $nargs and $optional, being the two optional arguments to
# something like \newcommand, convert it to the form we use
sub XXXXconvertLaTeXArgs {
my($nargs,$optional)=@_;
$nargs = (ref $nargs ? $nargs->toString : $nargs || 0);
my $default = ($optional ? $optional->toString : undef);
join('', ($optional ? ($default ? "[Default:$default]" : "[]") : ''),
map('{}',1..($optional ? $nargs-1 : $nargs))); }
sub convertLaTeXArgs {
my($nargs,$optional)=@_;
$nargs = $nargs->toString if ref $nargs;
$nargs = 0 unless $nargs;
my @params = ();
if($optional){
push(@params,LaTeXML::Parameters::newParameter('Optional',
"[Default:".$optional->toString."]",
extra=>[$optional,undef]));
$nargs--; }
push(@params,map(LaTeXML::Parameters::newParameter('Plain','{}'), 1..$nargs));
LaTeXML::Parameters->new(@params); }
#======================================================================
# Convenience functions for writing definitions.
#======================================================================
sub LookupValue { $STATE->lookupValue(@_); }
sub AssignValue { $STATE->assignValue(@_); return; }
sub PushValue { $STATE->pushValue(@_); return; }
sub PopValue { $STATE->popValue(@_); }
sub UnshiftValue { $STATE->unshiftValue(@_); return; }
sub ShiftValue { $STATE->shiftValue(@_); }
sub LookupCatcode{ $STATE->lookupCatcode(@_); }
sub AssignCatcode{ $STATE->assignCatcode(@_); return; }
sub LookupMeaning { $STATE->lookupMeaning(@_); }
sub LookupDefinition { $STATE->lookupDefinition(@_); }
sub InstallDefinition { $STATE->installDefinition(@_); }
sub Let {
my($token1,$token2,$scope)=@_;
($token1)=TokenizeInternal($token1)->unlist unless ref $token1;
($token2)=TokenizeInternal($token2)->unlist unless ref $token2;
$STATE->assignMeaning($token1,$STATE->lookupMeaning($token2),$scope);
return; }
sub Digest {
$STATE->getStomach->digest(map((ref $_ ? $_ : Tokenize($_)),@_)); }
sub ReadParameters {
my($gullet,$spec)=@_;
my $for = T_OTHER("Anonymous");
my $parm = parseParameters($spec,$for);
($parm ? $parm->readArguments($gullet,$for) : ()); }
# Merge the current font with the style specifications
sub MergeFont { AssignValue(font=>LookupValue('font')->merge(@_), 'local'); }
# Dumb place for this, but where else...
# The TeX way! (bah!! hint: try a large number)
my @rmletters=('i','v', 'x','l', 'c','d', 'm');
sub roman_aux {
my($n)=@_;
my $div= 1000;
my $s=($n>$div ? ('m' x int($n/$div)) : '');
my $p=4;
while($n %= $div){
$div /= 10;
my $d = int($n/$div);
if($d%5==4){ $s.= $rmletters[$p]; $d++;}
if($d > 4 ){ $s.= $rmletters[$p+int($d/5)]; $d %=5; }
if($d) { $s.= $rmletters[$p] x $d; }
$p -= 2;}
$s; }
# Convert the number to lower case roman numerals, returning a list of LaTeXML::Token
sub roman { Explode(roman_aux(@_)); }
# Convert the number to upper case roman numerals, returning a list of LaTeXML::Token
sub Roman { Explode(uc(roman_aux(@_))); }
#======================================================================
# Cleaners
#======================================================================
# Gradually rethink all these and clarify.
# Are they intended to be valid ID's (or fragment ids?)
sub CleanLabel {
my($label,$prefix)=@_;
my $key = ToString($label);
$key =~ s/\s+/_/g;
($prefix||"LABEL").":".$key; }
sub CleanIndexKey {
my($key)=@_;
$key = ToString($key);
$key =~ s/[^a-zA-Z0-9]//g;
## Shouldn't be case insensitive?
## $key =~ tr|A-Z|a-z|;
$key; }
# used as id.
sub CleanBibKey {
my($key)=@_;
$key = lc(ToString($key));
$key =~ s/\s//g;
$key; }
sub CleanURL {
my($url)=@_;
$url = ToString($url);
$url =~ s/\\~{}/~/g;
$url; }
#======================================================================
# Defining new Control-sequence Parameter types.
#======================================================================
our $parameter_options = {nargs=>1,reversion=>1,optional=>1,novalue=>1,
semiverbatim=>1,undigested=>1};
sub DefParameterType {
my($type,$reader,%options)=@_;
CheckOptions("DefParameterType $type",$parameter_options,%options);
$LaTeXML::Parameters::PARAMETER_TABLE{$type}={reader=>$reader,%options};
return;
}
sub DefColumnType {
my($proto,$expansion)=@_;
$proto =~ s/^(.)//;
my $char = $1;
$proto =~ s/^\s*//;
my $params = parseParameters($proto,$char);
$expansion = TokenizeInternal($expansion) unless ref $expansion;
DefMacroI(T_CS('\NC@rewrite@'.$char),$params,$expansion); }
#======================================================================
# Counters
#======================================================================
# This is modelled on LaTeX's counter mechanisms, but since it also
# provides support for ID's, even where there is no visible reference number,
# it is defined in genera.
# These id's should be both unique, and parallel the visible reference numbers
# (as much as possible). Also, for consistency, we add id's to unnumbered
# document elements (eg from \section*); this requires an additional counter
# (eg. UNsection) and mechanisms to track it.
# Defines a new counter named $ctr.
# If $within is defined, $ctr will be reset whenever $within is incremented.
# Keywords:
# idprefix : specifies a prefix to be used in formatting ID's for document structure elements
# counted by this counter. Ie. subsection 3 in section 2 might get: id="S2.SS3"
# idwithin : specifies that the ID is composed from $idwithin's ID,, even though
# the counter isn't numbered within it. (mainly to avoid duplicated ids)
# nested : a list of counters that correspond to scopes which are "inside" this one.
# Whenever any definitions scoped to this counter are deactivated,
# the inner counter's scopes are also deactivated.
# NOTE: I'm not sure this is even a sensible implementation,
# or why inner should be different than the counters reset by incrementing this counter.
sub NewCounter {
my($ctr,$within,%options)=@_;
my $unctr = "UN$ctr"; # UNctr is counter for generating ID's for UN-numbered items.
DefRegisterI(T_CS("\\c\@$ctr"),undef,Number(0));
AssignValue("\\c\@$ctr"=>Number(0),'global');
AssignValue("\\cl\@$ctr"=>Tokens(),'global') unless LookupValue("\\cl\@$ctr");
DefRegisterI(T_CS("\\c\@$unctr"),undef,Number(0));
AssignValue("\\c\@$unctr"=>Number(0),'global');
AssignValue("\\cl\@$unctr"=>Tokens(),'global') unless LookupValue("\\cl\@$unctr");
AssignValue("\\cl\@$within" =>
Tokens(T_CS($ctr),T_CS($unctr),
(LookupValue("\\cl\@$within") ? LookupValue("\\cl\@$within")->unlist :())),
'global') if $within;
AssignValue("\\cl\@UN$within" =>
Tokens(T_CS($unctr),
(LookupValue("\\cl\@UN$within") ? LookupValue("\\cl\@UN$within")->unlist :())),
'global') if $within;
AssignValue('nested_counters_'.$ctr =>$options{nested}) if $options{nested};
DefMacroI(T_CS("\\the$ctr"),undef,"\\arabic{$ctr}",scope=>'global');
my $prefix = $options{idprefix};
AssignValue('@ID@prefix@'.$ctr=>$prefix) if $prefix;
$prefix = LookupValue('@ID@prefix@'.$ctr) unless $prefix;
if(defined $prefix){
if(my $idwithin = $options{idwithin} || $within){
DefMacroI(T_CS("\\the$ctr\@ID"),undef,
"\\expandafter\\ifx\\csname the$idwithin\@ID\\endcsname\\\@empty"
."\\else\\csname the$idwithin\@ID\\endcsname.\\fi"
." $prefix\\csname \@$ctr\@ID\\endcsname",
scope=>'global'); }
else {
DefMacroI(T_CS("\\the$ctr\@ID"),undef,"$prefix\\csname \@$ctr\@ID\\endcsname",
scope=>'global'); }
DefMacroI(T_CS("\\\@$ctr\@ID"),undef,"0",scope=>'global'); }
return; }
sub CounterValue {
my($ctr)=@_;
$ctr = $ctr->toString if ref $ctr;
my $value = LookupValue('\c@'.$ctr);
if(!$value){
Warn(":expected:<counter> Counter $ctr was not defined; assuming 0");
$value = Number(0); }
$value; }
sub StepCounter {
my($ctr)=@_;
my $value = CounterValue($ctr);
AssignValue("\\c\@$ctr"=>$value->add(Number(1)),'global');
DefMacroI(T_CS("\\\@$ctr\@ID"),undef, Tokens(Explode(LookupValue('\c@'.$ctr)->valueOf)),
scope=>'global');
# and reset any within counters!
if(my $nested = LookupValue("\\cl\@$ctr")){
foreach my $c ($nested->unlist){
ResetCounter($c->toString); }}
Expand(T_CS("\\the$ctr")); }
# HOW can we retract this?
sub RefStepCounter {
my($ctr)=@_;
my $v = StepCounter($ctr);
DefMacroI(T_CS("\\\@$ctr\@ID"),undef, Tokens(Explode(LookupValue('\c@'.$ctr)->valueOf)),
scope=>'global');
my $id = Expand(T_CS("\\the$ctr\@ID"));
DefMacroI(T_CS('\@currentlabel'),undef,$v);
DefMacroI(T_CS('\@currentID'),undef,$id);
# Any scopes activated for previous value of this counter (& any nested counters) must be removed.
# This may also include scopes activated for \label
deactivateCounterScope($ctr);
# And install the scope (if any) for this reference number.
AssignValue(current_counter=>$ctr,'local');
AssignValue('scopes_for_counter:'.$ctr => [$ctr.':'.ToString($v)],'local');
$STATE->activateScope($ctr.':'.ToString($v));
(refnum=>$v, id=>$id); }
sub deactivateCounterScope {
my($ctr)=@_;
# print STDERR "Unusing scopes for $ctr\n";
if(my $scopes = LookupValue('scopes_for_counter:'.$ctr)){
map($STATE->deactivateScope($_), @$scopes); }
foreach my $inner_ctr (@{LookupValue('nested_counters_'.$ctr) || []}){
deactivateCounterScope($inner_ctr); }}
# For UN-numbered units
sub RefStepID {
my($ctr)=@_;
my $unctr = "UN$ctr";
my $v = StepCounter($unctr);
DefMacroI(T_CS("\\\@$ctr\@ID"),undef,
Tokens(T_OTHER('x'),Explode(LookupValue('\c@'.$unctr)->valueOf)),
scope=>'global');
my $id = Expand(T_CS("\\the$ctr\@ID"));
DefMacroI(T_CS('\@currentID'),undef,$id);
(id=>$id); }
sub ResetCounter {
my($ctr)=@_;
AssignValue('\c@'.$ctr => Number(0),'global');
# and reset any within counters!
if(my $nested = LookupValue("\\cl\@$ctr")){
foreach my $c ($nested->unlist){
ResetCounter($c->toString); }}
return;}
#**********************************************************************
# This function computes an xml:id for a node, if it hasn't already got one.
# It is suitable for use in Tag afterOpen as
# Tag('ltx:para',afterOpen=>sub { GenerateID(@_,'p'); });
# It generates an id of the form <parentid>.<prefix><number>
sub GenerateID {
my($document,$node,$whatsit,$prefix)=@_;
if(!$node->hasAttribute('xml:id')){
my $parent = $document->findnode('ancestor::*[@xml:id][1]',$node)
|| $document->getDocument->documentElement;
my $ctrkey = '_ID_counter_'.$prefix;
my $ctr = ($parent && $parent->getAttribute($ctrkey)) || 0;
## Old versions don't like this!!!
## my $parent_id = $parent && $parent->getAttribute('xml:id');
my $parent_id = $parent && $parent->getAttributeNS("http://www.w3.org/XML/1998/namespace",'id');
my $id = ($parent_id ? $parent_id."." : '').$prefix. (++$ctr);
$parent->setAttribute($ctrkey=>$ctr) if $parent;
$document->setAttribute($node,'xml:id'=>$id);
}}
#======================================================================
# Readers for reading various data types
#======================================================================
sub Expand { $STATE->getStomach->getGullet->expandTokens(@_); }
sub Invocation {
my($token,@args)=@_;
if(my $defn = LookupDefinition((ref $token ? $token : T_CS($token)))){
Tokens($defn->invocation(@args)); }
else {
Fatal(":undefined:".Stringify($token)." Cannot invoke ".Stringify($token)."; it is undefined");
Tokens(); }}
#======================================================================
# Non-exported support for defining forms.
#======================================================================
sub CheckOptions {
my($operation,$allowed,%options)=@_;
my @badops = grep(!$$allowed{$_}, keys %options);
Error(":misdefined:$operation $operation does not accept options:".join(', ',@badops)) if @badops;
}
sub requireMath {
my $cs = ToString($_[0]);
Warn(":unexpected:$cs $cs should only appear in math mode") unless LookupValue('IN_MATH');
return; }
sub forbidMath {
my $cs = ToString($_[0]);
Warn(":unexpected:$cs $cs should not appear in math mode") if LookupValue('IN_MATH');
return; }
#**********************************************************************
# Definitions
#**********************************************************************
#======================================================================
# Defining Expandable Control Sequences.
#======================================================================
# Define an expandable control sequence. It will be expanded in the Gullet.
# The $replacement should be a LaTeXML::Tokens (the arguments will be
# substituted for any #1,...), or a sub which returns a list of tokens (or just return;).
# Those tokens, if any, will be reinserted into the input.
# There are no options to these definitions.
our $expandable_options = {isConditional=>1, scope=>1, locked=>1};
sub DefExpandable {
my($proto,$expansion,%options)=@_;
Warn(":misdefined:DefExpandable DefExpandable ($proto) is deprecated; use DefMacro");
DefMacro($proto,$expansion,%options); }
# Define a Macro: Essentially an alias for DefExpandable
# For convenience, the $expansion can be a string which will be tokenized.
our $macro_options = {isConditional=>1, scope=>1, locked=>1};
sub DefMacro {
my($proto,$expansion,%options)=@_;
CheckOptions("DefMacro ($proto)",$macro_options,%options);
DefMacroI(parsePrototype($proto),$expansion,%options); }
sub DefMacroI {
my($cs,$paramlist,$expansion,%options)=@_;
if(!defined $expansion){ $expansion = Tokens(); }
$cs = coerceCS($cs);
$STATE->installDefinition(LaTeXML::Expandable->new($cs,$paramlist,$expansion,%options),
$options{scope});
AssignValue(ToString($cs).":locked"=>1) if $options{locked};
return; }
sub RawTeX {
my($text)=@_;
Digest(TokenizeInternal($text));
return; }
#======================================================================
# Define a primitive control sequence.
#======================================================================
# Primitives are executed in the Stomach.
# The $replacement should be a sub which returns nothing, or a list of Box's or Whatsit's.
# The options are:
# isPrefix : 1 for things like \global, \long, etc.
# registerType : for parameters (but needs to be worked into DefParameter, below).
our $primitive_options = {isPrefix=>1,scope=>1, requireMath=>1,
forbidMath=>1,beforeDigest=>1, locked=>1};
sub DefPrimitive {
my($proto,$replacement,%options)=@_;
CheckOptions("DefPrimitive ($proto)",$primitive_options,%options);
DefPrimitiveI(parsePrototype($proto),$replacement,%options); }
sub DefPrimitiveI {
my($cs,$paramlist,$replacement,%options)=@_;
$replacement = sub { (); } unless defined $replacement;
$cs = coerceCS($cs);
$STATE->installDefinition(LaTeXML::Primitive
->new($cs,$paramlist,$replacement,
beforeDigest=> flatten(($options{requireMath} ? (sub{requireMath($cs);}):()),
($options{forbidMath} ? (sub{forbidMath($cs);}):()),
$options{beforeDigest}),
isPrefix=>$options{isPrefix}),
$options{scope});
AssignValue(ToString($cs).":locked"=>1) if $options{locked};
return; }
our $register_options = {readonly=>1, getter=>1, setter=>1};
our %register_types = ('LaTeXML::Number' =>'Number',
'LaTeXML::Dimension'=>'Dimension',
'LaTeXML::Glue' =>'Glue',
'LaTeXML::MuGlue' =>'MuGlue',
'LaTeXML::Tokens' =>'Tokens',
);
sub DefRegister {
my($proto,$value,%options)=@_;
CheckOptions("DefRegsiter ($proto)",$register_options,%options);
DefRegisterI(parsePrototype($proto),$value,%options); }
sub DefRegisterI {
my($cs,$paramlist,$value,%options)=@_;
$cs = coerceCS($cs);
my $type = $register_types{ref $value};
my $name = $cs->toString;
my $getter = $options{getter}
|| sub { LookupValue(join('',$name,map($_->toString,@_))) || $value; };
my $setter = $options{setter}
|| ($options{readonly}
? sub { my($value,@args)=@_;
Error(":unexpected:$name Cannot assign to register $name"); return; }
: sub { my($value,@args)=@_;
AssignValue(join('',$name,map($_->toString,@args)) => $value); });
# Not really right to set the value!
AssignValue($cs->toString =>$value) if defined $value;
$STATE->installDefinition(LaTeXML::Register->new($cs,$paramlist, $type,$getter,$setter,
readonly=>$options{readonly}),
'global');
return; }
sub flatten {
[map((defined $_ ? (ref $_ eq 'ARRAY' ? @$_ : ($_)) : ()), @_)]; }
#======================================================================
# Define a constructor control sequence.
#======================================================================
# The arguments, if any, will be collected and processed in the Stomach, and
# a Whatsit will be constructed.
# It is the Whatsit that will be processed in the Document: It is responsible
# for constructing XML Nodes. The $replacement should be a sub which inserts nodes,
# or a string specifying a constructor pattern (See somewhere).
#
# Options are:
# bounded : any side effects of before/after daemans are bounded; they are
# automatically enclosed by bgroup/egroup pair.
# mode : causes a switch into the given mode during the Whatsit building in the stomach.
# reversion : a string representing the preferred TeX form of the invocation.
# beforeDigest : code to be executed (in the stomach) before parsing & constructing the Whatsit.
# Can be used for changing modes, beginning groups, etc.
# afterDigest : code to be executed (in the stomach) after parsing & constructing the Whatsit.
# useful for setting Whatsit properties,
# properties : a hashref listing default values of properties to assign to the Whatsit.
# These properties can be used in the constructor.
our $constructor_options = {mode=>1, requireMath=>1, forbidMath=>1, font=>1,
reversion=>1, properties=>1, alias=>1, nargs=>1,
beforeDigest=>1, afterDigest=>1, beforeConstruct=>1, afterConstruct=>1,
captureBody=>1, scope=>1, bounded=>1, locked=>1};
sub DefConstructor {
my($proto,$replacement,%options)=@_;
CheckOptions("DefConstructor ($proto)",$constructor_options,%options);
DefConstructorI(parsePrototype($proto),$replacement,%options); }
sub DefConstructorI {
my($cs,$paramlist,$replacement,%options)=@_;
$cs = coerceCS($cs);
my $mode = $options{mode};
my $bounded = $options{bounded};
$STATE->installDefinition(LaTeXML::Constructor
->new($cs,$paramlist,$replacement,
beforeDigest=> flatten(($options{requireMath} ? (sub{requireMath($cs);}):()),
($options{forbidMath} ? (sub{forbidMath($cs);}):()),
($mode ? (sub { $_[0]->beginMode($mode); })
:($bounded ? (sub {$_[0]->bgroup;}) :()) ),
($options{font}? (sub { MergeFont(%{$options{font}});}):()),
$options{beforeDigest}),
afterDigest => flatten($options{afterDigest},
($mode ? (sub { $_[0]->endMode($mode) })
: ($bounded ? (sub{$_[0]->egroup;}):()) )),
beforeConstruct=> flatten($options{beforeConstruct}),
afterConstruct => flatten($options{afterConstruct}),
nargs => $options{nargs},
alias => $options{alias},
reversion => ($options{reversion} && !ref $options{reversion}
? Tokenize($options{reversion}) : $options{reversion}),
captureBody => $options{captureBody},
properties => $options{properties}||{}),
$options{scope});
AssignValue(ToString($cs).":locked"=>1) if $options{locked};
return; }
# DefMath Define a Mathematical symbol or function.
# There are two sets of cases:
# (1) If the presentation appears to be TeX code, we create an XMDual,
# since the presentation may end up with structure, etc.
# (2) But if the presentation is a simple string, or unicode,
# it is just the content of the symbol; even if the function takes arguments.
# ALSO
# arrange that the operator token gets cs="$cs"
# ALSO
# Possibly some trick with SUMOP/INTOP affecting limits ?
# Well, not exactly, but....
# HMM.... Still fishy.
# When to make a dual ?
# If the $presentation seems to be TeX (ie. it involves #1... but not ONLY!)
our $math_options = {name=>1, meaning=>1, omcd=>1, reversion=>1, alias=>1,
role=>1, operator_role=>1, reorder=>1, dual=>1,
style=>1, font=>1, size=>1,
scriptpos=>1,operator_scriptpos=>1,
beforeDigest=>1, afterDigest=>1, scope=>1, nogroup=>1,locked=>1};
our $XMID=0;
sub next_id {
## "LXID".$XMID++; }
my $docid = LookupValue('DOCUMENTID');
($docid ? "$docid.XM" : 'XM').++$XMID; }
sub dualize_arglist {
my(@args)=@_;
my(@cargs,@pargs);
foreach my $arg (@args){
if(defined $arg){
# my $id = next_id();
# push(@cargs, Invocation(T_CS('\@XMArg'),T_OTHER($id),$arg));
# push(@pargs, Invocation(T_CS('\@XMRef'),T_OTHER($id))); }
StepCounter('@XMARG');
DefMacroI(T_CS('\@@XMARG@ID'),undef, Tokens(Explode(LookupValue('\c@@XMARG')->valueOf)),
scope=>'global');
my $id = Expand(T_CS('\the@XMARG@ID'));
push(@cargs, Invocation(T_CS('\@XMArg'),$id,$arg));
push(@pargs, Invocation(T_CS('\@XMRef'),$id)); }
else {
push(@cargs,undef);
push(@pargs,undef); }}
( [@cargs],[@pargs] ); }
# Quick reversal!
# ( [@pargs],[@cargs] ); }
sub DefMath {
my($proto,$presentation,%options)=@_;
CheckOptions("DefMath ($proto)",$math_options,%options);
DefMathI(parsePrototype($proto),$presentation,%options); }
sub DefMathI {
my($cs,$paramlist,$presentation,%options)=@_;
$cs = coerceCS($cs);
my $nargs = ($paramlist ? scalar($paramlist->getParameters): 0);
my $csname = $cs->getString;
my $meaning = $options{meaning};
my $name = $csname;
$name =~ s/^\\//;
$name = $options{name} if defined $options{name};
$name = undef if (defined $name)
&& (($name eq $presentation) || ($name eq '')
|| ((defined $meaning) && ($meaning eq $name)));
my $attr="name='#name' meaning='#meaning' omcd='#omcd' style='#style' size='#size'";
$options{role} = 'UNKNOWN'
if ($nargs == 0) && !defined $options{role};
$options{operator_role} = 'UNKNOWN'
if ($nargs > 0) && !defined $options{operator_role};
$options{reversion} = Tokenize($options{reversion})
if $options{reversion} && !ref $options{reversion};
# Store some data for introspection
AssignValue(join("##","math_definition",$csname,$nargs,
$options{role}||$options{operator_role}||'',$name||'',
(defined $options{meaning} ? $options{meaning} :''),
$STATE->getStomach->getGullet->getMouth->getSource,
(ref $presentation ? '' : $presentation))=>1, global=>1);
my %common =(alias=>$options{alias}||$cs->getString,
(defined $options{reversion}
? (reversion=>$options{reversion}) : ()),
beforeDigest=> flatten(sub{requireMath($csname);},
($options{nogroup}
? ()
:(sub{$_[0]->bgroup;})),
($options{font}
? (sub { MergeFont(%{$options{font}});})
:()),
$options{beforeDigest}),
afterDigest => flatten($options{afterDigest},
($options{nogroup}
? ()
:(sub{$_[0]->egroup;}))),
beforeConstruct=> flatten($options{beforeConstruct}),
afterConstruct => flatten($options{afterConstruct}),
properties => {name=>$name, meaning=>$meaning,
omcd=>$options{omcd},
role => $options{role},
operator_role=>$options{operator_role},
style=>$options{style},
size=>$options{size},
scriptpos=>$options{scriptpos},
operator_scriptpos=>$options{operator_scriptpos}},
scope=>$options{scope});
# If single character, Make the character active in math.
if(length($csname) == 1){
AssignCatcode('math:'.$csname=>1, $options{scope}); }
# If the presentation is complex, and involves arguments,
# we will create an XMDual to separate content & presentation.
# This involves creating 3 control sequences:
# \cs macro that expands into \DUAL{pres}{content}
# \cs@content constructor creates the content branch
# \cs@presentation macro that expands into code in the presentation branch.
### if((ref $presentation eq 'CODE')
### || ((ref $presentation) && grep($_->equals(T_PARAM),$presentation->unlist))
if((ref $presentation) || ($presentation =~ /\#\d|\\./)){
my $cont_cs = T_CS($csname."\@content");
my $pres_cs = T_CS($csname."\@presentation");
# Make the original CS expand into a DUAL invoking a presentation macro and content constructor
$STATE->installDefinition(LaTeXML::Expandable->new($cs,$paramlist, sub {
my($self,@args)=@_;
my($cargs,$pargs)=dualize_arglist(@args);
Invocation(T_CS('\DUAL'),
($options{role} ? T_OTHER($options{role}):undef),
Invocation($cont_cs,@$cargs),
Invocation($pres_cs,@$pargs) )->unlist; }),
$options{scope});
# Make the presentation macro.
$presentation = TokenizeInternal($presentation) unless ref $presentation;
$presentation = Invocation(T_CS('\@ASSERT@MEANING'), T_OTHER($meaning), $presentation)
if $meaning;
$STATE->installDefinition(LaTeXML::Expandable->new($pres_cs, $paramlist,
$presentation),
$options{scope});
$STATE->installDefinition(LaTeXML::Constructor->new($cont_cs,$paramlist,
($nargs == 0
? "<ltx:XMTok $attr role='#role' scriptpos='#scriptpos'/>"
: "<ltx:XMApp role='#role' scriptpos='#scriptpos'>"
. "<ltx:XMTok $attr role='#operator_role'"
. " scriptpos='#operator_scriptpos'/>"
. join('',map("#$_",
($options{reorder}? @{$options{reorder}} : (1..$nargs))))
."</ltx:XMApp>"),
%common), $options{scope}); }
else {
my $end_tok = (defined $presentation ? ">$presentation</ltx:XMTok>" : "/>");
$common{properties}{font} = sub { LookupValue('font')->specialize($presentation); };
$STATE->installDefinition(LaTeXML::Constructor->new($cs,$paramlist,
($nargs == 0
# If trivial presentation, allow it in Text
? ($presentation !~ /(\(|\)|\\)/
? "?#isMath(<ltx:XMTok role='#role' scriptpos='#scriptpos'"
. " font='#font' $attr$end_tok)"
. "($presentation)"
: "<ltx:XMTok role='#role' scriptpos='#scriptpos'"
. " font='#font' $attr$end_tok")
: "<ltx:XMApp role='#role' scriptpos='#scriptpos'>"
. "<ltx:XMTok $attr font='#font' role='#operator_role'"
. " scriptpos='#operator_scriptpos' $end_tok"
. join('',map("<ltx:XMArg>#$_</ltx:XMArg>", 1..$nargs))
."</ltx:XMApp>"),
%common), $options{scope}); }
AssignValue(ToString($cs).":locked"=>1) if $options{locked};
return; }
#======================================================================
# Define a LaTeX environment
# Note that the body of the environment is treated is the 'body' parameter in the constructor.
our $environment_options = {mode=>1, requireMath=>1, forbidMath=>1,
properties=>1, nargs=>1, font=>1,
beforeDigest=>1, afterDigest=>1, beforeConstruct=>1, afterConstruct=>1,
afterDigestBegin=>1, beforeDigestEnd=>1,
scope=>1, locked=>1};
sub DefEnvironment {
my($proto,$replacement,%options)=@_;
CheckOptions("DefEnvironment ($proto)",$environment_options,%options);
## $proto =~ s/^\{([^\}]+)\}\s*//; # Pull off the environment name as {name}
## my $paramlist=parseParameters($proto,"Environment $name");
## my $name = $1;
my($name,$paramspec)=Text::Balanced::extract_bracketed($proto,'{}');
$name =~ s/[\{\}]//g;
$paramspec =~ s/^\s*//;
my $paramlist=parseParameters($paramspec,"Environment $name");
DefEnvironmentI($name,$paramlist,$replacement,%options); }
sub DefEnvironmentI {
my($name,$paramlist,$replacement,%options)=@_;
my $mode = $options{mode};
$name = $name->toString if ref $name;
# This is for the common case where the environment is opened by \begin{env}
$STATE->installDefinition(LaTeXML::Constructor
->new(T_CS("\\begin{$name}"), $paramlist,$replacement,
beforeDigest=>flatten(($options{requireMath} ? (sub{requireMath($name);}):()),
($options{forbidMath} ? (sub{forbidMath($name);}):()),
($mode ? (sub { $_[0]->beginMode($mode);})
: (sub {$_[0]->bgroup;})),
sub { AssignValue(current_environment=>$name); },
($options{font}? (sub { MergeFont(%{$options{font}});}):()),
$options{beforeDigest}),
afterDigest =>flatten($options{afterDigestBegin}),
beforeConstruct=> flatten(sub{$STATE->pushFrame;},$options{beforeConstruct}),
# Curiously, it's the \begin whose afterConstruct gets called.
afterConstruct => flatten($options{afterConstruct},sub{$STATE->popFrame;}),
nargs=>$options{nargs},
captureBody=>1,
properties=>$options{properties}||{},
), $options{scope});
$STATE->installDefinition(LaTeXML::Constructor
->new(T_CS("\\end{$name}"),"","",
beforeDigest =>flatten($options{beforeDigestEnd}),
afterDigest=>flatten($options{afterDigest},
sub { my $env = LookupValue('current_environment');
Error(":unexpected:\\end{$name} Cannot close environment $name; current are "
.join(', ',$STATE->lookupStackedValues('current_environment')))
unless $env && $name eq $env;
return; },
($mode ? (sub { $_[0]->endMode($mode);})
:(sub{$_[0]->egroup;}))),
),$options{scope});
# For the uncommon case opened by \csname env\endcsname
$STATE->installDefinition(LaTeXML::Constructor
->new(T_CS("\\$name"), $paramlist,$replacement,
beforeDigest=>flatten(($options{requireMath} ? (sub{requireMath($name);}):()),
($options{forbidMath} ? (sub{forbidMath($name);}):()),
($mode ? (sub { $_[0]->beginMode($mode);}):()),
($options{font}? (sub { MergeFont(%{$options{font}});}):()),
$options{beforeDigest}),
afterDigest =>flatten($options{afterDigestBegin}),
beforeConstruct=> flatten(sub{$STATE->pushFrame;},$options{beforeConstruct}),
# Curiously, it's the \begin whose afterConstruct gets called.
afterConstruct => flatten($options{afterConstruct},sub{$STATE->popFrame;}),
nargs=>$options{nargs},
captureBody=>T_CS("\\end$name"), # Required to capture!!
properties=>$options{properties}||{}),
$options{scope});
$STATE->installDefinition(LaTeXML::Constructor
->new(T_CS("\\end$name"),"","",
beforeDigest =>flatten($options{beforeDigestEnd}),
afterDigest=>flatten($options{afterDigest},
($mode ? (sub { $_[0]->endMode($mode);}):())),
),$options{scope});
if($options{locked}){
AssignValue("\\begin{$name}:locked"=>1);
AssignValue("\\end{$name}:locked"=>1);
AssignValue("\\$name:locked"=>1);
AssignValue("\\end$name:locked"=>1); }
return; }
#======================================================================
# Declaring and Adjusting the Document Model.
#======================================================================
# Specify the properties of a Node tag.
our $tag_options = {autoOpen=>1, autoClose=>1, afterOpen=>1, afterClose=>1};
sub Tag {
my($tag,%properties)=@_;
CheckOptions("Tag ($tag)",$tag_options,%properties);
my $model = $STATE->getModel;
$model->setTagProperty($tag,autoOpen=>$properties{autoOpen})
if $properties{autoOpen};
$model->setTagProperty($tag,autoClose=>$properties{autoClose})
if $properties{autoClose};
# ADD after daemons to any already present.
$model->setTagProperty($tag,
afterOpen=>flatten($model->getTagProperty($tag,'afterOpen'),
$properties{afterOpen}))
if $properties{afterOpen};
$model->setTagProperty($tag,
afterClose=>flatten($model->getTagProperty($tag,'afterClose'),
$properties{afterClose}))
if $properties{afterClose};
return; }
sub DocType {
my($rootelement,$pubid,$sysid,%namespaces)=@_;
my $model = $STATE->getModel;
$model->setDocType($rootelement,$pubid,$sysid);
foreach my $prefix (keys %namespaces){
$model->registerDocumentNamespace($prefix=>$namespaces{$prefix}); }
return; }
# What verb here? Set, Choose,...
sub RelaxNGSchema {
my($schema,%namespaces)=@_;
my $model = $STATE->getModel;
$model->setRelaxNGSchema($schema,);
foreach my $prefix (keys %namespaces){
$model->registerDocumentNamespace($prefix=>$namespaces{$prefix}); }
return; }
sub RegisterNamespace {
my($prefix,$namespace)=@_;
$STATE->getModel->registerNamespace($prefix,$namespace);
return; }
#======================================================================
# Package, Class and File Loading
#======================================================================
# Find a file:
# If the raw option is given,
# it searches for the file.
# If $ext is given or $file ends with a known extension
# (eg. .sty for packages, .cls for classes, etc):
# the file is sought first as $file.$ext.ltxml then as $file.$ext
# Otherwise, the file is sought in a more TeX-like fashion:
# the file is sought as $file.tex.ltxml, $file.tex, $file.ltxml or $file.
# [ie. if raw=>1, we do _not_ loo for .ltxml forms]
our $findfile_options = {raw=>1, type=>1};
sub FindFile {
my ($file,%options)=@_;
CheckOptions("FindFile ($file)",$findfile_options,%options);
my $paths = LookupValue('SEARCHPATHS');
$file = ToString($file);
$file .= ".$options{type}" if $options{type};
if($options{raw}){
pathname_find("$file",paths=>$paths); }
elsif($options{type} || ($file =~ /\.(tex|pool|sty|cls|clo|cnf|cfg)$/)){ # explicit or known extensions
pathname_find("$file.ltxml",paths=>$paths,installation_subdir=>'Package')
|| pathname_find("$file",paths=>$paths); }
else {
pathname_find("$file.tex.ltxml",paths=>$paths,installation_subdir=>'Package')
|| pathname_find("$file.tex",paths=>$paths)
|| pathname_find("$file.ltxml",paths=>$paths,installation_subdir=>'Package')
|| pathname_find("$file",paths=>$paths); }}
# Declare an option for the current package or class
# If $option is undef, it is the default.
# $code can be a sub (as a primitive), or a string to be expanded.
# (effectively a macro)
sub DeclareOption {
my($option,$code)=@_;
$option = ToString($option) if ref $option;
PushValue('@declaredoptions',$option) if $option;
my $cs = ($option ? '\ds@'.$option : '\default@ds');
# print STDERR "Declaring option: ".($option ? $option : '<default>')."\n";
if((!defined $code) || (ref $code eq 'CODE')){
DefPrimitiveI($cs,undef,$code); }
else {
DefMacroI($cs,undef,$code); }
return; }
# Pass the sequence of @options to the package $name (if $ext is 'sty'),
# or class $name (if $ext is 'cls').
sub PassOptions {
my($name,$ext,@options)=@_;
PushValue('opt@'.$name.'.'.$ext, map(ToString($_),@options));
# print STDERR "Passing to $name.$ext options: ".join(', ',@options)."\n";
return; }
# Process the options passed to the currently loading package or class.
# If inorder=>1, they are processed in the order given (like \ProcessOptions*),
# otherwise, they are processed in the order declared.
# Unless noundefine=>1 (like for \ExecuteOptions), all option definitions
# undefined after execution.
our $processoptions_options = {inorder=>1};
sub ProcessOptions {
my(%options)=@_;
CheckOptions("ProcessOptions",$processoptions_options,%options);
my $name = ToString(Digest(T_CS('\@currname')));
my $ext = ToString(Digest(T_CS('\@currext')));
my @declaredoptions = @{LookupValue('@declaredoptions')};
my @curroptions = @{LookupValue('opt@'.$name.'.'.$ext)};
# print STDERR "\nProcessing options for $name.$ext: ".join(', ',@curroptions)."\n";
my $defaultcs = T_CS('\default@ds');
# Execute options in declared order (unless \ProcessOptions*)
if($options{inorder}){ # Execute options in order (eg. \ProcessOptions*)
foreach my $option (@curroptions){
DefMacroI('\CurrentOption',undef,$option);
my $cs = T_CS('\ds@'.$option);
if(LookupDefinition($cs)){
# print STDERR "\nUsing option $option to $name.$ext\n";
Digest($cs); }
elsif($defaultcs){
# print STDERR "\nUsing option $option to $name.$ext with default handler\n";
Digest($defaultcs); }}}
else { # Execute options in declared order (eg. \ProcessOptions)
foreach my $option (@declaredoptions){
if(grep($option eq $_,@curroptions)){
@curroptions = grep($option ne $_, @curroptions); # Remove it, since it's been handled.
# print STDERR "\nUsing option $option to $name.$ext\n";
DefMacroI('\CurrentOption',undef,$option);
Digest(T_CS('\ds@'.$option)); }}
# Now handle any remaining options (eg. default options), in the given order.
foreach my $option (@curroptions){
DefMacroI('\CurrentOption',undef,$option);
# print STDERR "\nUsing option $option to $name.$ext with default handler\n";
Digest($defaultcs); }}
# Now, undefine the handlers?
foreach my $option (@declaredoptions){
Let(T_CS('\ds@'.$option),T_CS('\relax')); }
return; }
sub ExecuteOptions {
my(@options)=@_;
my %unhandled=();
foreach my $option (@options){
my $cs = T_CS('\ds@'.$option);
if(LookupDefinition($cs)){
DefMacroI('\CurrentOption',undef,$option);
Digest($cs); }
else {
$unhandled{$option}=1; }}
Warn(":unexpected:<option> Unrecognized options passed to ExecuteOptions: ".join(', ',sort keys %unhandled))
if keys %unhandled;
return; }
sub resetOptions {
AssignValue('@declaredoptions',[]);
Let(T_CS('\default@ds'),
(ToString(Digest(T_CS('\@currext'))) eq 'cls'
? T_CS('\OptionNotUsed') : T_CS('\@unknownoptionerror')));
}
sub AddToMacro {
my($cs,$tokens)=@_;
# Needs error checking!
my $defn = LookupDefinition($cs);
DefMacroI($cs,undef,Tokens($defn->getExpansion->unlist,$tokens->unlist)); }
our $require_options = {options=>1, withoptions=>1, type=>1, as_class=>1, raw=>1, after=>1};
sub RequirePackage {
my($package,%options)=@_;
$package = ToString($package) if ref $package;
$package =~ s/^\s*//; $package =~ s/\s*$//;
CheckOptions("RequirePackage ($package)",$require_options,%options);
my $prevname = LookupDefinition(T_CS('\@currname')) && ToString(Digest(T_CS('\@currname')));
my $prevext = LookupDefinition(T_CS('\@currext')) && ToString(Digest(T_CS('\@currext')));
$options{type} = 'sty' unless $options{type};
# This package will be treated somewhat as if it were a class if as_class is true
# OR if it is loaded by such a class, and has withoptions true!!!
$options{as_class} = 1 if $options{withoptions}
&& grep($prevname eq $_, @{LookupValue('@masquerading@as@class')||[]});
my $filetype = $options{type};
my $type = ($options{as_class} ? 'cls' : $options{type});
# For \RequirePackageWithOptions, pass the options from the outer class/style to the inner one.
if($options{withoptions} && $prevname){
PassOptions($package,$type,@{LookupValue('opt@'.$prevname.".".$prevext)}); }
DefMacroI('\@currname',undef,Tokens(Explode($package)));
DefMacroI('\@currext',undef,Tokens(Explode($type)));
# reset options
resetOptions();
PassOptions($package,$type,@{$options{options} || []});
if(my $file = FindFile($package, type=>$filetype, raw=>$options{raw})){
# Note which packages are pretending to be classes.
PushValue('@masquerading@as@class',$package) if $options{as_class};
DefMacroI(T_CS("\\$package.$type-hook"),undef,$options{after} || '');
my $gullet = $STATE->getStomach->getGullet;
$gullet->input($file,undef,%options);
Digest(T_CS("\\$package.$type-hook"));
DefMacroI('\@currname',undef,Tokens(Explode($prevname))) if $prevname;
DefMacroI('\@currext',undef,Tokens(Explode($prevext))) if $prevext;
resetOptions(); } # And reset options afterwards, too.
else {
$STATE->noteStatus(missing=>$package);
Error(":missing_file:$package Cannot find package $package"
.($type eq 'sty' ? '' : "(w/type=$type)")
." in paths ".join(', ',@{$STATE->lookupValue('SEARCHPATHS')})); }
return; }
our $loadclass_options = {options=>1, withoptions=>1, after=>1};
sub LoadClass {
my($class,%options)=@_;
$class = ToString($class) if ref $class;
$class =~ s/^\s*//; $class =~ s/\s*$//;
CheckOptions("LoadClass ($class)",$loadclass_options,%options);
$options{type} = 'cls' unless $options{type};
my $type = $options{type};
my $prevname = LookupDefinition(T_CS('\@currname')) && ToString(Digest(T_CS('\@currname')));
my $prevext = LookupDefinition(T_CS('\@currext')) && ToString(Digest(T_CS('\@currext')));
# For \LoadClassWithOptions, pass the options from the outer class to the inner one.
if($options{withoptions} && $prevname){
PassOptions($class,$type,@{LookupValue('opt@'.$prevname.".".$prevext)}); }
DefMacroI('\@currname',undef,Tokens(Explode($class)));
DefMacroI('\@currext',undef,Tokens(Explode($type)));
PassOptions($class,$type,@{$options{options} || []});
# What about "global options" ??????
resetOptions();
my $classfile = FindFile($class, type=>$type, raw=>$options{raw});
if(!$classfile || ($classfile =~ /\.\Q$type\E$/)){
Warn(":missing_file:$class.cls.ltxml No LaTeXML implementation of class $class found, using article");
if(!($classfile = FindFile("article.cls"))){
Fatal(":missing_file:article.cls.ltxml Installation error: Cannot find article implementation!"); }}
DefMacroI(T_CS("\\$class.$type-hook"),undef,$options{after} || '');
my $gullet = $STATE->getStomach->getGullet;
## $gullet->openMouth(Tokens(T_CS("\\$class.$type-hook")));
$gullet->input($classfile,undef,%options);
Digest(T_CS("\\$class.$type-hook"));
DefMacroI('\@currname',undef,Tokens(Explode($prevname))) if $prevname;
DefMacroI('\@currext',undef,Tokens(Explode($prevext))) if $prevext;
resetOptions(); # And reset options afterwards, too.
return; }
#======================================================================
# Defining Rewrite rules that act on the DOM
our $rewrite_options = {label=>1,scope=>1, xpath=>1, match=>1,
attributes=>1, replace=>1, regexp=>1};
sub DefRewrite {
my(@specs)=@_;
CheckOptions("DefRewrite",$rewrite_options,@specs);
$STATE->getModel->addRewriteRule('text',@specs);
return; }
sub DefMathRewrite {
my(@specs)=@_;
CheckOptions("DefRewrite",$rewrite_options,@specs);
$STATE->getModel->addRewriteRule('math',@specs);
return; }
our $ligature_options = {fontTest=>1};
sub DefLigature {
my($regexp,%options)=@_;
CheckOptions("DefLigature",$ligature_options,%options);
$STATE->getModel->addLigature($regexp,%options);
return; }
#our $math_ligature_options = {fontTest=>1, nodeText=>1, attributes=>1};
sub DefMathLigature {
my($matcher)=@_;
# CheckOptions("DefMathLigature",$math_ligature_options,%options);
$STATE->getModel->addMathLigature($matcher);
return; }
#**********************************************************************
1;
__END__
=pod
=head1 NAME
C<LaTeXML::Package> - Support for package implementations and document customization.
=head1 SYNOPSIS
This package defines and exports most of the procedures users will need
to customize or extend LaTeXML. The LaTeXML implementation of some
package might look something like the following, but see the
installed C<LaTeXML/Package> directory for realistic examples.
use LaTeXML::Package;
use strict;
#
# Load "anotherpackage"
RequirePackage('anotherpackage');
#
# A simple macro, just like in TeX
DefMacro('\thesection', '\thechapter.\roman{section}');
#
# A constructor defines how a control sequence generates XML:
DefConstructor('\thanks{}', "<ltx:thanks>#1</ltx:thanks>");
#
# And a simple environment ...
DefEnvironment('{abstract}','<abstract>#body</abstract>');
#
# A math symbol \Real to stand for the Reals:
DefMath('\Real', "\x{211D}", role=>'ID');
#
# Or a semantic floor:
DefMath('\floor{}','\left\lfloor#1\right\rfloor');
#
# More esoteric ...
# Use a RelaxNG schema
RelaxNGSchema("MySchema");
# Or use a special DocType if you have to:
# DocType("rootelement",
# "-//Your Site//Your DocType",'your.dtd',
# prefix=>"http://whatever/");
#
# Allow sometag elements to be automatically closed if needed
Tag('prefix:sometag', autoClose=>1);
#
# Don't forget this, so perl knows the package loaded.
1;
=head1 DESCRIPTION
To provide a LaTeXML-specific version of a LaTeX package C<mypackage.sty>
or class C<myclass.cls> (so that eg. C<\usepackage{mypackage}> works),
you create the file C<mypackage.sty.ltxml> or C<myclass.cls.ltxml>
and save it in the searchpath (current directory, or one of the directories
given to the --path option, or possibly added to the variable SEARCHPATHS).
Similarly, to provide document-specific customization for, say, C<mydoc.tex>,
you would create the file C<mydoc.latexml> (typically in the same directory).
However, in the first cases, C<mypackage.sty.ltxml> are loaded I<instead> of
C<mypackage.sty>, while a file like C<mydoc.latexml> is loaded in I<addition> to
C<mydoc.tex>.
In either case, you'll C<use LaTeXML::Package;> to import the various declarations
and defining forms that allow you to specify what should be done with various
control sequences, whether there is special treatment of certain document elements,
and so forth. Using C<LaTeXML::Package> also imports the functions and variables
defined in L<LaTeXML::Global>, so see that documentation as well.
Since LaTeXML attempts to mimic TeX, a familiarity with TeX's processing
model is also helpful. Additionally, it is often useful, when implementing
non-trivial behaviour, to think TeX-like.
Many of the following forms take code references as arguments or options.
That is, either a reference to a defined sub, C<\&somesub>, or an
anonymous function S<sub { ... }>. To document these cases, and the
arguments that are passed in each case, we'll use a notation like
S<CODE($token,..)>.
=head2 Control Sequences
Many of the following forms define the behaviour of control sequences.
In TeX you'll typically only define macros. In LaTeXML, we're
effectively redefining TeX itself, so we define macros as well as primitives,
registers, constructors and environments. These define the behaviour
of these commands when processed during the various phases of LaTeX's
immitation of TeX's digestive tract.
The first argument to each of these defining forms (C<DefMacro>, C<DefPrimive>, etc)
is a I<prototype> consisting of the control sequence being defined along with
the specification of parameters required by the control sequence.
Each parameter describes how to parse tokens following the control sequence into
arguments or how to delimit them. To simplify coding and capture common idioms
in TeX/LaTeX programming, latexml's parameter specifications are more expressive
than TeX's C<\def> or LaTeX's C<\newcommand>. Examples of the prototypes for
familiar TeX or LaTeX control sequences are:
DefConstructor('\usepackage[]{}',...
DefPrimitive('\multiply Variable SkipKeyword:by Number',..
DefPrimitive('\newcommand OptionalMatch:* {Token}[][]{}', ...
=head3 Control Sequence Parameters
The general syntax for parameter for a control sequence is something like
OpenDelim? Modifier? Type (: value (| value)* )? CloseDelim?
The enclosing delimiters, if any, are either {} or [], affect the way the
argument is delimited. With {}, a regular TeX argument (token or sequence
balanced by braces) is read before parsing according to the type (if needed).
With [], a LaTeX optional argument is read, delimited by (non-nested) square brackets.
The modifier can be either C<Optional> or C<Skip>, allowing the argument to
be optional. For C<Skip>, no argument is contributed to the argument list.
The shorthands {} and [] default the type to C<Plain> and reads a normal
TeX argument or LaTeX default argument.
The predefined argument types are as follows.
=over
=item C<Plain>, C<Semiverbatim>
Reads a standard TeX argument being either the next token, or if the
next token is an {, the balanced token list. In the case of C<Semiverbatim>,
many catcodes are disabled, which is handy for URL's, labels and similar.
=item C<Token>, C<XToken>
Read a single TeX Token. For C<XToken>, if the next token is expandable,
it is repeatedly expanded until an unexpandable token remains, which is returned.
=item C<Number>, C<Dimension>, C<Glue> or C<MuGlue>
Read an Object corresponding to Number, Dimension, Glue or MuGlue,
using TeX's rules for parsing these objects.
=item C<Until:>I<match>
Reads tokens until a match to the tokens I<match> is found, returning
the tokens preceding the match. This corresponds to TeX delimited arguments.
=item C<UntilBrace>
Reads tokens until the next open brace C<{>.
This corresponds to the peculiar TeX construct C<\def\foo#{...>.
=item C<Match:>I<match(|match)*>, C<Keyword:>I<match(|match)*>
Reads tokens expecting a match to one of the token lists I<match>,
returning the one that matches, or undef.
For C<Keyword>, case and catcode of the I<matches> are ignored.
Additionally, any leading spaces are skipped.
=item C<Balanced>
Read tokens until a closing }, but respecting nested {} pairs.
=item C<Variable>
Reads a token, expanding if necessary, and expects a control sequence naming
a writable register. If such is found, it returns an array of the corresponding
definition object, and any arguments required by that definition.
=item C<SkipSpaces>
Skips any space tokens, but contributes nothing to the argument list.
=back
=head3 Control of Scoping
Most defining commands accept an option to control how the definition is stored,
C<< scope=>$scope >>, where C<$scope> can be c<'global'> for global definitions,
C<'local'>, to be stored in the current stack frame, or a string naming a I<scope>.
A scope saves a set of definitions and values that can be activated at a later time.
Particularly interesting forms of scope are those that get automatically activated
upon changes of counter and label. For example, definitions that have
C<< scope=>'section:1.1' >> will be activated when the section number is "1.1",
and will be deactivated when the section ends.
=head3 Macros
=over
=item C<< DefMacro($prototype,$string | $tokens | $code,%options); >>
Defines the macro expansion for C<$prototype>. If a C<$string> is supplied, it will be
tokenized at definition time, and any macro arguments will be substituted for parameter
indicators (eg #1) at expansion time; the result is used as the expansion of
the control sequence.
The only option, other than C<scope>, is C<isConditional> which should be true,
for conditional control sequences (TeX uses these to keep track of conditional
nesting when skipping to \else or \fi).
If defined by C<$code>, the form is C<CODE($gullet,@args)> and it
must return a list of L<LaTeXML::Token>'s.
=item C<< DefMacroI($cs,$paramlist,$string | $tokens | $code,%options); >>
Internal form of C<DefMacro> where the control sequence and parameter list
have already been parsed; useful for definitions from within code.
Also, slightly more efficient for macros with no arguments (use C<undef> for
C<$paramlist>).
=back
=head3 Primitives
=over
=item C<< DefPrimitive($prototype,CODE($stomach,@args),%options); >>
Define a primitive control sequence.
These are usually done for side effect and
so CODE should end with C<return;>, but can also
return a list of digested items.
The only option is for the special case: C<< isPrefix=>1 >> is used for assignment
prefixes (like \global).
=item C<< DefPrimitiveI($cs,$paramlist,CODE($stomach,@args),%options); >>
Internal form of C<DefPrimitive> where the control sequence and parameter list
have already been parsed; useful for definitions from within code.
=item C<< DefRegister($prototype,$value,%options); >>
Defines a register with the given initial value (a Number, Dimension, Glue, MuGlue or Tokens
--- I haven't handled Box's yet). Usually, the C<$prototype> is just the control sequence,
but registers are also handled by prototypes like C<\count{Number}>. C<DefRegister> arranges
that the register value can be accessed when a numeric, dimension, ... value is being read,
and also defines the control sequence for assignment.
Options are
=over
=item C<readonly>
specifies if it is not allowed to change this value.
=item C<getter>=>CODE(@args)
=item C<setter>=>CODE($value,@args)
By default the value is stored in the State's Value table under a name concatenating the
control sequence and argument values. These options allow other means of fetching and
storing the value.
=back
=item C<< DefRegisterI($cs,$paramlist,$value,%options); >>
Internal form of C<DefRegister> where the control sequence and parameter list
have already been parsed; useful for definitions from within code.
=back
=head3 Constructors
=over
=item C<< DefConstructor($prototype,$xmlpattern | $code,%options); >>
The Constructor is where LaTeXML really starts getting interesting;
invoking the control sequence will generate an arbitrary XML
fragment in the document tree. More specifically: during digestion, the arguments
will be read and digested, creating a L<LaTeXML::Whatsit> to represent the object. During
absorbtion by the L<LaTeXML::Document>, the C<Whatsit> will generate the XML fragment according
to the replacement C<$xmlpattern>, or by executing C<CODE>.
The C<$xmlpattern> is simply a bit of XML as a string with certain substitutions to be made.
The substitutions are of the following forms:
If code is supplied, the form is C<CODE($document,@args,%properties)>
=over
=item #1, #2 ... #name
These are replaced by the corresponding argument (for #1) or property (for #name)
stored with the Whatsit. Each are turned into a string when it appears as
in an attribute position, or recursively processed when it appears as content.
=item C<&function(@args)>
Another form of substituted value is prefixed with C<&> which invokes a function.
For example, C< &func(#1) > would invoke the function C<func> on the first argument
to the control sequence; what it returns will be inserted into the document.
=item C<?COND(pattern)> or C<?COND(ifpattern)(elsepattern)>
Patterns can be conditionallized using this form. The C<COND> is any
of the above expressions, considered true if the result is non-empty.
Thus C<< ?#1(<foo/>) >> would add the empty element C<foo> if the first argument
were given.
=item C<^>
If the constuctor I<begins> with C<^>, the XML fragment is allowed to I<float up>
to a parent node that is allowed to contain it, according to the Document Type.
=back
The Whatsit property C<font> is defined by default. Additional properties
C<body> and C<trailer> are defined when C<captureBody> is true, or for environments.
By using C<< $whatsit->setProperty(key=>$value); >> within C<afterDigest>,
or by using the C<properties> option, other properties can be added.
DefConstructor options are
=over
=item mode=>(text|display_math|inline_math)
Changes to this mode during digestion.
=item bounded=>boolean
If true, TeX grouping (ie. C<{}>) is enforced around this invocation.
=item requireMath=>boolean
=item forbidMath=>boolean
These specify whether the given constructor can only appear,
or cannot appear, in math mode.
=item font=>{fontspec...}
Specifies the font to be set by this invocation.
See L</MergeFont>
If the font change is to only apply to this construct,
you would also use C<<bounded=>1>>.
=item reversion=>$texstring or CODE($whatsit,#1,#2,...)
Specifies the reversion of the invocation back into TeX tokens
(if the default reversion is not appropriate).
The $textstring string can include #1,#2...
The CODE is called with the $whatsit and digested arguments.
=item properties=>{prop=>value,...} or CODE($stomach,#1,#2...)
This option supplies additional properties to be set on the
generated Whatsit. In the first form, the values can
be of any type, but (1) if it is a code references, it takes
the same args ($stomach,#1,#2,...) and should return a value.
and (2) if the value is a string, occurances of #1 (etc) are
replaced by the corresponding argument.
In the second form, the code should return a hash of properties.
=item beforeDigest=>CODE($stomach)
This option supplies a Daemon to be executed during digestion
just before the Whatsit is created. The CODE should either
return nothing (return;) or a list of digested items (Box's,List,Whatsit).
It can thus change the State and/or add to the digested output.
=item afterDigest=>CODE($stomach,$whatsit)
This option supplies a Daemon to be executed during digestion
just after the Whatsit is created. it should either return
nothing (return;) or digested items. It can thus change the State,
modify the Whatsit, and/or add to the digested output.
=item beforeConstruct=>CODE($document,$whatsit)
Supplies CODE to execute before constructing the XML
(generated by $replacement).
=item afterConstruct=>CODE($document,$whatsit)
Supplies CODE to execute after constructing the XML.
=item captureBody=>boolean or Token
if true, arbitrary following material will be accumulated into
a `body' until the current grouping level is reverted,
or till the C<Token> is encountered if the option is a C<Token>.
This body is available as the C<body> property of the Whatsit.
This is used by environments and math.
=item alias=>$control_sequence
Provides a control sequence to be used when reverting Whatsit's back to Tokens,
in cases where it isn't the command used in the C<$prototype>.
=item nargs=>$nargs
This gives a number of args for cases where it can't be infered directly
from the C<$prototype> (eg. when more args are explictly read by Daemons).
=item scope=>$scope
See L</scope>.
=back
=item C<< DefConstructorI($cs,$paramlist,$xmlpattern | $code,%options); >>
Internal form of C<DefConstructor> where the control sequence and parameter list
have already been parsed; useful for definitions from within code.
=item C<< DefMath($prototype,$tex,%options); >>
A common shorthand constructor; it defines a control sequence that creates a mathematical object,
such as a symbol, function or operator application.
The options given can effectively create semantic macros that contribute to the eventual
parsing of mathematical content.
In particular, it generates an XMDual using the replacement $tex for the presentation.
The content information is drawn from the name and options
These C<DefConstructor> options also apply:
reversion, alias, beforeDigest, afterDigest,
beforeConstruct, afterConstruct and scope.
Additionally, it accepts
=over
=item style=>astyle
adds a style attribute to the object.
=item name=>aname
gives a name attribute for the object
=item omcd=>cdname
gives the OpenMath content dictionary that name is from.
=item role=>grammatical_role
adds a grammatical role attribute to the object; this specifies
the grammatical role that the object plays in surrounding expressions.
This direly needs documentation!
=item font=>{fontspec}
Specifies the font to be used for when creating this object.
See L</MergeFont>.
=item scriptpos=>boolean
Controls whether any sub and super-scripts will be stacked over or under this
object, or whether they will appear in the usual position.
WRONG: Redocument this!
=item operator_role=>grammatical_role
=item operator_scriptpos=>boolean
These two are similar to C<role> and C<scriptpos>, but are used in
unusual cases. These apply to the given attributes to the operator token
in the content branch.
=item nogroup=>boolean
Normally, these commands are digested with an implicit grouping around them,
so that changes to fonts, etc, are local. Providing C<<noggroup=>1>> inhibits this.
=back
=item C<< DefMathI($cs,$paramlist,$tex,%options); >>
Internal form of C<DefMath> where the control sequence and parameter list
have already been parsed; useful for definitions from within code.
=item C<< DefEnvironment($prototype,$replacement,%options); >>
Defines an Environment that generates a specific XML fragment. The C<$replacement> is
of the same form as that for DefConstructor, but will generally include reference to
the C<#body> property. Upon encountering a C<\begin{env}>: the mode is switched, if needed,
else a new group is opened; then the environment name is noted; the beforeDigest daemon is run.
Then the Whatsit representing the begin command (but ultimately the whole environment) is created
and the afterDigestBegin daemon is run.
Next, the body will be digested and collected until the balancing C<\end{env}>. Then,
any afterDigest daemon is run, the environment is ended, finally the mode is ended or
the group is closed. The body and C<\end{env}> whatsit are added to the C<\begin{env}>'s whatsit
as body and trailer, respectively.
It shares options with C<DefConstructor>:
mode, requireMath, forbidMath, properties, nargs,
font, beforeDigest, afterDigest, beforeConstruct,
afterConstruct and scope.
Additionally, C<afterDigestBegin> is effectively an C<afterDigest>
for the C<\begin{env}> control sequence.
=item C<< DefEnvironmentI($name,$paramlist,$replacement,%options); >>
Internal form of C<DefEnvironment> where the control sequence and parameter list
have already been parsed; useful for definitions from within code.
=back
=head2 Class and Packages
=over
=item C<< RequirePackage($package,%options); >>
Finds and loads a package implementation (usually C<*.sty.ltxml>, unless C<raw> is specified)
for the required C<$package>.
The options are:
=over
=item C<< type=>type >> specifies the file type (default C<sty>.
=item C<< options=>[...] >> specifies a list of package options.
=item C<< raw=>1 >> specifies that it is allowable to try to read a raw TeX style file.
=back
=item C<< LoadClass($class,%options); >>
Finds and loads a class definition (usually C<*.cls.ltxml>).
The only option is
=over
=item C<< options=>[...] >> specifies a list of class options.
=back
=item C<< FindFile($name,%options); >>
Find an appropriate file with the given C<$name> in the current directories
in C<SEARCHPATHS>.
If a file ending with C<.ltxml> is found, it will be preferred.
The options are:
=over
=item C<< type=>type >> specifies the file type (default C<sty>.
=item C<< raw=>1 >> specifies that it is allowable to try to read a raw TeX style file.
=back
=item C<< DeclareOption($option,$code); >>
Declares an option for the current package or class.
The C<$code> can be a string or Tokens (which will be macro expanded),
or can be a code reference which is treated as a primitive.
If a package or class wants to accomodate options, it should start
with one or more C<DeclareOptions>, followed by C<ProcessOptions()>.
=item C<< PassOptions($name,$ext,@options); >>
Causes the given C<@options> (strings) to be passed to the
package (if C<$ext> is C<sty>) or class (if C<$ext> is C<cls>)
named by C<$name>.
=item C<< ProcessOptions(); >>
Processes the options that have been passed to the current package
or class in a fashion similar to LaTeX. If the keyword
C<< inorder=>1 >> is given, the options are processed in the
order they were used, like C<ProcessOptions*>.
=item C<< ExecuteOptions(@options); >>
Process the options given explicitly in C<@options>.
=back
=head2 Counters and IDs
=over 4
=item C<< NewCounter($ctr,$within,%options); >>
Defines a new counter, like LaTeX's \newcounter, but extended.
It defines a counter that can be used to generate reference numbers,
and defines \the$ctr, etc. It also defines an "uncounter" which
can be used to generate ID's (xml:id) for unnumbered objects.
C<$ctr> is the name of the counter. If defined, C<$within> is the name
of another counter which, when incremented, will cause this counter
to be reset.
The options are
idprefix Specifies a prefix to be used to generate ID's
when using this counter
nested Not sure that this is even sane.
=item C<< $num = CounterValue($ctr); >>
Fetches the value associated with the counter C<$ctr>.
=item C<< $tokens = StepCounter($ctr); >>
Analog of C<\stepcounter>, steps the counter and returns the expansion of
C<\the$ctr>. Usually you should use C<RefStepCounter($ctr)> instead.
=item C<< $keys = RefStepCounter($ctr); >>
Analog of C<\refstepcounter>, steps the counter and returns a hash
containing the keys C<refnum=>$refnum, id=>$id>. This makes it
suitable for use in a C<properties> option to constructors.
The C<id> is generated in parallel with the reference number
to assist debugging.
=item C<< $keys = RefStepID($ctr); >>
Like to C<RefStepCounter>, but only steps the "uncounter",
and returns only the id; This is useful for unnumbered cases
of objects that normally get both a refnum and id.
=item C<< ResetCounter($ctr); >>
Resets the counter C<$ctr> to zero.
=item C<< GenerateID($document,$node,$whatsit,$prefix); >>
Generates an ID for nodes during the construction phase, useful
for cases where the counter based scheme is inappropriate.
The calling pattern makes it appropriate for use in Tag, as in
Tag('ltx:para',sub { GenerateID(@_,'p'); })
If C<$node> doesn't already have an xml:id set, it computes an
appropriate id by concatenating the xml:id of the closest
ancestor with an id (if any), the prefix and a unique counter.
=back
=head2 Document Model
Constructors define how TeX markup will generate XML fragments, but the
Document Model is used to control exactly how those fragments are assembled.
=over
=item C<< Tag($tag,%properties); >>
Declares properties of elements with the name C<$tag>.
The recognized properties are:
=over
=item autoOpen=>boolean
Specifies whether this $tag can be automatically opened
if needed to insert an element that can only
be contained by $tag.
This property can help match the more SGML-like LaTeX to XML.
=item autoClose=>boolean
Specifies whether this $tag can be automatically closed
if needed to close an ancestor node, or insert
an element into an ancestor.
This property can help match the more SGML-like LaTeX to XML.
=item afterOpen=>CODE($document,$box)
Provides CODE to be run whenever a node with this $tag
is opened. It is called with the document being constructed,
and the initiating digested object as arguments.
It is called after the node has been created, and after
any initial attributes due to the constructor (passed to openElement)
are added.
=item afterClose=>CODE($document,$box)
Provides CODE to be run whenever a node with this $tag
is closed. It is called with the document being constructed,
and the initiating digested object as arguments.
=back
=item C<< RelaxNGSchema($schemaname); >>
Specifies the schema to use for determining document model.
You can leave off the extension; it will look for C<.rng>,
and maybe eventually, C<.rnc> once that is implemented.
=item C<< RegisterNamespace($prefix,$URL); >>
Declares the C<$prefix> to be associated with the given C<$URL>.
These prefixes may be used in ltxml files, particularly for
constructors, xpath expressions, etc. They are not necessarily
the same as the prefixes that will be used in the generated document
(See DocType).
=item C<< DocType($rootelement,$publicid,$systemid,%namespaces); >>
Declares the expected rootelement, the public and system ID's of the document type
to be used in the final document. The hash C<%namespaces> specifies
the namespaces prefixes that are expected to be found in the DTD, along with
each associated namespace URI. Use the prefix C<#default> for the default namespace
(ie. the namespace of non-prefixed elements in the DTD).
The prefixes defined for the DTD may be different from the prefixes used in
implementation CODE (eg. in ltxml files; see RegisterNamespace).
The generated document will use the namespaces and prefixes defined for the DTD.
=back
=head2 Document Rewriting
During document construction, as each node gets closed, the text content gets simplfied.
We'll call it I<applying ligatures>, for lack of a better name.
=over
=item C<< DefLigature($regexp,%options); >>
Apply the regular expression (given as a string: "/fa/fa/" since it will
be converted internally to a true regexp), to the text content.
The only option is C<fontTest=CODE($font)>; if given, then the substitution
is applied only when C<fontTest> returns true.
Predefined Ligatures combine sequences of "." or single-quotes into appropriate
Unicode characters.
=item C<< DefMathLigature(CODE($document,@nodes)); >>
CODE is called on each sequence of math nodes at a given level. If they should
be replaced, return a list of C<($n,$string,%attributes)> to replace
the text content of the first node with C<$string> content and add the given attributes.
The next C<$n-1> nodes are removed. If no replacement is called for, CODE
should return undef.
Predefined Math Ligatures combine letter or digit Math Tokens (XMTok) into multicharacter
symbols or numbers, depending on the font (non math italic).
=back
After document construction, various rewriting and augmenting of the
document can take place.
=over
=item C<< DefRewrite(%specification); >>
=item C<< DefMathRewrite(%specification); >>
These two declarations define document rewrite rules that are applied to the
document tree after it has been constructed, but before math parsing, or
any other postprocessing, is done. The C<%specification> consists of a
seqeuence of key/value pairs with the initial specs successively narrowing the
selection of document nodes, and the remaining specs indicating how
to modify or replace the selected nodes.
The following select portions of the document:
=over
=item label =>$label
Selects the part of the document with label=$label
=item scope =>$scope
The $scope could be "label:foo" or "section:1.2.3" or something
similar. These select a subtree labelled 'foo', or
a section with reference number "1.2.3"
=item xpath =>$xpath
Select those nodes matching an explicit xpath expression.
=item match =>$TeX
Selects nodes that look like what the processing of $TeX would produce.
=item regexp=>$regexp
Selects text nodes that match the regular expression.
=back
The following act upon the selected node:
=over
=item attributes => $hash
Adds the attributes given in the hash reference to the node.
=item replace =>$replacement
Interprets the $replacement as TeX code to generate nodes that will
replace the selected nodes.
=back
=back
=head2 Mid-Level support
=over
=item C<< $tokens = Expand($tokens); >>
Expands the given C<$tokens> according to current definitions.
=item C<< $boxes = Digest($tokens); >>
Processes and digestes the C<$tokens>. Any arguments needed by
control sequences in C<$tokens> must be contained within the C<$tokens> itself.
=item C<< @tokens = Invocation($cs,@args); >>
Constructs a sequence of tokens that would invoke the token C<$cs>
on the arguments.
=item C<< RawTeX('... tex code ...'); >>
RawTeX is a convenience function for including chunks of raw TeX (or LaTeX) code
in a Package implementation. It is useful for copying portions of the normal
implementation that can be handled simply using macros and primitives.
=item C<< Let($token1,$token2); >>
Gives C<$token1> the same `meaning' (definition) as C<$token2>; like TeX's \let.
=back
=head2 Argument Readers
=over
=item C<< ReadParameters($gullet,$spec); >>
Reads from C<$gullet> the tokens corresponding to C<$spec>
(a Parameters object).
=item C<< DefParameterType($type,CODE($gullet,@values),%options); >>
Defines a new Parameter type, C<$type>, with CODE for its reader.
Options are:
=over
=item reversion=>CODE($arg,@values);
This CODE is responsible for converting a previously parsed argument back
into a sequence of Token's.
=item optional=>boolean
whether it is an error if no matching input is found.
=item novalue=>boolean
whether the value returned should contribute to argument lists, or
simply be passed over.
=item semiverbatim=>boolean
whether the catcode table should be modified before reading tokens.
=back
=item C<< DefColumnType($proto,$expansion); >>
Defines a new column type for tabular and arrays.
C<$proto> is the prototype for the pattern, analogous to the pattern
used for other definitions, except that macro being defined is a single character.
The C<$expansion> is a string specifying what it should expand into,
typically more verbose column specification.
=back
=head2 Access to State
=over
=item C<< $value = LookupValue($name); >>
Lookup the current value associated with the the string C<$name>.
=item C<< AssignValue($name,$value,$scope); >>
Assign $value to be associated with the the string C<$name>, according
to the given scoping rule.
Values are also used to specify most configuration parameters (which can
therefor also be scoped). The recognized configuration parameters are:
VERBOSITY : the level of verbosity for debugging
output, with 0 being default.
STRICT : whether errors (eg. undefined macros)
are fatal.
INCLUDE_COMMENTS : whether to preserve comments in the
source, and to add occasional line
number comments. (Default true).
PRESERVE_NEWLINES : whether newlines in the source should
be preserved (not 100% TeX-like).
By default this is true.
SEARCHPATHS : a list of directories to search for
sources, implementations, etc.
=item C<< PushValue($type,$name,@values); >>
This is like C<AssignValue>, but pushes values onto
the end of the value, which should be a LIST reference.
Scoping is not handled here (yet?), it simply pushes the value
onto the last binding of C<$name>.
=item C<< UnshiftValue($type,$name,@values); >>
Similar to C<PushValue>, but pushes a value onto
the front of the values, which should be a LIST reference.
=item C<< $value = LookupCatcode($char); >>
Lookup the current catcode associated with the the character C<$char>.
=item C<< AssignCatcode($char,$catcode,$scope); >>
Set C<$char> to have the given C<$catcode>, with the assignment made
according to the given scoping rule.
This method is also used to specify whether a given character is
active in math mode, by using C<math:$char> for the character,
and using a value of 1 to specify that it is active.
=item C<< $meaning = LookupMeaning($token); >>
Looks up the current meaning of the given C<$token> which may be a
Definition, another token, or the token itself if it has not
otherwise been defined.
=item C<< $defn = LookupDefinition($token); >>
Looks up the current definition, if any, of the C<$token>.
=item C<< InstallDefinition($defn); >>
Install the Definition C<$defn> into C<$STATE> under its
control sequence.
=back
=head2 Low-level Functions
=over
=item C<< CleanLabel($label,$prefix); >>
Cleans a C<$label> of disallowed characters,
prepending C<$prefix> (or C<LABEL>, if none given).
=item C<< CleanIndexKey($key); >>
Cleans an index key, so it can be used as an ID.
=item C<< CleanBibKey($key); >>
Cleans a bibliographic citation key, so it can be used as an ID.
=item C<< CleanURL($url); >>
Cleans a url.
=item C<< UTF($code); >>
Generates a UTF character, handy for the the 8 bit characters.
For example, C<UTF(0xA0)> generates the non-breaking space.
=item C<< MergeFont(%style); >>
Set the current font by merging the font style attributes with the current font.
The attributes and likely values (the values aren't required to be in this set):
family : serif, sansserif, typewriter, caligraphic,
fraktur, script
series : medium, bold
shape : upright, italic, slanted, smallcaps
size : tiny, footnote, small, normal, large,
Large, LARGE, huge, Huge
color : any named color, default is black
Some families will only be used in math.
This function returns nothing so it can be easily used in beforeDigest, afterDigest.
=item C<< @tokens = roman($number); >>
Formats the C<$number> in (lowercase) roman numerals, returning a list of the tokens.
=item C<< @tokens = Roman($number); >>
Formats the C<$number> in (uppercase) roman numerals, returning a list of the tokens.
=back
=head1 AUTHOR
Bruce Miller <bruce.miller@nist.gov>
=head1 COPYRIGHT
Public domain software, produced as part of work done by the
United States Government & not subject to copyright in the US.
=cut
|