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