# -*- mode: Perl -*- # /=====================================================================\ # # | eTeX | # # | enhanced TeX enhancements Implementation for LaTeXML | # # |=====================================================================| # # | 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 #_# | # # | http://dlmf.nist.gov/LaTeXML/ (o o) | # # \=========================================================ooo==U==ooo=/ # package LaTeXML::Package::Pool; use strict; use warnings; use LaTeXML::Package; # See http://tex.loria.fr/moteurs/etex_ref.html # Or better yet, see the full manual # http://texdoc.net/texmf-dist/doc/etex/base/etex_man.pdf # Section 3. The new features #====================================================================== # 3.1 Additional control over expansion # \protected associates with the next defn # (note that it isn't actually used anywhere). DefPrimitiveI('\protected', undef, sub { $STATE->setPrefix('protected'); return; }, isPrefix => 1); # \detokenize DefMacro('\detokenize GeneralText', sub { Explode(writableTokens($_[1])); }); # When building an expanded token list, the tokens resulting from the expansion # of \unexpanded are not expanded further (this is the same behaviour as is # exhibited by the tokens resulting from the expansion of # \the〈token variable〉in both TEX and ε-TEX). DefMacro('\unexpanded GeneralText', '#1'); #====================================================================== # 3.2. Provision for re-scanning already read text # \readline; like \read, but only spaces & other DefMacro('\readline Number SkipKeyword:to SkipSpaces Token', sub { my ($gullet, $port, $token) = @_; $port = ToString($port); if (my $mouth = LookupValue('input_file:' . $port)) { DefMacroI($token, undef, Tokens(Explode(($mouth->readRawLine || '') . "\r"))); } return; }); DefMacro('\scantokens GeneralText', sub { my ($gullet, $generaltext) = @_; $gullet->openMouth(LaTeXML::Core::Mouth->new(writableTokens($generaltext)), 0); return; }); #====================================================================== # 3.3 Environmental enquiries our @ETEX_VERSION = (qw(2 .2)); DefMacro('\eTeXrevision', sub { Explode($ETEX_VERSION[1]); }); DefRegister('\eTeXversion' => Number($ETEX_VERSION[0])); # \currentgrouplevel DefRegister('\currentgrouplevel', Number(0), readonly => 1, getter => sub { $STATE->getFrameDepth; }); # \currentgrouptype returns group types from 0..16 ; but what IS a "group type"? DefRegister('\currentgrouptype', Number(0), readonly => 1); # \ifcsname stuff \endcsname DefConditional('\ifcsname CSName', sub { defined LookupMeaning($_[1]); }); # \ifdefined DefConditional('\ifdefined Token', sub { defined LookupMeaning($_[1]); }); # ??? DefRegister('\lastnodetype', Number(0)); #====================================================================== # 3.4 Generalization of the \mark concept: a class of \marks # but since we don't manage Pages... DefPrimitive('\marks Number GeneralText', undef); DefMacroI('\topmarks Number', undef, Tokens()); DefMacroI('\firstmarks Number', undef, Tokens()); DefMacroI('\botmarks Number', undef, Tokens()); DefMacroI('\splitfirstmarks Number', undef, Tokens()); DefMacroI('\splitbotmarks Number', undef, Tokens()); #====================================================================== # 3.5 Bi-directional typesetting: the TeX--XeT primitives # Should these simply ouput some unicode direction changers, # [Things like: # 202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;; # 202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;; # 202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;; # 202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;; # 202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;; # ] # or do we need to do some more intelligent tracking of modes # and directionality? # Presumably we can't rely on the material itself being directional. # By leaving this 0, we're saying "Don't use these features"! DefRegister('\TeXXeTstate' => Number(0)); DefMacroI('\beginL', undef, ''); DefMacroI('\beginR', undef, ''); DefMacroI('\endL', undef, ''); DefMacroI('\endR', undef, ''); DefRegister('\predisplaydirection' => Number(0)); # ??? #====================================================================== # 3.6 Additional debugging features DefRegister('\interactionmode' => Number(0)); # Should show all open groups & their type. DefPrimitive('\showgroups', undef); # \showtokens DefPrimitive('\showtokens GeneralText', sub { Note("> " . writableTokens($_[1])); Note($_[0]->getLocator->toString()); return; }); DefRegister('\tracingassigns' => Number(0)); # ??? DefRegister('\tracinggroups' => Number(0)); DefRegister('\tracingifs' => Number(0)); # ??? DefRegister('\tracingscantokens' => Number(0)); DefRegister('\tracingnesting' => Number(0)); DefRegister('\savingvdiscards' => Number(0)); DefRegister('\savinghyphcodes' => Number(0)); #====================================================================== # 3.7 Miscellaneous primitives # \everyeof # NOTE: These tokens are NOT used anywhere (yet?) DefRegister('\everyeof', Tokens()); DefConstructor('\middle Token', '#1', afterConstruct => sub { my ($document) = @_; my $current = $document->getNode; my $delim = $document->getLastChildElement($current) || $current; $document->setAttribute($delim, role => 'MIDDLE'); $document->setAttribute($delim, stretchy => 'true'); return; }); # \unless someif DefConditional('\unless Token', sub { my ($gullet, $if) = @_; my ($defn, $test); if (($defn = LookupDefinition($if)) && (($$defn{conditional_type} || '') eq 'if') && ($test = $defn->getTest)) { # Invert the if's test! !&$test($gullet, $defn->readArguments($gullet)); } else { Error('unexpected', $if, "\\unless should not be followed by " . Stringify($if)); } }); #====================================================================== # \numexpr, \dimexpr, \gluexpr, \muexpr # These read tokens doing simple parsing until \relax or the parse fails. # since we don't know where it ends, we can't easily use Parse::RecDescent. # They also act like a Register! # $type is one of Number, Dimension, Glue or MuGlue sub etex_readexpr { my ($gullet, $type) = @_; my $value = etex_readexpr_i($gullet, $type, 0); if (my $token = $gullet->readToken) { # Skip \relax $gullet->unread($token) unless Equals($token, T_CS('\relax')); } return $value; } sub etex_readexpr_i { my ($gullet, $type, $prec) = @_; # Read a first value my $value; my $token = $gullet->readXNonSpace; if (!$token) { return; } elsif ($token->equals(T_OTHER('('))) { $value = etex_readexpr_i($gullet, $type, 0); my $close = $gullet->readXToken; # close parenthesis should have terminated recursive call if (!$close || !$close->equals(T_OTHER(')'))) { Error('expected', ')', $gullet, "Missing close parenthesis in $type expr.", "Got " . ToString($close)); } } else { # Read core TeX value/register $gullet->unread($token); $value = $gullet->readValue($type); } # Now check for a following operator(s) & operand(s) (respecting precedence) while (my $next = $gullet->readXNonSpace) { if ($next->equals(T_CS('\relax'))) { $gullet->unread($next); # leave the \relax for top-level to strip off. last; } elsif ($next->equals(T_OTHER('+')) && ($prec < 1)) { $value = $value->add(etex_readexpr_i($gullet, $type, 1)); } elsif ($next->equals(T_OTHER('-')) && ($prec < 1)) { $value = $value->subtract(etex_readexpr_i($gullet, $type, 1)); } elsif ($next->equals(T_OTHER('*')) && ($prec < 2)) { # multiplier should be pure number $value = $value->multiply(etex_readexpr_i($gullet, 'Number', 2)); } elsif ($next->equals(T_OTHER('/')) && ($prec < 2)) { # denominator should be pure number $value = $value->divideround(etex_readexpr_i($gullet, 'Number', 2)); } else { # anything else, we're done. $gullet->unread($next); last; } } return $value; } DefParameterType('NumExpr', sub { etex_readexpr($_[0], 'Number'); }); DefParameterType('DimExpr', sub { etex_readexpr($_[0], 'Dimension'); }); DefParameterType('GlueExpr', sub { etex_readexpr($_[0], 'Glue'); }); DefParameterType('MuExpr', sub { etex_readexpr($_[0], 'MuGlue'); }); DefRegister('\numexpr NumExpr', Number(0), getter => sub { $_[0]; }); DefRegister('\dimexpr DimExpr', Dimension(0), getter => sub { $_[0]; }); DefRegister('\glueexpr GlueExpr', Glue(0), getter => sub { $_[0]; }); DefRegister('\muexpr MuExpr', MuGlue(0), getter => sub { $_[0]; }); # Not really sure where this comes from; pdftex? DefRegister('\synctex', Number(0)); #====================================================================== 1;