1# -*- mode: Perl -*- 2# /=====================================================================\ # 3# | eTeX | # 4# | enhanced TeX enhancements Implementation for LaTeXML | # 5# |=====================================================================| # 6# | Part of LaTeXML: | # 7# | Public domain software, produced as part of work done by the | # 8# | United States Government & not subject to copyright in the US. | # 9# |---------------------------------------------------------------------| # 10# | Bruce Miller <bruce.miller@nist.gov> #_# | # 11# | http://dlmf.nist.gov/LaTeXML/ (o o) | # 12# \=========================================================ooo==U==ooo=/ # 13package LaTeXML::Package::Pool; 14use strict; 15use warnings; 16use LaTeXML::Package; 17 18# See http://tex.loria.fr/moteurs/etex_ref.html 19# Or better yet, see the full manual 20# http://texdoc.net/texmf-dist/doc/etex/base/etex_man.pdf 21# Section 3. The new features 22 23#====================================================================== 24# 3.1 Additional control over expansion 25# \protected associates with the next defn 26# (note that it isn't actually used anywhere). 27DefPrimitiveI('\protected', undef, sub { 28 $STATE->setPrefix('protected'); return; }, isPrefix => 1); 29 30# \detokenize 31DefMacro('\detokenize GeneralText', sub { Explode(writableTokens($_[1])); }); 32 33# When building an expanded token list, the tokens resulting from the expansion 34# of \unexpanded are not expanded further (this is the same behaviour as is 35# exhibited by the tokens resulting from the expansion of 36# \the〈token variable〉in both TEX and ε-TEX). 37DefMacro('\unexpanded GeneralText', '#1'); 38 39#====================================================================== 40# 3.2. Provision for re-scanning already read text 41 42# \readline; like \read, but only spaces & other 43DefMacro('\readline Number SkipKeyword:to SkipSpaces Token', sub { 44 my ($gullet, $port, $token) = @_; 45 $port = ToString($port); 46 if (my $mouth = LookupValue('input_file:' . $port)) { 47 DefMacroI($token, undef, Tokens(Explode(($mouth->readRawLine || '') . "\r"))); } 48 return; }); 49 50DefMacro('\scantokens GeneralText', sub { 51 my ($gullet, $generaltext) = @_; 52 $gullet->openMouth(LaTeXML::Core::Mouth->new(writableTokens($generaltext)), 0); 53 return; }); 54 55#====================================================================== 56# 3.3 Environmental enquiries 57 58our @ETEX_VERSION = (qw(2 .2)); 59DefMacro('\eTeXrevision', sub { Explode($ETEX_VERSION[1]); }); 60DefRegister('\eTeXversion' => Number($ETEX_VERSION[0])); 61 62# \currentgrouplevel 63DefRegister('\currentgrouplevel', Number(0), 64 readonly => 1, 65 getter => sub { $STATE->getFrameDepth; }); 66 67# \currentgrouptype returns group types from 0..16 ; but what IS a "group type"? 68DefRegister('\currentgrouptype', Number(0), readonly => 1); 69 70# \ifcsname stuff \endcsname 71DefConditional('\ifcsname CSName', sub { defined LookupMeaning($_[1]); }); 72 73# \ifdefined <token> 74DefConditional('\ifdefined Token', sub { defined LookupMeaning($_[1]); }); 75 76# ??? 77DefRegister('\lastnodetype', Number(0)); 78 79#====================================================================== 80# 3.4 Generalization of the \mark concept: a class of \marks 81# but since we don't manage Pages... 82 83DefPrimitive('\marks Number GeneralText', undef); 84DefMacroI('\topmarks Number', undef, Tokens()); 85DefMacroI('\firstmarks Number', undef, Tokens()); 86DefMacroI('\botmarks Number', undef, Tokens()); 87DefMacroI('\splitfirstmarks Number', undef, Tokens()); 88DefMacroI('\splitbotmarks Number', undef, Tokens()); 89 90#====================================================================== 91# 3.5 Bi-directional typesetting: the TeX--XeT primitives 92 93# Should these simply ouput some unicode direction changers, 94# [Things like: 95# 202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;; 96# 202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;; 97# 202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;; 98# 202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;; 99# 202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;; 100# ] 101# or do we need to do some more intelligent tracking of modes 102# and directionality? 103# Presumably we can't rely on the material itself being directional. 104 105# By leaving this 0, we're saying "Don't use these features"! 106DefRegister('\TeXXeTstate' => Number(0)); 107 108DefMacroI('\beginL', undef, ''); 109DefMacroI('\beginR', undef, ''); 110DefMacroI('\endL', undef, ''); 111DefMacroI('\endR', undef, ''); 112 113DefRegister('\predisplaydirection' => Number(0)); # ??? 114 115#====================================================================== 116# 3.6 Additional debugging features 117DefRegister('\interactionmode' => Number(0)); 118 119# Should show all open groups & their type. 120DefPrimitive('\showgroups', undef); 121 122# \showtokens <generaltext> 123DefPrimitive('\showtokens GeneralText', sub { 124 Note("> " . writableTokens($_[1])); 125 Note($_[0]->getLocator->toString()); 126 return; }); 127 128DefRegister('\tracingassigns' => Number(0)); # ??? 129DefRegister('\tracinggroups' => Number(0)); 130DefRegister('\tracingifs' => Number(0)); # ??? 131DefRegister('\tracingscantokens' => Number(0)); 132DefRegister('\tracingnesting' => Number(0)); 133DefRegister('\savingvdiscards' => Number(0)); 134DefRegister('\savinghyphcodes' => Number(0)); 135#====================================================================== 136# 3.7 Miscellaneous primitives 137 138# \everyeof 139# NOTE: These tokens are NOT used anywhere (yet?) 140DefRegister('\everyeof', Tokens()); 141 142DefConstructor('\middle Token', '#1', 143 afterConstruct => sub { 144 my ($document) = @_; 145 my $current = $document->getNode; 146 my $delim = $document->getLastChildElement($current) || $current; 147 $document->setAttribute($delim, role => 'MIDDLE'); 148 $document->setAttribute($delim, stretchy => 'true'); 149 return; }); 150 151# \unless someif 152DefConditional('\unless Token', sub { 153 my ($gullet, $if) = @_; 154 my ($defn, $test); 155 if (($defn = LookupDefinition($if)) && (($$defn{conditional_type} || '') eq 'if') 156 && ($test = $defn->getTest)) { 157 # Invert the if's test! 158 !&$test($gullet, $defn->readArguments($gullet)); } 159 else { 160 Error('unexpected', $if, "\\unless should not be followed by " . Stringify($if)); } }); 161 162#====================================================================== 163# \numexpr, \dimexpr, \gluexpr, \muexpr 164# These read tokens doing simple parsing until \relax or the parse fails. 165# since we don't know where it ends, we can't easily use Parse::RecDescent. 166# They also act like a Register! 167# $type is one of Number, Dimension, Glue or MuGlue 168sub etex_readexpr { 169 my ($gullet, $type) = @_; 170 my $value = etex_readexpr_i($gullet, $type, 0); 171 if (my $token = $gullet->readToken) { # Skip \relax 172 $gullet->unread($token) unless Equals($token, T_CS('\relax')); } 173 return $value; } 174 175sub etex_readexpr_i { 176 my ($gullet, $type, $prec) = @_; 177 # Read a first value 178 my $value; 179 my $token = $gullet->readXNonSpace; 180 if (!$token) { 181 return; } 182 elsif ($token->equals(T_OTHER('('))) { 183 $value = etex_readexpr_i($gullet, $type, 0); 184 my $close = $gullet->readXToken; # close parenthesis should have terminated recursive call 185 if (!$close || !$close->equals(T_OTHER(')'))) { 186 Error('expected', ')', $gullet, 187 "Missing close parenthesis in $type expr.", "Got " . ToString($close)); } } 188 else { # Read core TeX value/register 189 $gullet->unread($token); 190 $value = $gullet->readValue($type); } 191 192 # Now check for a following operator(s) & operand(s) (respecting precedence) 193 while (my $next = $gullet->readXNonSpace) { 194 if ($next->equals(T_CS('\relax'))) { 195 $gullet->unread($next); # leave the \relax for top-level to strip off. 196 last; } 197 elsif ($next->equals(T_OTHER('+')) && ($prec < 1)) { 198 $value = $value->add(etex_readexpr_i($gullet, $type, 1)); } 199 elsif ($next->equals(T_OTHER('-')) && ($prec < 1)) { 200 $value = $value->subtract(etex_readexpr_i($gullet, $type, 1)); } 201 elsif ($next->equals(T_OTHER('*')) && ($prec < 2)) { # multiplier should be pure number 202 $value = $value->multiply(etex_readexpr_i($gullet, 'Number', 2)); } 203 elsif ($next->equals(T_OTHER('/')) && ($prec < 2)) { # denominator should be pure number 204 $value = $value->divideround(etex_readexpr_i($gullet, 'Number', 2)); } 205 else { # anything else, we're done. 206 $gullet->unread($next); 207 last; } } 208 return $value; } 209 210DefParameterType('NumExpr', sub { etex_readexpr($_[0], 'Number'); }); 211DefParameterType('DimExpr', sub { etex_readexpr($_[0], 'Dimension'); }); 212DefParameterType('GlueExpr', sub { etex_readexpr($_[0], 'Glue'); }); 213DefParameterType('MuExpr', sub { etex_readexpr($_[0], 'MuGlue'); }); 214 215DefRegister('\numexpr NumExpr', Number(0), getter => sub { $_[0]; }); 216DefRegister('\dimexpr DimExpr', Dimension(0), getter => sub { $_[0]; }); 217DefRegister('\glueexpr GlueExpr', Glue(0), getter => sub { $_[0]; }); 218DefRegister('\muexpr MuExpr', MuGlue(0), getter => sub { $_[0]; }); 219 220# Not really sure where this comes from; pdftex? 221DefRegister('\synctex', Number(0)); 222#====================================================================== 2231; 224