diff --git a/lib/LaTeXML/Common/Error.pm b/lib/LaTeXML/Common/Error.pm index 7ef70758f..b6c282fee 100644 --- a/lib/LaTeXML/Common/Error.pm +++ b/lib/LaTeXML/Common/Error.pm @@ -37,7 +37,7 @@ our @EXPORT = ( # Progress Spinner qw(&ProgressSpinup &ProgressSpindown &ProgressStep), # Debugging messages - qw(&DebuggableFeature &Debug &CheckDebuggable), + qw(&DebuggableFeature &Debug &CheckDebuggable &Deprecated), # Colored-logging related functions qw(&colorizeString), # stateless message generation @@ -486,6 +486,13 @@ sub CheckDebuggable { print STDERR _freshline(\*STDERR), "Known debugging features: " . join(', ', sort keys %LaTeXML::Debuggable) . "\n"; } return; } +sub Deprecated { + my ($key, $version, $message) = @_; + return if $STATE && $STATE->lookupMapping('DEPRECATIONS_WARNED', $key); + Warn('deprecated', $key, undef, "$key is deprecated (as of $version)", $message); + $STATE->assignMapping('DEPRECATIONS_WARNED', $key => 1) if $STATE; + return; } + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Handlers for perl's die & warn # We'll try to decode some common errors to make them more usable diff --git a/lib/LaTeXML/Core/Definition/Conditional.pm b/lib/LaTeXML/Core/Definition/Conditional.pm index 50f84eedb..18bd156ee 100644 --- a/lib/LaTeXML/Core/Definition/Conditional.pm +++ b/lib/LaTeXML/Core/Definition/Conditional.pm @@ -28,14 +28,17 @@ sub new { my ($class, $cs, $parameters, $test, %traits) = @_; my $source = $STATE->getStomach->getGullet->getMouth; return bless { cs => $cs, parameters => $parameters, test => $test, - locator => $source->getLocator, - isExpandable => 1, + locator => $source->getLocator, %traits }, $class; } +sub isExpandable { + return 1; } + sub getTest { my ($self) = @_; return $$self{test}; } +# This MUST return Tokens() or undef. (NOT a Token) sub invoke { my ($self, $gullet) = @_; # A real conditional must have condition_type set @@ -132,17 +135,17 @@ sub skipConditionalBody { shift(@$stack); } # then DO pop that conditional's frame; it's DONE! elsif (!--$level) { # If no more nesting, we're done. shift(@$stack); # Done with this frame - return $t; } } # AND Return the finishing token. + return TokensI($t); } } # AND Return the finishing token. elsif ($level > 1) { } # Ignore \else,\or nested in the body. elsif (($cond_type eq 'or') && (++$n_ors == $nskips)) { - return $t; } + return TokensI($t); } elsif (($cond_type eq 'else') && $nskips # Found \else and we're looking for one? # Make sure this \else is NOT for a nested \if that is part of the test clause! && ($$stack[0] eq $LaTeXML::IFFRAME)) { # No need to actually call elseHandler, but note that we've seen an \else! $$stack[0]{elses} = 1; - return $t; } } # } #} + return TokensI($t); } } # } #} Error('expected', '\fi', $gullet, "Missing \\fi or \\else, conditional fell off end", "Conditional started at " . ToString($start)); return; } @@ -156,7 +159,7 @@ sub invoke_else { . " since we seem not to be in a conditional"); return; } elsif ($$stack[0]{parsing}) { # Defer expanding the \else if we're still parsing the test - return Tokens(T_CS('\relax'), $LaTeXML::CURRENT_TOKEN); } + return TokensI(T_CS('\relax'), $LaTeXML::CURRENT_TOKEN); } elsif ($$stack[0]{elses}) { # Already seen an \else's at this level? Error('unexpected', $LaTeXML::CURRENT_TOKEN, $gullet, "Extra " . Stringify($LaTeXML::CURRENT_TOKEN), @@ -180,7 +183,7 @@ sub invoke_fi { . " since we seem not to be in a conditional"); return; } elsif ($$stack[0]{parsing}) { # Defer expanding the \else if we're still parsing the test - return Tokens(T_CS('\relax'), $LaTeXML::CURRENT_TOKEN); } + return TokensI(T_CS('\relax'), $LaTeXML::CURRENT_TOKEN); } else { # "expand" by removing the stack entry for this level local $LaTeXML::IFFRAME = $$stack[0]; $STATE->shiftValue('if_stack'); # Done with this frame diff --git a/lib/LaTeXML/Core/Definition/Expandable.pm b/lib/LaTeXML/Core/Definition/Expandable.pm index 72bc4cf39..909ee5231 100644 --- a/lib/LaTeXML/Core/Definition/Expandable.pm +++ b/lib/LaTeXML/Core/Definition/Expandable.pm @@ -24,21 +24,27 @@ use base qw(LaTeXML::Core::Definition); sub new { my ($class, $cs, $parameters, $expansion, %traits) = @_; - $expansion = Tokens($expansion) if ref $expansion eq 'LaTeXML::Core::Token'; my $source = $STATE->getStomach->getGullet->getMouth; - if (ref $expansion eq 'LaTeXML::Core::Tokens') { + my $type = ref $expansion; + # expansion must end up Tokens or CODE + if (!$type) { + $expansion = TokenizeInternal($expansion)->packParameters; } + elsif ($type eq 'LaTeXML::Core::Token') { + $expansion = TokensI($expansion); } + elsif ($type eq 'LaTeXML::Core::Tokens') { Fatal('misdefined', $cs, $source, "Expansion of '" . ToString($cs) . "' has unbalanced {}", "Expansion is " . ToString($expansion)) unless $expansion->isBalanced; $expansion = $expansion->packParameters unless $traits{nopackParameters}; } - elsif (!ref $expansion) { - $expansion = TokenizeInternal($expansion)->packParameters; } - + elsif ($type ne 'CODE') { + Error('misdefined', $cs, $source, + "Expansion of '" . ToString($cs) . "' cannot be of type '$type'"); + $expansion = TokensI(); } return bless { cs => $cs, parameters => $parameters, expansion => $expansion, - locator => $source->getLocator, - isProtected => $traits{protected} || $STATE->getPrefix('protected'), - isOuter => $traits{outer} || $STATE->getPrefix('outer'), - isLong => $traits{long} || $STATE->getPrefix('long'), - isExpandable => 1, + locator => $source->getLocator, + isProtected => $traits{protected} || $STATE->getPrefix('protected'), + isOuter => $traits{outer} || $STATE->getPrefix('outer'), + isLong => $traits{long} || $STATE->getPrefix('long'), + hasCCARG => (($type ne 'CODE') && (grep { $$_[1] == CC_ARG; } $expansion->unlist) ? 1 : 0), %traits }, $class; } sub isExpandable { @@ -49,6 +55,7 @@ sub getExpansion { return $$self{expansion}; } # Expand the expandable control sequence. This should be carried out by the Gullet. +# This MUST return Tokens() or undef. (NOT a Token) sub invoke { no warnings 'recursion'; my ($self, $gullet, $onceonly) = @_; @@ -80,20 +87,22 @@ sub invoke { Error('recursion', $$self{cs}, $gullet, "Token " . Stringify($$self{cs}) . " expands into itself!", "defining as empty"); - $expansion = Tokens(); } } + $expansion = TokensI(); } } $result = $expansion; } else { my @args = $parms->readArguments($gullet, $self); - # for "real" macros, make sure all args are Tokens - my $r; - my @targs = map { ($_ && ($r = ref $_) - && (($r eq 'LaTeXML::Core::Token') || ($r eq 'LaTeXML::Core::Tokens')) - ? $_ : Tokens(Revert($_))); } @args; + if ($$self{hasCCARG}) { # Do we actually need to substitute the args in? + my $r; # Make sure they are actually Tokens! + @args = map { ($_ && ($r = ref $_) + && (($r eq 'LaTeXML::Core::Token') || ($r eq 'LaTeXML::Core::Tokens')) + ? $_ : Tokens(Revert($_))); } @args; + $result = $expansion->substituteParameters(@args); } + else { + $result = $expansion; } if ($tracing) { # More involved... Debug($self->tracingCSName . ' ->' . tracetoString($expansion)); - Debug($self->tracingArgs(@targs)) if @args; } - $result = $expansion->substituteParameters(@targs); } - # Getting exclusive requires dubious Gullet support! + Debug($self->tracingArgs(@args)) if @args; } } + # Getting exclusive profiling requires dubious Gullet support! $result = Tokens($result, T_MARKER($profiled)) if $profiled; return $result; } diff --git a/lib/LaTeXML/Core/Document.pm b/lib/LaTeXML/Core/Document.pm index 989c54d37..a79bced30 100644 --- a/lib/LaTeXML/Core/Document.pm +++ b/lib/LaTeXML/Core/Document.pm @@ -72,7 +72,7 @@ sub getNode { my ($self) = @_; return $$self{node}; } sub setNode { my ($self, $node) = @_; - $self->closeText_internal; # Close any open text node, so ligatures run. + closeText_internal($self); # Close any open text node, so ligatures run. my $type = $node->nodeType; if ($type == XML_DOCUMENT_FRAG_NODE) { # Whoops my @n = $node->childNodes; @@ -88,7 +88,7 @@ sub setNode { sub getLocator { my ($self) = @_; - if (my $box = $self->getNodeBox($$self{node})) { + if (my $box = getNodeBox($self, $$self{node})) { return $box->getLocator; } else { return; } } # well? @@ -127,7 +127,7 @@ sub getFirstChildElement { # get the second element node (if any) in $node sub getSecondChildElement { my ($self, $node) = @_; - my $first_child = $self->getFirstChildElement($node); + my $first_child = getFirstChildElement($self, $node); my $second_child = $first_child && $first_child->nextSibling; while ($second_child && $second_child->nodeType != XML_ELEMENT_NODE) { $second_child = $second_child->nextSibling; } @@ -174,7 +174,7 @@ sub canContainIndirect { # $imodel{$tag}{$child} => $intermediate || $child my $imodel = $STATE->lookupValue('INDIRECT_MODEL'); if (!$imodel) { - $imodel = $self->computeIndirectModel(); + $imodel = computeIndirectModel($self); $STATE->assignValue(INDIRECT_MODEL => $imodel, 'global'); } return $$imodel{$tag}{$child}; } @@ -224,7 +224,7 @@ sub canContainSomehow { my $model = $$self{model}; $tag = $model->getNodeQName($tag) if ref $tag; # In case tag is a node. $child = $model->getNodeQName($child) if ref $child; # In case child is a node. - return $model->canContain($tag, $child) || $self->canContainIndirect($tag, $child); } + return $model->canContain($tag, $child) || canContainIndirect($self, $tag, $child); } sub canHaveAttribute { my ($self, $tag, $attrib) = @_; @@ -251,7 +251,7 @@ sub canAutoClose { && !$node->getAttribute('_noautoclose') # without _noautoclose && ($node->getAttribute('_autoclose') # and either with _autoclose # OR it has autoClose set on tag properties - || (($props = $STATE->lookupMapping('TAG_PROPERTIES', $self->getNodeQName($node))) + || (($props = $STATE->lookupMapping('TAG_PROPERTIES', getNodeQName($self, $node))) && $$props{autoClose}))); } @@ -288,22 +288,22 @@ sub doctest { my ($self, $when, $severe) = @_; local $LaTeXML::NNODES = 0; Debug("START DOC TEST $when....."); - if (my $root = $self->getDocument->documentElement) { - $self->doctest_rec(undef, $root, $severe); } + if (my $root = getDocument($self)->documentElement) { + doctest_rec($self, undef, $root, $severe); } Debug("...(" . $LaTeXML::NNODES . " nodes)....DONE"); return; } sub doctest_rec { my ($self, $parent, $node, $severe) = @_; # Check consistency of document, parent & type, before proceeding - $self->doctest_head($parent, $node, $severe); + doctest_head($self, $parent, $node, $severe); my $type = $node->nodeType; if ($type == XML_ELEMENT_NODE) { Debug("ELEMENT " . join(' ', "<" . $$self{model}->getNodeQName($node), (map { $_->nodeName . '="' . $_->getValue . '"' } $node->attributes)) . ">") if $severe; - $self->doctest_children($node, $severe); } + doctest_children($self, $node, $severe); } elsif ($type == XML_ATTRIBUTE_NODE) { Debug("ATTRIBUTE " . $node->nodeName . "=>" . $node->getValue) if $severe; } elsif ($type == XML_TEXT_NODE) { @@ -320,7 +320,7 @@ sub doctest_rec { # elsif($type == XML_DOCUMENT_TYPE_NODE){ elsif ($type == XML_DOCUMENT_FRAG_NODE) { Debug("DOCUMENT_FRAG") if $severe; - $self->doctest_children($node, $severe); } + doctest_children($self, $node, $severe); } # elsif($type == XML_NOTATION_NODE){} # elsif($type == XML_HTML_DOCUMENT_NODE){} # elsif($type == XML_DTD_NODE){} @@ -332,7 +332,7 @@ sub doctest_head { my ($self, $parent, $node, $severe) = @_; # Check consistency of document, parent & type, before proceeding Debug(" NODE $$node [") if $severe; # BEFORE checking nodeType! - if (!$node->ownerDocument->isSameNode($self->getDocument)) { + if (!$node->ownerDocument->isSameNode(getDocument($self))) { Debug("d!") if $severe; } if ($parent && !$node->parentNode->isSameNode($parent)) { Debug("p!") if $severe; } @@ -346,7 +346,7 @@ sub doctest_children { my $c = $node->firstChild; while ($c) { Debug("]") if $severe; - $self->doctest_rec($node, $c, $severe); + doctest_rec($self, $node, $c, $severe); Debug("[nc") if $severe; $c = $c->nextSibling; } Debug("]done") if $severe; @@ -358,11 +358,11 @@ sub doctest_children { # It removes the `helper' attributes that store fonts, source box, etc. sub finalize { my ($self) = @_; - $self->pruneXMDuals; - if (my $root = $self->getDocument->documentElement) { + pruneXMDuals($self); + if (my $root = getDocument($self)->documentElement) { local $LaTeXML::FONT = LaTeXML::Common::Font->textDefault; - $self->finalize_rec($root); - set_RDFa_prefixes($self->getDocument, $STATE->lookupValue('RDFa_prefixes')); } + finalize_rec($self, $root); + set_RDFa_prefixes(getDocument($self), $STATE->lookupValue('RDFa_prefixes')); } # return $$self{document}; } return $self; } @@ -393,7 +393,7 @@ sub finalize_rec { if ($attr eq 'class') { # Generalize? if (my $ovalue = $node->getAttribute('class')) { $value .= ' ' . $ovalue; } } - $self->setAttribute($node, $attr => $value); + setAttribute($self, $node, $attr => $value); # Merge to set the font currently in effect $declared_font = $declared_font->merge(%{ $pending_declaration{$attr}{properties} }); @@ -402,7 +402,7 @@ sub finalize_rec { # Optionally add ids to all nodes (AFTER all parsing, rearrangement, etc) if ($STATE && $STATE->lookupValue('GENERATE_IDS') && !$node->hasAttribute('xml:id') - && $self->canHaveAttribute($qname, 'xml:id') + && canHaveAttribute($self, $qname, 'xml:id') && ($qname ne 'ltx:document')) { LaTeXML::Package::GenerateID($self, $node); } @@ -411,14 +411,14 @@ sub finalize_rec { my $type = $child->nodeType; if ($type == XML_ELEMENT_NODE) { my $was_forcefont = $child->getAttribute('_force_font'); - $self->finalize_rec($child); + finalize_rec($self, $child); # Also check if child is $FONT_ELEMENT_NAME AND has no attributes # AND providing $node can contain that child's content, we'll collapse it. if (($model->getNodeQName($child) eq $FONT_ELEMENT_NAME) && !$was_forcefont && !$child->hasAttributes) { my @grandchildren = $child->childNodes; - if (!grep { !$self->canContain($qname, $_) } @grandchildren) { - $self->replaceNode($child, @grandchildren); } } + if (!grep { !canContain($self, $qname, $_) } @grandchildren) { + replaceNode($self, $child, @grandchildren); } } } # On the other hand, if the font declaration has NOT been effected, # We'll need to put an extra wrapper around the text! @@ -428,19 +428,19 @@ sub finalize_rec { my $elementname = $pending_declaration{element}{value} || $FONT_ELEMENT_NAME; delete $pending_declaration{element}; # If any... foreach my $key (keys %pending_declaration) { - delete $pending_declaration{$key} unless $self->canHaveAttribute($elementname, $key); } - if ($self->canContain($qname, $elementname) + delete $pending_declaration{$key} unless canHaveAttribute($self, $elementname, $key); } + if (canContain($self, $qname, $elementname) && scalar(keys %pending_declaration)) { # Too late to do wrapNodes? - my $text = $self->wrapNodes($elementname, $child); + my $text = wrapNodes($self, $elementname, $child); # Add (or combine) attributes foreach my $attr (keys %pending_declaration) { my $value = $pending_declaration{$attr}{value}; if ($attr eq 'class') { # Generalize? if (my $ovalue = $text->getAttribute('class')) { $value .= ' ' . $ovalue; } } - $self->setAttribute($text, $attr => $value); } - $self->finalize_rec($text); # Now have to clean up the new node! + setAttribute($self, $text, $attr => $value); } + finalize_rec($self, $text); # Now have to clean up the new node! } } } @@ -462,11 +462,11 @@ sub toString { my ($self, $format) = @_; # This line is to use libxml2's built-in serializer w/indentation heuristic. # Apparently, libxml2 is giving us "binary" or byte strings which we'd prefer to have as text. - # return decode('UTF-8',$self->getDocument->toString($format)); } + # return decode('UTF-8',getDocument($self)->toString($format)); } # This uses our own serializer emulating libxml2's heuristic indentation. - # return $self->serialize_aux($self->getDocument, 0, 0, 1); } + # return serialize_aux($self, getDocument($self), 0, 0, 1); } # This uses our own serializer w/ correct indentation rules. - return $self->serialize_aux($self->getDocument, 0, 0, 0); } + return serialize_aux($self, getDocument($self), 0, 0, 0); } # We ought to try for something close to C14N (http://www.w3.org/TR/xml-c14n), # but keep XML declaration, comments and don't convert empty elements. @@ -502,7 +502,7 @@ sub serialize_aux { # ? $noindent || grep { $_->nodeType != XML_ELEMENT_NODE } @children ? $noindent || grep { $_->nodeType == XML_TEXT_NODE } @children # This is the "Correct" way to determine whether to add indentation - : $model->canContain($self->getNodeQName($node), '#PCDATA')); + : $model->canContain(getNodeQName($self, $node), '#PCDATA')); return join('', ($noindent ? '' : $indent), $start, (scalar(@children) # with contents. @@ -584,22 +584,22 @@ sub absorb { local @LaTeXML::CONSTRUCTED_NODES = (); $box->beAbsorbed($self); @n = @LaTeXML::CONSTRUCTED_NODES; } # These were created just now - map { $self->recordConstructedNode($_) } @n; # record these for OUTER caller! + map { recordConstructedNode($self, $_) } @n; # record these for OUTER caller! push(@results, @n); } # but return only the most recent set. else { push(@results, $box->beAbsorbed($self)); } } # Else, plain string in text mode. elsif (!$props{isMath}) { - push(@results, $self->openText($box, $props{font} || ($LaTeXML::BOX && $LaTeXML::BOX->getFont))); } + push(@results, openText($self, $box, $props{font} || ($LaTeXML::BOX && $LaTeXML::BOX->getFont))); } # Or plain string in math mode. # Note text nodes can ONLY appear in or !!! # Have we already opened an XMTok? Then insert into it. elsif ($$self{model}->getNodeQName($$self{node}) eq $MATH_TOKEN_NAME) { - push(@results, $self->openMathText_internal($box)); } + push(@results, openMathText_internal($self, $box)); } # Else create the XMTok now. else { # Odd case: constructors that work in math & text can insert raw strings in Math mode. - push(@results, $self->insertMathToken($box, font => $props{font})); } } + push(@results, insertMathToken($self, $box, font => $props{font})); } } return @results; } # Note that a box has been absorbed creating $node; @@ -637,18 +637,18 @@ sub filterChildren { # Shorthand for open,absorb,close, but returns the new node. sub insertElement { my ($self, $qname, $content, %attrib) = @_; - my $node = $self->openElement($qname, %attrib); + my $node = openElement($self, $qname, %attrib); if (ref $content eq 'ARRAY') { - map { $self->absorb($_) } @$content; } + map { absorb($self, $_) } @$content; } elsif (defined $content) { - $self->absorb($content); } + absorb($self, $content); } # In obscure situations, $node may have already gotten closed? # close it if it is still open. my $c = $$self{node}; while ($c && ($c->nodeType != XML_DOCUMENT_NODE) && !$c->isSameNode($node)) { $c = $c->parentNode; } if ($c->isSameNode($node)) { - $self->closeElement($qname); } + closeElement($self, $qname); } return $node; } sub insertMathToken { @@ -659,16 +659,16 @@ sub insertMathToken { if ($attributes{isSpace} && (defined $string) && ($string =~ /^\s*$/)) { $string = undef; } # Make empty hint, of only spaces if (($qname eq $MATH_TOKEN_NAME) && ($cur_qname eq $qname)) { # Already INSIDE a token! - $self->openMathText_internal($string) if defined $string; + openMathText_internal($self, $string) if defined $string; return $$self{node}; } else { - my $node = $self->openElement($qname, %attributes); + my $node = openElement($self, $qname, %attributes); my $box = $attributes{_box} || $LaTeXML::BOX; my $font = $attributes{font} || $box->getFont; - $self->setNodeFont($node, $font); - $self->setNodeBox($node, $box); - $self->openMathText_internal($string) if defined $string; - $self->closeNode_internal($node); # Should be safe. + setNodeFont($self, $node, $font); + setNodeBox($self, $node, $box); + openMathText_internal($self, $string) if defined $string; + closeNode_internal($self, $node); # Should be safe. return $node; } } # Insert a new comment, or append to previous comment. @@ -706,7 +706,7 @@ sub insertPI { (grep { $_ !~ /^(?:class|package|options)$/ } sort keys %attrib)); my $data = join(' ', map { $_ . "=\"" . ToString($attrib{$_}) . "\"" } @keys); my $pi = $$self{document}->createProcessingInstruction($op, $data); - $self->closeText_internal; # Close any open text node + closeText_internal($self); # Close any open text node if ($$self{node}->nodeType == XML_DOCUMENT_NODE) { push(@{ $$self{pending} }, $pi); } else { @@ -744,39 +744,39 @@ sub openText { my $t = $node->nodeType; return if ((!defined $text) || $text =~ /^\s*$/) && (($t == XML_DOCUMENT_NODE) # Ignore initial whitespace - || (($t == XML_ELEMENT_NODE) && !$self->canContain($node, '#PCDATA'))); + || (($t == XML_ELEMENT_NODE) && !canContain($self, $node, '#PCDATA'))); return if $font->getFamily eq 'nullfont'; Debug("openText \"$text\" /" . Stringify($font) . " at " . Stringify($node)) if $LaTeXML::DEBUG{document}; # Get the desired font attributes, particularly the desired element # (usually ltx:text, but let Font override, eg for \emph) - my $declared_font = $self->getNodeFont($node); + my $declared_font = getNodeFont($self, $node); my %pending_declaration = $font->relativeTo($declared_font); my $elementname = $pending_declaration{element}{value} || $FONT_ELEMENT_NAME; if (($t != XML_DOCUMENT_NODE) # If not at document begin && !(($t == XML_TEXT_NODE) && # And not appending text in same font. - ($font->distance($self->getNodeFont($node->parentNode)) == 0))) { + ($font->distance(getNodeFont($self, $node->parentNode)) == 0))) { # then we'll need to do some open/close to get fonts matched. - $node = $self->closeText_internal; # Close text node, if any. + $node = closeText_internal($self); # Close text node, if any. my ($bestdiff, $closeto) = (99999, $node); my $n = $node; while ($n->nodeType != XML_DOCUMENT_NODE) { - my $d = $font->distance($self->getNodeFont($n)); + my $d = $font->distance(getNodeFont($self, $n)); if ($d < $bestdiff) { $bestdiff = $d; $closeto = $n; last if ($d == 0); } last if ($$self{model}->getNodeQName($n) ne $elementname) || $n->getAttribute('_noautoclose'); $n = $n->parentNode; } - $self->closeToNode($closeto) if $closeto ne $node; # Move to best starting point for this text. - $self->openElement($elementname, font => $font, + closeToNode($self, $closeto) if $closeto ne $node; # Move to best starting point for this text. + openElement($self, $elementname, font => $font, _fontswitch => 1, _autoopened => 1) if $bestdiff > 0; # Open if needed. } # Finally, insert the darned text. - my $tnode = $self->openText_internal($text); - $self->recordConstructedNode($tnode); + my $tnode = openText_internal($self, $text); + recordConstructedNode($self, $tnode); return $tnode; } # Mystery: @@ -787,12 +787,12 @@ sub openElement { my ($self, $qname, %attributes) = @_; ProgressStep() if ($$self{progress}++ % $CONSTRUCTION_PROGRESS_QUANTUM) == 0; Debug("openElement $qname at " . Stringify($$self{node})) if $LaTeXML::DEBUG{document}; - my $point = $self->find_insertion_point($qname); + my $point = find_insertion_point($self, $qname); $attributes{_box} = $LaTeXML::BOX unless $attributes{_box}; - my $newnode = $self->openElementAt($point, $qname, + my $newnode = openElementAt($self, $point, $qname, _font => $attributes{font} || $attributes{_box}->getFont, %attributes); - $self->setNode($newnode); + setNode($self, $newnode); return $newnode; } # Note: This closes the deepest open node of a given type. @@ -803,18 +803,18 @@ sub openElement { sub closeElement { my ($self, $qname) = @_; Debug("closeElement $qname at " . Stringify($$self{node})) if $LaTeXML::DEBUG{document}; - $self->closeText_internal(); + closeText_internal($self); my ($node, @cant_close) = ($$self{node}); while ($node->nodeType != XML_DOCUMENT_NODE) { my $t = $$self{model}->getNodeQName($node); # autoclose until node of same name BUT also close nodes opened' for font switches! last if ($t eq $qname) && !(($t eq $FONT_ELEMENT_NAME) && $node->getAttribute('_fontswitch')); - push(@cant_close, $node) unless $self->canAutoClose($node); + push(@cant_close, $node) unless canAutoClose($self, $node); $node = $node->parentNode; } if ($node->nodeType == XML_DOCUMENT_NODE) { # Didn't find $qname at all!! Error('malformed', $qname, $self, "Attempt to close " . ($qname eq '#PCDATA' ? $qname : '') . ", which isn't open", - "Currently in " . $self->getInsertionContext()); + "Currently in " . getInsertionContext($self)); return; } else { # Found node. # Intervening non-auto-closeable nodes!! @@ -824,7 +824,7 @@ sub closeElement { "Descendents are " . join(', ', map { Stringify($_) } @cant_close)) if @cant_close; # So, now close up to the desired node. - $self->closeNode_internal($node); + closeNode_internal($self, $node); return $node; } } # Check whether it is possible to open $qname at this point, @@ -833,8 +833,8 @@ sub isOpenable { my ($self, $qname) = @_; my $node = $$self{node}; while ($node) { - return 1 if $self->canContainSomehow($node, $qname); - return 0 unless $self->canAutoClose($node); # could close, then check if parent can contain + return 1 if canContainSomehow($self, $node, $qname); + return 0 unless canAutoClose($self, $node); # could close, then check if parent can contain $node = $node->parentNode; } return 0; } @@ -851,7 +851,7 @@ sub isCloseable { return if $node->nodeType == XML_DOCUMENT_NODE; my $this_qname = $$self{model}->getNodeQName($node); last if $this_qname eq $qname; - return unless $self->canAutoClose($node); + return unless canAutoClose($self, $node); $node = $node->parentNode; } $node = $node->parentNode if @tags; } return $node; } @@ -860,8 +860,8 @@ sub isCloseable { sub maybeCloseElement { my ($self, $qname) = @_; Debug("maybeCloseNode(int) $qname") if $LaTeXML::DEBUG{document}; - if (my $node = $self->isCloseable($qname)) { - $self->closeNode_internal($node); + if (my $node = isCloseable($self, $qname)) { + closeNode_internal($self, $node); return $node; } } # This closes all nodes until $node becomes the current point. @@ -873,20 +873,20 @@ sub closeToNode { my $lastopen; # go up the tree from current node, till we find $node while ((($t = $n->getType) != XML_DOCUMENT_NODE) && !$n->isSameNode($node)) { - push(@cant_close, $n) unless $self->canAutoClose($n); + push(@cant_close, $n) unless canAutoClose($self, $n); $lastopen = $n; $n = $n->parentNode; } if ($t == XML_DOCUMENT_NODE) { # Didn't find $node at all!! Error('malformed', $model->getNodeQName($node), $self, "Attempt to close to " . Stringify($node) . ", which isn't open", - "Currently in " . $self->getInsertionContext()) unless $ifopen; + "Currently in " . getInsertionContext($self)) unless $ifopen; return; } else { # Found node. Error('malformed', $model->getNodeQName($node), $self, "Closing to " . Stringify($node) . " whose open descendents do not auto-close", "Descendents are " . join(', ', map { Stringify($_) } @cant_close)) if @cant_close; # But found has intervening non-auto-closeable nodes!! - $self->closeNode_internal($lastopen) if $lastopen; } + closeNode_internal($self, $lastopen) if $lastopen; } return; } # This closes all nodes until $node is closed. @@ -897,19 +897,19 @@ sub closeNode { my $n = $$self{node}; Debug("To closeNode " . Stringify($node)) if $LaTeXML::DEBUG{document}; while ((($t = $n->getType) != XML_DOCUMENT_NODE) && !$n->isSameNode($node)) { - push(@cant_close, $n) unless $self->canAutoClose($n); + push(@cant_close, $n) unless canAutoClose($self, $n); $n = $n->parentNode; } if ($t == XML_DOCUMENT_NODE) { # Didn't find $qname at all!! Error('malformed', $model->getNodeQName($node), $self, "Attempt to close " . Stringify($node) . ", which isn't open", - "Currently in " . $self->getInsertionContext()); } + "Currently in " . getInsertionContext($self)); } else { # Found node. # Intervening non-auto-closeable nodes!! Error('malformed', $model->getNodeQName($node), $self, "Closing " . Stringify($node) . " whose open descendents do not auto-close", "Descendents are " . join(', ', map { Stringify($_) } @cant_close)) if @cant_close; - $self->closeNode_internal($node); } + closeNode_internal($self, $node); } return; } sub maybeCloseNode { @@ -919,7 +919,7 @@ sub maybeCloseNode { my $n = $$self{node}; Debug("To closeNode " . Stringify($node)) if $LaTeXML::DEBUG{document}; while ((($t = $n->getType) != XML_DOCUMENT_NODE) && !$n->isSameNode($node)) { - push(@cant_close, $n) unless $self->canAutoClose($n); + push(@cant_close, $n) unless canAutoClose($self, $n); $n = $n->parentNode; } if ($t == XML_DOCUMENT_NODE) { } # Didn't find $qname at all!! else { # Found node. @@ -928,7 +928,7 @@ sub maybeCloseNode { "Closing " . Stringify($node) . " whose open descendents do not auto-close", "Descendents are " . join(', ', map { Stringify($_) } @cant_close)) if @cant_close; - $self->closeNode_internal($node); } + closeNode_internal($self, $node); } return; } # Add the given attribute to the nearest node that is allowed to have it. @@ -943,7 +943,7 @@ sub addAttribute { Error('malformed', $key, $self, "Attribute $key not allowed in this node or ancestors"); } else { - $self->setAttribute($node, $key, $value); } + setAttribute($self, $node, $key, $value); } return; } #********************************************************************** @@ -972,40 +972,40 @@ sub getInsertionContext { # or down (inserting auto-openable elements), as needed. sub find_insertion_point { my ($self, $qname, $has_opened) = @_; - $self->closeText_internal; # Close any current text node. + closeText_internal($self); # Close any current text node. my $cur_qname = $$self{model}->getNodeQName($$self{node}); my $inter; # If $qname is allowed at the current point, we're done. - if ($self->canContain($cur_qname, $qname)) { + if (canContain($self, $cur_qname, $qname)) { return $$self{node}; } # Else, if we can create an intermediate node that accepts $qname, we'll do that. - elsif (($inter = $self->canContainIndirect($cur_qname, $qname)) + elsif (($inter = canContainIndirect($self, $cur_qname, $qname)) && ($inter ne $qname) && ($inter ne $cur_qname)) { Debug("Need intermediate $inter to open $qname") if $LaTeXML::DEBUG{document}; - $self->openElement($inter, _autoopened => 1, - font => $self->getNodeFont($$self{node})); - return $self->find_insertion_point($qname, $inter); } # And retry insertion (should work now). + openElement($self, $inter, _autoopened => 1, + font => getNodeFont($self, $$self{node})); + return find_insertion_point($self, $qname, $inter); } # And retry insertion (should work now). elsif ($has_opened) { # out of options if already inside an auto-open chain Error('malformed', $qname, $self, ($qname eq '#PCDATA' ? $qname : '<' . $qname . '>') . " failed auto-open through <$has_opened> at inadmissible <$cur_qname>", - "Currently in " . $self->getInsertionContext()); + "Currently in " . getInsertionContext($self)); return $$self{node}; } # But we'll do it anyway, unless Error => Fatal. else { # Now we're getting more desparate... # Check if we can auto close some nodes, and _then_ insert the $qname. my ($node, $closeto) = ($$self{node}); - while (($node->nodeType != XML_DOCUMENT_NODE) && $self->canAutoClose($node)) { + while (($node->nodeType != XML_DOCUMENT_NODE) && canAutoClose($self, $node)) { my $parent = $node->parentNode; - if ($self->canContainSomehow($parent, $qname)) { + if (canContainSomehow($self, $parent, $qname)) { $closeto = $node; last; } $node = $parent; } if ($closeto) { my $closeto_qname = $$self{model}->getNodeQName($closeto); - $self->closeNode_internal($closeto); # Close the auto closeable nodes. - return $self->find_insertion_point($qname); } # Then retry, possibly w/auto open's + closeNode_internal($self, $closeto); # Close the auto closeable nodes. + return find_insertion_point($self, $qname); } # Then retry, possibly w/auto open's else { # Didn't find a legit place. Error('malformed', $qname, $self, ($qname eq '#PCDATA' ? $qname : '<' . $qname . '>') . " isn't allowed in <$cur_qname>", - "Currently in " . $self->getInsertionContext()); + "Currently in " . getInsertionContext($self)); return $$self{node}; } } } # But we'll do it anyway, unless Error => Fatal. sub getInsertionCandidates { @@ -1053,41 +1053,41 @@ sub floatToElement { my @candidates = getInsertionCandidates($$self{node}); my $closeable = 1; # If the current node can contain already, we're fine right here - just return - if (@candidates && $self->canContain($candidates[0], $qname)) { + if (@candidates && canContain($self, $candidates[0], $qname)) { # Edge case: Don't resume at a text node, if it is current. Don't append more to it after other insertions. - $self->setNode($candidates[0]) if $$self{node}->getType == XML_TEXT_NODE; + setNode($self, $candidates[0]) if $$self{node}->getType == XML_TEXT_NODE; return $candidates[0]; } - while (@candidates && !$self->canContain($candidates[0], $qname)) { - $closeable &&= $self->canAutoClose($candidates[0]); + while (@candidates && !canContain($self, $candidates[0], $qname)) { + $closeable &&= canAutoClose($self, $candidates[0]); shift(@candidates); } if (my $n = shift(@candidates)) { if ($closeifpossible && $closeable) { - $self->closeToNode($n); } + closeToNode($self, $n); } else { my $savenode = $$self{node}; - $self->setNode($n); + setNode($self, $n); Debug("Floating from " . Stringify($savenode) . " to " . Stringify($n) . " for $qname") if ($$savenode ne $$n) && $LaTeXML::DEBUG{document}; return $savenode; } } else { Warn('malformed', $qname, $self, "No open node can contain element '$qname'", - $self->getInsertionContext()) - unless $self->canContainSomehow($$self{node}, $qname); } + getInsertionContext($self)) + unless canContainSomehow($self, $$self{node}, $qname); } return; } # Find a node in the document that can accept the attribute $key sub floatToAttribute { my ($self, $key) = @_; my @candidates = getInsertionCandidates($$self{node}); - while (@candidates && !$self->canHaveAttribute($candidates[0], $key)) { + while (@candidates && !canHaveAttribute($self, $candidates[0], $key)) { shift(@candidates); } if (my $n = shift(@candidates)) { my $savenode = $$self{node}; - $self->setNode($n); + setNode($self, $n); return $savenode; } else { Warn('malformed', $key, $self, "No open node can get attribute '$key'", - $self->getInsertionContext()); + getInsertionContext($self)); return; } } # find a node that can accept a label. @@ -1105,24 +1105,24 @@ sub floatToLabel { my @candidates = @ancestors; # Should we only accept a node that already has an id, or should we create an id? while (@candidates - && !($self->canHaveAttribute($candidates[0], $key) + && !(canHaveAttribute($self, $candidates[0], $key) && $candidates[0]->hasAttribute('xml:id'))) { shift(@candidates); } my $node = shift(@candidates); if (!$node) { # No appropriate ancestor? my $sib = $ancestors[0] && $ancestors[0]->lastChild; - if ($sib && $self->canHaveAttribute($sib, $key) + if ($sib && canHaveAttribute($self, $sib, $key) && $sib->hasAttribute('xml:id')) { $node = $sib; } elsif (@ancestors) { # just take root element? $node = $ancestors[-1]; } } if ($node) { my $savenode = $$self{node}; - $self->setNode($node); + setNode($self, $node); return $savenode; } else { Warn('malformed', $key, $self, "No open node with an xml:id can get attribute '$key'", - $self->getInsertionContext()); + getInsertionContext($self)); return; } } sub openText_internal { @@ -1133,24 +1133,24 @@ sub openText_internal { Debug("Appending text \"$text\" to " . Stringify($$self{node})) if $LaTeXML::DEBUG{document}; my $parent = $$self{node}->parentNode; if ($LaTeXML::BOX && $parent->getAttribute('_autoopened')) { - $self->appendTextBox($parent, $LaTeXML::BOX); } + appendTextBox($self, $parent, $LaTeXML::BOX); } $$self{node}->appendData($text); } elsif (($p = $$self{node}->lastChild) && ($p->nodeType == XML_COMMENT_NODE) && ($pp = $p->previousSibling) && ($pp->nodeType == XML_TEXT_NODE)) { # Avoid spliting text runs: Swap to and THEN append $text $$self{node}->insertAfter($pp, $p); $$self{node} = $pp; - $self->openText_internal($text); } + openText_internal($self, $text); } elsif (($text =~ /\S/) # If non space - || $self->canContain($$self{node}, '#PCDATA')) { # or text allowed here - my $point = $self->find_insertion_point('#PCDATA'); + || canContain($self, $$self{node}, '#PCDATA')) { # or text allowed here + my $point = find_insertion_point($self, '#PCDATA'); my $node = $$self{document}->createTextNode($text); if ($point->getAttribute('_autoopened')) { - $self->appendTextBox($point, $LaTeXML::BOX); } + appendTextBox($self, $point, $LaTeXML::BOX); } Debug("Inserting text node for \"$text\" into " . Stringify($point)) if $LaTeXML::DEBUG{document}; $point->appendChild($node); - $self->setNode($node); } + setNode($self, $node); } return $$self{node}; } # return the text node (current) # Since xml text nodes don't have attributes to record the origining box, @@ -1158,16 +1158,16 @@ sub openText_internal { # Indeed, propogate it to ancestors if they were autoOpened for same cause (box) sub appendTextBox { my ($self, $node, $box) = @_; - my $origbox = $self->getNodeBox($node); + my $origbox = getNodeBox($self, $node); if ($origbox && ($box ne $origbox)) { # if not already the same box my $newbox = List($origbox, $box); - $self->setNodeBox($node, $newbox); + setNodeBox($self, $node, $newbox); my $p = $node; # AND, propogate change to autoOpen'd ancestors based on same initial box while (($p = $p->parentNode) && ($p->nodeType == XML_ELEMENT_NODE) && $p->getAttribute('_autoopened') - && (($self->getNodeBox($p) || '') eq $origbox)) { - $self->setNodeBox($p, $newbox); } } + && ((getNodeBox($self, $p) || '') eq $origbox)) { + setNodeBox($self, $p, $newbox); } } return; } # Question: Why do I have math ligatures handled within openMathText_internal, @@ -1177,10 +1177,10 @@ sub openMathText_internal { my ($self, $string) = @_; # And if there's already text??? my $node = $$self{node}; - my $font = $self->getNodeFont($node); + my $font = getNodeFont($self, $node); $node->appendText($string); if (!$STATE->lookupValue('NOMATHPARSE')) { - $self->applyMathLigatures($node); } + applyMathLigatures($self, $node); } return $node; } # New stategy (but inefficient): apply ligatures until one succeeds, @@ -1192,7 +1192,7 @@ sub applyMathLigatures { while (@ligatures) { my $matched = 0; foreach my $ligature (@ligatures) { - if ($self->applyMathLigature($node, $ligature)) { + if (applyMathLigature($self, $node, $ligature)) { @ligatures = grep { $_ ne $ligature } @ligatures; $matched = 1; last; } } @@ -1205,19 +1205,19 @@ sub applyMathLigature { my ($self, $node, $ligature) = @_; my ($nmatched, $newstring, %attr) = &{ $$ligature{matcher} }($self, $node); if ($nmatched) { - my @boxes = ($self->getNodeBox($node)); + my @boxes = (getNodeBox($self, $node)); $node->firstChild->setData($newstring); my $prev = $node; for (my $i = 0 ; $i < $nmatched - 1 ; $i++) { my $remove = $prev->previousSibling; - unshift(@boxes, $self->getNodeBox($remove)); + unshift(@boxes, getNodeBox($self, $remove)); if ($remove->nodeType == XML_COMMENT_NODE) { $prev = $remove; } # keep comments - else { $self->removeNode($remove); } } + else { removeNode($self, $remove); } } ## This fragment replaces the node's box by the composite boxes it replaces ## HOWEVER, this gets things out of sync because parent lists of boxes still ## have the old ones. Unless we could recursively replace all of them, we'd better skip it(??) if (scalar(@boxes) > 1) { - $self->setNodeBox($node, List(@boxes, mode => 'math')); } + setNodeBox($self, $node, List(@boxes, mode => 'math')); } foreach my $key (sort keys %attr) { my $value = $attr{$key}; if (defined $value) { @@ -1234,7 +1234,7 @@ sub closeText_internal { my $node = $$self{node}; if ($node->nodeType == XML_TEXT_NODE) { # Current node is text? my $parent = $node->parentNode; - my $font = $self->getNodeFont($parent); + my $font = getNodeFont($self, $parent); my $string = $node->data; my $ostring = $string; my $fonttest; @@ -1254,15 +1254,15 @@ sub closeText_internal { sub closeNode_internal { my ($self, $node) = @_; my $closeto = $node->parentNode; # Grab now in case afterClose screws the structure. - my $n = $self->closeText_internal; # Close any open text node. + my $n = closeText_internal($self); # Close any open text node. while ($n->nodeType == XML_ELEMENT_NODE) { - $self->closeElementAt($n); - $self->autoCollapseChildren($n); + closeElementAt($self, $n); + autoCollapseChildren($self, $n); last if $node->isSameNode($n); $n = $n->parentNode; } Debug("closeNode(int) " . Stringify($$self{node})) if $LaTeXML::DEBUG{document}; - $self->setNode($closeto); - # $self->autoCollapseChildren($node); + setNode($self, $closeto); + # autoCollapseChildren($self, $node); return $$self{node}; } # If these attributes are present on both of two nodes, @@ -1292,13 +1292,13 @@ sub autoCollapseChildren { # BUT, it isn't being forced somehow && !$c[0]->hasAttribute('_force_font')) { my $c = $c[0]; - $self->setNodeFont($node, $self->getNodeFont($c)); - $self->removeNode($c); + setNodeFont($self, $node, getNodeFont($self, $c)); + removeNode($self, $c); foreach my $gc ($c->childNodes) { $node->appendChild($gc); - $self->recordNodeIDs($node); } + recordNodeIDs($self, $node); } # Merge the attributes from the child onto $node - $self->mergeAttributes($c, $node); } + mergeAttributes($self, $c, $node); } return; } # When merging attributes of two nodes, some attributes should be combined @@ -1322,11 +1322,11 @@ sub mergeAttributes { if ($key eq 'xml:id') { # Use the replacement id if (!$to->hasAttribute($key) || ($override && $$override{$key})) { # BUT: If $to DID have an attribute, we really should patch any idrefs!!!!!!! - $self->unRecordID($val); # presuming that $from will be going away. - $val = $self->recordID($val, $to); + unRecordID($self, $val); # presuming that $from will be going away. + $val = recordID($self, $val, $to); $to->setAttribute($key, $val); } } elsif ($merge_attribute_spacejoin{$key}) { # combine space separated values - $self->addSSValues($to, $key, $val); } + addSSValues($self, $to, $key, $val); } elsif ($merge_attribute_semicolonjoin{$key}) { # combine space separated values my $oldval = $to->getAttribute($key); if ($oldval) { # if duplicate? @@ -1354,12 +1354,12 @@ sub mergeAttributes { sub makeError { my ($self, $type, $content) = @_; my $savenode = undef; - $savenode = $self->floatToElement('ltx:ERROR') - unless $self->isOpenable('ltx:ERROR'); - $self->openElement('ltx:ERROR', class => ToString($type)); - $self->openText_internal(ToString($content)); - $self->closeElement('ltx:ERROR'); - $self->setNode($savenode) if $savenode; + $savenode = floatToElement($self, 'ltx:ERROR') + unless isOpenable($self, 'ltx:ERROR'); + openElement($self, 'ltx:ERROR', class => ToString($type)); + openText_internal($self, ToString($content)); + closeElement($self, 'ltx:ERROR'); + setNode($self, $savenode) if $savenode; return; } #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1378,7 +1378,7 @@ sub setAttribute { $value = $value->toAttribute if ref $value; if ((defined $value) && ($value ne '')) { # Skip if `empty'; but 0 is OK! if ($key eq 'xml:id') { # If it's an ID attribute - $value = $self->recordID($value, $node); # Do id book keeping + $value = recordID($self, $value, $node); # Do id book keeping $node->setAttributeNS($LaTeXML::Common::XML::XML_NS, 'id', $value); } # and bypass all ns stuff elsif ($key !~ /:/) { # No colon; no namespace (the common case!) # Ignore attributes not allowed by the model, @@ -1393,7 +1393,7 @@ sub setAttribute { my $prefix = $node->lookupNamespacePrefix($ns); # namespace already declared? if (!$prefix) { # if namespace not already declared $prefix = $$self{model}->getDocumentNamespacePrefix($ns, 1); # get the prefix to use - $self->getDocument->documentElement->setNamespace($ns, $prefix, 0); } # and declare it + getDocument($self)->documentElement->setNamespace($ns, $prefix, 0); } # and declare it if ($prefix eq '#default') { # Probably shouldn't happen...? $node->setAttribute($name => $value); } else { @@ -1411,14 +1411,14 @@ sub addSSValues { my @old = split(/\s/, $oldvalues); foreach my $new (@values) { push(@old, $new) unless grep { $_ eq $new } @old; } - $self->setAttribute($node, $key => join(' ', sort @old)); } + setAttribute($self, $node, $key => join(' ', sort @old)); } else { - $self->setAttribute($node, $key => join(' ', sort @values)); } } + setAttribute($self, $node, $key => join(' ', sort @values)); } } return; } sub addClass { my ($self, $node, $class) = @_; - return $self->addSSValues($node, class => $class); } + return addSSValues($self, $node, class => $class); } #********************************************************************** # Association of nodes and ids (xml:id) @@ -1429,7 +1429,7 @@ sub recordID { # Can we recover? if (!$node->isSameNode($prev)) { my $badid = $id; - $id = $self->modifyID($id); + $id = modifyID($self, $id); Info('malformed', 'id', $node, "Duplicated attribute xml:id", "Using id='$id' on " . Stringify($node), "id='$badid' already set on " . Stringify($prev)); } } @@ -1444,17 +1444,17 @@ sub unRecordID { # These are used to record or unrecord, in bulk, all the ids within a node (tree). sub recordNodeIDs { my ($self, $node) = @_; - foreach my $idnode ($self->findnodes('descendant-or-self::*[@xml:id]', $node)) { + foreach my $idnode (findnodes($self, 'descendant-or-self::*[@xml:id]', $node)) { if (my $id = $idnode->getAttribute('xml:id')) { - my $newid = $self->recordID($id, $idnode); + my $newid = recordID($self, $id, $idnode); $idnode->setAttribute('xml:id' => $newid) if $newid ne $id; } } return; } sub unRecordNodeIDs { my ($self, $node) = @_; - foreach my $idnode ($self->findnodes('descendant-or-self::*[@xml:id]', $node)) { + foreach my $idnode (findnodes($self, 'descendant-or-self::*[@xml:id]', $node)) { if (my $id = $idnode->getAttribute('xml:id')) { - $self->unRecordID($id); } } + unRecordID($self, $id); } } return; } # Get a new, related, but unique id @@ -1496,18 +1496,18 @@ sub lookupID { # since it is also needed before conversion to parallel markup! sub markXMNodeVisibility { my ($self) = @_; - my @xmath = $self->findnodes('//ltx:XMath/*'); + my @xmath = findnodes($self, '//ltx:XMath/*'); foreach my $math (@xmath) { - foreach my $node ($self->findnodes('descendant-or-self::*[@_pvis or @_cvis]', $math)) { + foreach my $node (findnodes($self, 'descendant-or-self::*[@_pvis or @_cvis]', $math)) { $node->removeAttribute('_pvis'); $node->removeAttribute('_cvis'); } } foreach my $math (@xmath) { - $self->markXMNodeVisibility_aux($math, 1, 1); } + markXMNodeVisibility_aux($self, $math, 1, 1); } return; } sub markXMNodeVisibility_aux { my ($self, $node, $cvis, $pvis) = @_; - my $qname = $self->getNodeQName($node); + my $qname = getNodeQName($self, $node); return if (!$cvis || $node->getAttribute('_cvis')) && (!$pvis || $node->getAttribute('_pvis')); # Special case: for XMArg used to wrap "formal" arguments on the content side, # mark them as visible as presentation as well. @@ -1516,24 +1516,24 @@ sub markXMNodeVisibility_aux { $node->setAttribute('_pvis' => 1) if $pvis; if ($qname eq 'ltx:XMDual') { my ($c, $p) = element_nodes($node); - $self->markXMNodeVisibility_aux($c, 1, 0) if $cvis; - $self->markXMNodeVisibility_aux($p, 0, 1) if $pvis; } + markXMNodeVisibility_aux($self, $c, 1, 0) if $cvis; + markXMNodeVisibility_aux($self, $p, 0, 1) if $pvis; } elsif ($qname eq 'ltx:XMRef') { - # $self->markXMNodeVisibility_aux($self->realizeXMNode($node),$cvis,$pvis); } + # markXMNodeVisibility_aux($self, realizeXMNode($self, $node),$cvis,$pvis); } my $id = $node->getAttribute('idref'); if (!$id) { my $key = $node->getAttribute('_xmkey'); Warn('expected', 'id', $self, "Missing idref on ltx:XMRef", ($key ? ("_xmkey is $key") : ())); return; } - my $reffed = $self->lookupID($id); + my $reffed = lookupID($self, $id); if (!$reffed) { Warn('expected', 'node', $self, "No node found with id=$id (referred to from ltx:XMRef)"); return; } - $self->markXMNodeVisibility_aux($reffed, $cvis, $pvis); } + markXMNodeVisibility_aux($self, $reffed, $cvis, $pvis); } else { foreach my $child (element_nodes($node)) { - $self->markXMNodeVisibility_aux($child, $cvis, $pvis); } } + markXMNodeVisibility_aux($self, $child, $cvis, $pvis); } } return; } # Reduce any ltx:XMDual's to just the visible branch, if the other is not visible @@ -1543,16 +1543,16 @@ sub markXMNodeVisibility_aux { sub pruneXMDuals { my ($self) = @_; # RE-mark visibility! - $self->markXMNodeVisibility; + markXMNodeVisibility($self); # will reversing keep from problems removing nodes from trees that already have been removed? - foreach my $dual (reverse $self->findnodes('descendant-or-self::ltx:XMDual')) { + foreach my $dual (reverse findnodes($self, 'descendant-or-self::ltx:XMDual')) { my ($content, $presentation) = element_nodes($dual); - if (!$self->findnode('descendant-or-self::*[@_pvis or @_cvis]', $content)) { # content never seen - $self->collapseXMDual($dual, $presentation); } - elsif (!$self->findnode('descendant-or-self::*[@_pvis or @_cvis]', $presentation)) { # pres. - $self->collapseXMDual($dual, $content); } + if (!findnode($self, 'descendant-or-self::*[@_pvis or @_cvis]', $content)) { # content never seen + collapseXMDual($self, $dual, $presentation); } + elsif (!findnode($self, 'descendant-or-self::*[@_pvis or @_cvis]', $presentation)) { # pres. + collapseXMDual($self, $dual, $content); } else { # compact aligned structures, where possible - $self->compactXMDual($dual, $content, $presentation); } } + compactXMDual($self, $dual, $content, $presentation); } } return; } our $content_transfer_overrides = { map { ($_ => 1) } qw(decl_id meaning name omcd) }; @@ -1561,13 +1561,13 @@ our $dual_transfer_overrides = { %$content_transfer_overrides, sub compactXMDual { my ($self, $dual, $content, $presentation) = @_; - my $c_name = $self->getNodeQName($content); - my $p_name = $self->getNodeQName($presentation); + my $c_name = getNodeQName($self, $content); + my $p_name = getNodeQName($self, $presentation); # 1.Quick fix: merge two tokens if (($c_name eq 'ltx:XMTok') && ($p_name eq 'ltx:XMTok')) { - $self->mergeAttributes($content, $presentation, $content_transfer_overrides); - $self->mergeAttributes($dual, $presentation, $dual_transfer_overrides); - $self->replaceNode($dual, $presentation); + mergeAttributes($self, $content, $presentation, $content_transfer_overrides); + mergeAttributes($self, $dual, $presentation, $dual_transfer_overrides); + replaceNode($self, $dual, $presentation); return; } # 2.For now, only main use case is compacting mirror XMApp nodes @@ -1589,25 +1589,25 @@ sub compactXMDual { next; } # pres-refs-content, OK # we can handle content-side XMToks, to any XM* presentation subtree differing for now. - if ($self->getNodeQName($c_arg) ne 'ltx:XMTok') { + if (getNodeQName($self, $c_arg) ne 'ltx:XMTok') { return; } else { # otherwise we can compact this case. but delay actual libxml changes until we are *sure* the entire tree is compactable push(@new_args, [$c_arg, $p_arg]); } } # If we made it here, this is a dual with two mirrored applications and a single XMTok difference, compact it. - my $compact_apply = $self->openElementAt($dual->parentNode, 'ltx:XMApp'); + my $compact_apply = openElementAt($self, $dual->parentNode, 'ltx:XMApp'); for my $n_arg (@new_args) { # one of the args has our dual node that needs compacting if (ref $n_arg eq 'ARRAY') { my ($c_arg, $p_arg) = @$n_arg; - $self->mergeAttributes($c_arg, $p_arg, $content_transfer_overrides); + mergeAttributes($self, $c_arg, $p_arg, $content_transfer_overrides); $n_arg = $p_arg; } $n_arg->unbindNode; $compact_apply->appendChild($n_arg); } # if the dual has any attributes migrate them to the new XMApp - $self->mergeAttributes($dual, $compact_apply, $dual_transfer_overrides); - $self->replaceNode($dual, $compact_apply); - $self->closeElementAt($compact_apply); + mergeAttributes($self, $dual, $compact_apply, $dual_transfer_overrides); + replaceNode($self, $dual, $compact_apply); + closeElementAt($self, $compact_apply); return; } # Replace an XMDual with one of its branches @@ -1616,14 +1616,14 @@ sub collapseXMDual { # The other branch is not visible, nor referenced, # but the dual may have an id and be referenced if (my $dualid = $dual->getAttribute('xml:id')) { - $self->unRecordID($dualid); # We'll move or remove the ID from the dual + unRecordID($self, $dualid); # We'll move or remove the ID from the dual if (my $branchid = $branch->getAttribute('xml:id')) { # branch has id too! - foreach my $ref ($self->findnodes("//*[\@idref='$dualid']")) { + foreach my $ref (findnodes($self, "//*[\@idref='$dualid']")) { $ref->setAttribute(idref => $branchid); } } # Change dualid refs to branchid else { $branch->setAttribute('xml:id' => $dualid); # Just use same ID on the branch - $self->recordID($dualid => $branch); } } - $self->replaceTree($branch, $dual); + recordID($self, $dualid => $branch); } } + replaceTree($self, $branch, $dual); return; } #********************************************************************** @@ -1657,12 +1657,12 @@ sub setNodeFont { sub mergeNodeFontRec { my ($self, $node, $font) = @_; return unless ref $font; # ? - my $oldfont = $self->getNodeFont($node); + my $oldfont = getNodeFont($self, $node); my %props = $oldfont->purestyleChanges($font); my @nodes = ($node); while (my $n = shift(@nodes)) { if ($n->nodeType == XML_ELEMENT_NODE) { - $self->setNodeFont($n, $self->getNodeFont($n)->merge(%props)); + setNodeFont($self, $n, getNodeFont($self, $n)->merge(%props)); push(@nodes, $n->childNodes); } } return; } @@ -1695,11 +1695,11 @@ sub removeNode { my $chopped = $$self{node}->isSameNode($node); # Note if we're removing insertion point if ($node->nodeType == XML_ELEMENT_NODE) { # If an element, do ID bookkeeping. if (my $id = $node->getAttribute('xml:id')) { - $self->unRecordID($id); } - $chopped ||= grep { $self->removeNode_aux($_) } $node->childNodes; } + unRecordID($self, $id); } + $chopped ||= grep { removeNode_aux($self, $_) } $node->childNodes; } my $parent = $node->parentNode; if ($chopped) { # Don't remove insertion point! - $self->setNode($parent); } + setNode($self, $parent); } $parent->removeChild($node); } return $node; } @@ -1709,8 +1709,8 @@ sub removeNode_aux { my $chopped = $$self{node}->isSameNode($node); if ($node->nodeType == XML_ELEMENT_NODE) { # If an element, do ID bookkeeping. if (my $id = $node->getAttribute('xml:id')) { - $self->unRecordID($id); } - $chopped ||= grep { $self->removeNode_aux($_) } $node->childNodes; } + unRecordID($self, $id); } + $chopped ||= grep { removeNode_aux($self, $_) } $node->childNodes; } return $chopped; } #********************************************************************** @@ -1749,7 +1749,7 @@ sub openElementAt { $$self{model}->addSchemaDeclaration($self, $tag); map { $$self{document}->appendChild($_) } @{ $$self{pending} }; # Add saved comments, PI's $newnode = $$self{document}->createElement($tag); - $self->recordConstructedNode($newnode); + recordConstructedNode($self, $newnode); $$self{document}->setDocumentElement($newnode); if ($ns) { # Here, we're creating the initial, document element, which will hold ALL of the namespace declarations. @@ -1762,20 +1762,20 @@ sub openElementAt { $newnode->setNamespace($ns, $attprefix, 0); } $newnode->setNamespace($ns, $prefix, 1); } } else { - $font = $self->getNodeFont($point) unless $font; - $box = $self->getNodeBox($point) unless $box; - $newnode = $self->openElement_internal($point, $ns, $tag); } + $font = getNodeFont($self, $point) unless $font; + $box = getNodeBox($self, $point) unless $box; + $newnode = openElement_internal($self, $point, $ns, $tag); } foreach my $key (sort keys %attributes) { next if $key eq 'font'; # !!! next if $key eq 'locator'; # !!! - $self->setAttribute($newnode, $key, $attributes{$key}); } - $self->setNodeFont($newnode, $font) if $font; - $self->setNodeBox($newnode, $box) if $box; - $self->appendElementBox($newnode, $box) if $box; + setAttribute($self, $newnode, $key, $attributes{$key}); } + setNodeFont($self, $newnode, $font) if $font; + setNodeBox($self, $newnode, $box) if $box; + appendElementBox($self, $newnode, $box) if $box; Debug("Inserting " . Stringify($newnode) . " into " . Stringify($point)) if $LaTeXML::DEBUG{document}; # Run afterOpen operations - $self->afterOpen($newnode); + afterOpen($self, $newnode); return $newnode; } # When appending nodes to an autoOpen'd node, we'll need to record the new boxes there, too. @@ -1784,14 +1784,14 @@ sub appendElementBox { my ($p, $origbox); if (($p = $node->parentNode) && ($p->nodeType == XML_ELEMENT_NODE) && $p->getAttribute('_autoopened') - && ($origbox = $self->getNodeBox($p)) && ($origbox ne $box)) { + && ($origbox = getNodeBox($self, $p)) && ($origbox ne $box)) { my $newbox = List($origbox, $box); - $self->setNodeBox($p, $newbox); + setNodeBox($self, $p, $newbox); # AND, propogate to autoOpen'd ancestors due to same initial box (See appendTextBox) while (($p = $p->parentNode) && ($p->nodeType == XML_ELEMENT_NODE) && $p->getAttribute('_autoopened') - && (($self->getNodeBox($p) || '') eq $origbox)) { - $self->setNodeBox($p, $newbox); } } + && ((getNodeBox($self, $p) || '') eq $origbox)) { + setNodeBox($self, $p, $newbox); } } return; } sub openElement_internal { @@ -1799,12 +1799,12 @@ sub openElement_internal { my $newnode; if ($ns) { if (!defined $point->lookupNamespacePrefix($ns)) { # namespace not already declared? - $self->getDocument->documentElement + getDocument($self)->documentElement ->setNamespace($ns, $$self{model}->getDocumentNamespacePrefix($ns), 0); } $newnode = $point->addNewChild($ns, $tag); } else { $newnode = $point->appendChild($$self{document}->createElement($tag)); } - $self->recordConstructedNode($newnode); + recordConstructedNode($self, $newnode); return $newnode; } # Whenever a node has been created using openElementAt, @@ -1812,25 +1812,25 @@ sub openElement_internal { # Basically, this just runs any afterClose operations. sub closeElementAt { my ($self, $node) = @_; - return $self->afterClose($node); } + return afterClose($self, $node); } sub afterOpen { my ($self, $node) = @_; # Set current point to this node, just in case the afterOpen's use it. my $savenode = $$self{node}; - $self->setNode($node); - my $box = $self->getNodeBox($node); - map { &$_($self, $node, $box) } $self->getTagActionList($node, 'afterOpen'); - $self->setNode($savenode); + setNode($self, $node); + my $box = getNodeBox($self, $node); + map { &$_($self, $node, $box) } getTagActionList($self, $node, 'afterOpen'); + setNode($self, $savenode); return $node; } sub afterClose { my ($self, $node) = @_; # Should we set point to this node? (or to last child, or something ?? my $savenode = $$self{node}; - my $box = $self->getNodeBox($node); - map { &$_($self, $node, $box) } $self->getTagActionList($node, 'afterClose'); - $self->setNode($savenode); + my $box = getNodeBox($self, $node); + map { &$_($self, $node, $box) } getTagActionList($self, $node, 'afterClose'); + setNode($self, $savenode); return $node; } #********************************************************************** @@ -1854,11 +1854,11 @@ sub appendClone { local %LaTeXML::Core::Document::IDMAP = (); # Find all id's defined in the copy and change the id. foreach my $child (@newchildren) { - foreach my $idnode ($self->findnodes('.//@xml:id', $child)) { + foreach my $idnode (findnodes($self, './/@xml:id', $child)) { my $id = $idnode->getValue; - $LaTeXML::Core::Document::IDMAP{$id} = $self->modifyID($id); } } + $LaTeXML::Core::Document::IDMAP{$id} = modifyID($self, $id); } } # Now do the cloning (actually copying) and insertion. - $self->appendClone_aux($node, @newchildren); + appendClone_aux($self, $node, @newchildren); return $node; } sub appendClone_aux { @@ -1866,13 +1866,13 @@ sub appendClone_aux { foreach my $child (@newchildren) { my $type = $child->nodeType; if ($type == XML_ELEMENT_NODE) { - my $new = $self->openElement_internal($node, $child->namespaceURI, $child->localname); + my $new = openElement_internal($self, $node, $child->namespaceURI, $child->localname); foreach my $attr ($child->attributes) { if ($attr->nodeType == XML_ATTRIBUTE_NODE) { my $key = $attr->nodeName; if ($key eq 'xml:id') { # Use the replacement id my $newid = $LaTeXML::Core::Document::IDMAP{ $attr->getValue }; - $newid = $self->recordID($newid, $new); + $newid = recordID($self, $newid, $new); $new->setAttribute($key, $newid); } elsif ($key eq 'idref') { # Refer to the replacement id if it was replaced my $id = $attr->getValue; @@ -1882,9 +1882,9 @@ sub appendClone_aux { else { $new->setAttribute($attr->localname, $attr->getValue); } } } - $self->afterOpen($new); - $self->appendClone_aux($new, $child->childNodes); - $self->afterClose($new); } + afterOpen($self, $new); + appendClone_aux($self, $new, $child->childNodes); + afterClose($self, $new); } elsif ($type == XML_TEXT_NODE) { $node->appendTextNode($child->textContent); } } return $node; } @@ -1904,23 +1904,23 @@ sub wrapNodes { my $leave_open = 0; # Check if any of @nodes, or any of it's children, are the current node, and thus still "open" foreach my $n (@nodes) { - if ($self->isOpen($n)) { + if (isOpen($self, $n)) { $leave_open = 1; last; } } my $model = $$self{model}; my $parent = $nodes[0]->parentNode; my ($ns, $tag) = $model->decodeQName($qname); - my $new = $self->openElement_internal($parent, $ns, $tag); - $self->afterOpen($new); + my $new = openElement_internal($self, $parent, $ns, $tag); + afterOpen($self, $new); $parent->replaceChild($new, $nodes[0]); - if (my $font = $self->getNodeFont($parent)) { - $self->setNodeFont($new, $font); } - if (my $box = $self->getNodeBox($parent)) { - $self->setNodeBox($new, $box); } + if (my $font = getNodeFont($self, $parent)) { + setNodeFont($self, $new, $font); } + if (my $box = getNodeBox($self, $parent)) { + setNodeBox($self, $new, $box); } foreach my $node (@nodes) { $new->appendChild($node); } - $self->afterClose($new) unless $leave_open; + afterClose($self, $new) unless $leave_open; return $new; } # Check if $node, or any of it's children, are the current node, and thus still "open" @@ -1932,13 +1932,13 @@ sub isOpen { return 1; } else { foreach my $n ($node->childNodes) { - return 1 if $self->isOpen($n); } + return 1 if isOpen($self, $n); } return 0; } } # Unwrap the children of $node, by replacing $node by its children. sub unwrapNodes { my ($self, $node) = @_; - return $self->replaceNode($node, $node->childNodes); } + return replaceNode($self, $node, $node->childNodes); } # Replace $node by @nodes (presumably descendants of some kind?) sub replaceNode { @@ -1949,7 +1949,7 @@ sub replaceNode { if ($c0) { $parent->insertAfter($c1, $c0); } else { $parent->replaceChild($c1, $node); } $c0 = $c1; } - $self->removeNode($node); + removeNode($self, $node); return $node; } # initially since $node->setNodeName was broken in XML::LibXML 1.58 @@ -1959,7 +1959,7 @@ sub renameNode { my $model = $$self{model}; my ($ns, $tag) = $model->decodeQName($newname); my $parent = $node->parentNode; - my $new = $self->openElement_internal($parent, $ns, $tag); + my $new = openElement_internal($self, $parent, $ns, $tag); my $id; # Move to the position AFTER $node $parent->insertAfter($new, $node); @@ -1975,13 +1975,13 @@ sub renameNode { ## THEN call afterOpen... ? # It would normally be called before children added, # but how can we know if we're duplicated auto-added stuff? - $self->afterOpen($new); - $self->afterClose($new); + afterOpen($self, $new); + afterClose($self, $new); # Finally, remove the old node - $self->removeNode($node); + removeNode($self, $node); # and FINALLY, we can register the new node under the id. if ($id) { - my $newid = $self->recordID($id, $new); + my $newid = recordID($self, $id, $new); $new->setAttribute('xml:id' => $newid) if $newid ne $id; } return $new; } @@ -2000,8 +2000,8 @@ sub replaceTree { last if $sib->isSameNode($old); $parent->removeChild($sib); # We're putting these back, in a moment! unshift(@following, $sib); } - $self->removeNode($old); - $self->appendTree($parent, $new); + removeNode($self, $old); + appendTree($self, $parent, $new); my $inserted = $parent->lastChild; map { $parent->appendChild($_) } @following; # No need for clone return $inserted; } @@ -2012,36 +2012,36 @@ sub appendTree { if (ref $child eq 'ARRAY') { my ($tag, $attributes, @children) = @$child; if (!$tag && !$attributes) { - $self->appendTree($node, @children); } + appendTree($self, $node, @children); } else { - my $new = $self->openElementAt($node, $tag, ($attributes ? %$attributes : ())); - $self->appendTree($new, @children); - $self->closeElementAt($new); } } + my $new = openElementAt($self, $node, $tag, ($attributes ? %$attributes : ())); + appendTree($self, $new, @children); + closeElementAt($self, $new); } } elsif ((ref $child) =~ /^XML::LibXML::/) { my $type = $child->nodeType; if ($type == XML_ELEMENT_NODE) { - my $tag = $self->getNodeQName($child); - my %attributes = map { $_->nodeType == XML_ATTRIBUTE_NODE ? ($self->getNodeQName($_) => $_->getValue) : () } + my $tag = getNodeQName($self, $child); + my %attributes = map { $_->nodeType == XML_ATTRIBUTE_NODE ? (getNodeQName($self, $_) => $_->getValue) : () } $child->attributes; # DANGER: REMOVE the xml:id attribute from $child!!!! # This protects against some versions of XML::LibXML that warn against duplicate id's # Hopefully, you shouldn't be using the node any more if (my $id = $attributes{'xml:id'}) { $child->removeAttribute('xml:id'); - $self->unRecordID($id); } - my $new = $self->openElementAt($node, $tag, %attributes); - $self->appendTree($new, $child->childNodes); - $self->closeElementAt($new); } + unRecordID($self, $id); } + my $new = openElementAt($self, $node, $tag, %attributes); + appendTree($self, $new, $child->childNodes); + closeElementAt($self, $new); } elsif ($type == XML_DOCUMENT_FRAG_NODE) { - $self->appendTree($node, $child->childNodes); } + appendTree($self, $node, $child->childNodes); } elsif ($type == XML_TEXT_NODE) { $node->appendTextNode($child->textContent); } } elsif ((ref $child) && $child->isaBox) { - my $savenode = $self->getNode; - $self->setNode($node); - $self->absorb($child); - $self->setNode($savenode); } + my $savenode = getNode($self); + setNode($self, $node); + absorb($self, $child); + setNode($self, $savenode); } elsif (ref $child) { Warn('malformed', $child, $node, "Dont know how to add '$child' to document; ignoring"); } elsif (defined $child) { diff --git a/lib/LaTeXML/Core/Gullet.pm b/lib/LaTeXML/Core/Gullet.pm index d1448f0a1..5dd29cb12 100644 --- a/lib/LaTeXML/Core/Gullet.pm +++ b/lib/LaTeXML/Core/Gullet.pm @@ -57,7 +57,7 @@ sub openMouth { sub closeMouth { my ($self, $forced) = @_; if (!$forced && (@{ $$self{pushback} } || $$self{mouth}->hasMoreInput)) { - my $next = Stringify($self->readToken); + my $next = Stringify(readToken($self)); Error('unexpected', $next, $self, "Closing mouth with input remaining '$next'"); } $$self{mouth}->finish; if (@{ $$self{mouthstack} }) { @@ -106,7 +106,7 @@ sub flush { # and the mouth should end up empty afterwards, and only be closed here. sub readingFromMouth { my ($self, $mouth, $closure) = @_; - $self->openMouth($mouth, 1); # only allow mouth to be explicitly closed here. + openMouth($self, $mouth, 1); # only allow mouth to be explicitly closed here. my ($result, @result); if (wantarray) { @result = &$closure($self); } @@ -115,18 +115,18 @@ sub readingFromMouth { # $mouth must still be open, with (at worst) empty autoclosable mouths in front of it while (1) { if ($$self{mouth} eq $mouth) { - $self->closeMouth(1); last; } + closeMouth($self, 1); last; } elsif (!@{ $$self{mouthstack} }) { Error('unexpected', '', $self, "Mouth is unexpectedly already closed", "Reading from " . Stringify($mouth) . ", but it has already been closed."); last; } elsif (!$$self{autoclose} || @{ $$self{pushback} } || $$self{mouth}->hasMoreInput) { - my $next = Stringify($self->readToken); + my $next = Stringify(readToken($self)); Error('unexpected', $next, $self, "Unexpected input remaining: '$next'", "Finished reading from " . Stringify($mouth) . ", but it still has input."); $$self{mouth}->finish; - $self->closeMouth(1); } # ?? if we continue? + closeMouth($self, 1); } # ?? if we continue? else { - $self->closeMouth; } } + closeMouth($self); } } return (wantarray ? @result : $result); } # User feedback for where something (error?) occurred. @@ -168,11 +168,11 @@ sub getSourceMouth { sub showUnexpected { my ($self) = @_; my $message = "Input is empty"; - if (my $token = $self->readToken) { + if (my $token = readToken($self)) { my @pb = @{ $$self{pushback} }; $message = "Next token is " . Stringify($token) . " ( == " . Stringify($STATE->lookupMeaning($token)) . ")" - . (@pb ? " more: " . ToString(Tokens(@pb)) : ''); + . (@pb ? " more: " . ToString(TokensI(@pb)) : ''); unshift(@{ $$self{pushback} }, $token); } return $message; } @@ -219,7 +219,7 @@ sub handleTemplate { my ($self, $alignment, $token, $type, $hidden) = @_; Debug("Halign $alignment: ALIGNMENT Column ended at " . Stringify($token) . " type $type [" . Stringify($STATE->lookupMeaning($token)) . "]" - . "@ " . ToString($self->getLocator)) + . "@ " . ToString(getLocator($self))) if $LaTeXML::DEBUG{halign}; # Append expansion to end!?!?!?! local $LaTeXML::CURRENT_TOKEN = $token; @@ -228,7 +228,7 @@ sub handleTemplate { ### NOTE: Truly fishy smuggling w/ \hidden@cr my $arg; if (($type eq 'cr') && $hidden) { # \hidden@cr gets an argument as payload!!!!! - $arg = $self->readArg(); } + $arg = readArg($self); } Debug("Halign $alignment: column after " . ToString($post)) if $LaTeXML::DEBUG{halign}; if ((($type eq 'cr') || ($type eq 'crcr')) && $$alignment{in_row} && !$alignment->currentRow->{pseudorow}) { @@ -271,14 +271,14 @@ sub readToken { if ($cc == CC_COMMENT) { push(@{ $$self{pending_comments} }, $token); } elsif ($cc == CC_MARKER) { - $self->handleMarker($token); } } + handleMarker($self, $token); } } # Not in pushback, use the current mouth if (!defined $token) { while (($token = $$self{mouth}->readToken()) && $CATCODE_HOLD[$cc = $$token[1]]) { if ($cc == CC_COMMENT) { push(@{ $$self{pending_comments} }, $token); } # What to do with comments??? elsif ($cc == CC_MARKER) { - $self->handleMarker($token); } } } + handleMarker($self, $token); } } } ProgressStep() if ($$self{progress}++ % $TOKEN_PROGRESS_QUANTUM) == 0; # some infinite loops are hard to predict and may be # better guarded against via a global token limit. @@ -292,10 +292,10 @@ sub readToken { if ((defined $token) && !$LaTeXML::ALIGN_STATE # SHOULD count nesting of { }!!! when SCANNED (not digested) && $LaTeXML::READING_ALIGNMENT - && (($atoken, $atype, $ahidden) = $self->isColumnEnd($token))) { - $self->handleTemplate($LaTeXML::READING_ALIGNMENT, $token, $atype, $ahidden); } + && (($atoken, $atype, $ahidden) = isColumnEnd($self, $token))) { + handleTemplate($self, $LaTeXML::READING_ALIGNMENT, $token, $atype, $ahidden); } elsif ((defined $token) && ($$token[1] == CC_CS) && ($$token[0] eq '\dont_expand')) { - my $unexpanded = $self->readToken; # Replace next token with a special \relax + my $unexpanded = readToken($self); # Replace next token with a special \relax return T_CS('\special_relax'); } else { last; } } @@ -335,51 +335,46 @@ sub readXToken { if ($cc == CC_COMMENT) { push(@{ $$self{pending_comments} }, $token); } elsif ($cc == CC_MARKER) { - $self->handleMarker($token); } } + handleMarker($self, $token); } } if (!defined $token) { # Else read from current mouth while (($token = $$self{mouth}->readToken()) && $CATCODE_HOLD[$cc = $$token[1]]) { if ($cc == CC_COMMENT) { push(@{ $$self{pending_comments} }, $token); } elsif ($cc == CC_MARKER) { - $self->handleMarker($token); } } } + handleMarker($self, $token); } } } ProgressStep() if ($$self{progress}++ % $TOKEN_PROGRESS_QUANTUM) == 0; if (!defined $token) { return unless $autoclose && $$self{autoclose} && @{ $$self{mouthstack} }; - $self->closeMouth; } # Next input stream. + closeMouth($self); } # Next input stream. elsif (($cc == CC_CS) && ($$token[0] eq '\dont_expand')) { - my $unexpanded = $self->readToken; + my $unexpanded = readToken($self); return ($for_conditional && ($$unexpanded[1] == CC_ACTIVE) ? $unexpanded : T_CS('\special_relax')); } ## Wow!!!!! See TeX the Program \S 309 elsif (!$LaTeXML::ALIGN_STATE # SHOULD count nesting of { }!!! when SCANNED (not digested) && $LaTeXML::READING_ALIGNMENT - && (($atoken, $atype, $ahidden) = $self->isColumnEnd($token))) { - $self->handleTemplate($LaTeXML::READING_ALIGNMENT, $token, $atype, $ahidden); } + && (($atoken, $atype, $ahidden) = isColumnEnd($self, $token))) { + handleTemplate($self, $LaTeXML::READING_ALIGNMENT, $token, $atype, $ahidden); } ## Note: use general-purpose lookup, since we may reexamine $defn below elsif ($LaTeXML::Core::State::CATCODE_ACTIVE_OR_CS[$cc] && defined($defn = $STATE->lookupMeaning($token))) { if ((ref $defn) eq 'LaTeXML::Core::Token') { # \let to a token? Return it! return ($for_conditional ? $defn : $token); } - elsif (!$$defn{isExpandable} # Not expandable or is protected + elsif (!$defn->isExpandable # Not expandable or is protected || ($$defn{isProtected} && !$for_evaluation)) { return $token; } else { local $LaTeXML::CURRENT_TOKEN = $token; - my $r; no warnings 'recursion'; - my @expansion = map { (($r = ref $_) eq 'LaTeXML::Core::Token' ? $_ - : ($r eq 'LaTeXML::Core::Tokens' ? @$_ - : Error('misdefined', $r, undef, "Expected a Token, got " . Stringify($_), - "in " . ToString($defn)) || T_OTHER(Stringify($_)))) } - $defn->invoke($self); + my $expansion = $defn->invoke($self); # add the newly expanded tokens back into the gullet stream, in the ordinary case. - unshift(@{ $$self{pushback} }, @expansion); } } + unshift(@{ $$self{pushback} }, @$expansion) if $expansion; } } elsif ($$token[1] == CC_CS && !(defined $defn)) { - $STATE->generateErrorStub($self, $token); # cs SHOULD have defn by now; report early! + $STATE->generateErrorStub($self, $token); # cs SHOULD have defn by now; report early! return $token; } else { - return $token; } # just return it + return $token; } # just return it } - return; } # never get here. + return; } # never get here. # readBalanced approximates TeX's scan_toks (but doesn't parse \def parameter lists) # and only optionally requires the openning "{". @@ -397,13 +392,13 @@ our $DEFERRED_COMMANDS = { sub readBalanced { my ($self, $expanded, $macrodef, $require_open) = @_; local $LaTeXML::ALIGN_STATE = 1000000; - my $startloc = ($$self{verbosity} > 0) && $self->getLocator; + my $startloc = ($$self{verbosity} > 0) && getLocator($self); # Does we need to expand to get the { ??? if ($require_open) { - my $token = ($expanded ? $self->readXToken(0) : $self->readToken()); + my $token = ($expanded ? readXToken($self, 0) : readToken($self)); if ((!$token) || ($$token[1] != CC_BEGIN)) { Error('expected', '{', $self, "Expected opening '{'"); - return Tokens(); } } + return TokensI(); } } my @tokens = (); my $level = 1; my ($token, $cc, $defn, $atoken, $atype, $ahidden); @@ -415,11 +410,11 @@ sub readBalanced { # Examine pushback first while (($token = shift(@{ $$self{pushback} })) && $CATCODE_HOLD[$cc = $$token[1]]) { if ($cc == CC_COMMENT) { push(@tokens, $token); } - elsif ($cc == CC_MARKER) { $self->handleMarker($token); } } + elsif ($cc == CC_MARKER) { handleMarker($self, $token); } } if (!defined $token) { # Else read from current mouth while (($token = $$self{mouth}->readToken()) && $CATCODE_HOLD[$cc = $$token[1]]) { if ($cc == CC_COMMENT) { push(@tokens, $token); } - elsif ($cc == CC_MARKER) { $self->handleMarker($token); } } } + elsif ($cc == CC_MARKER) { handleMarker($self, $token); } } } ProgressStep() if ($$self{progress}++ % $TOKEN_PROGRESS_QUANTUM) == 0; if (!defined $token) { # What's the right error handling now? @@ -438,37 +433,33 @@ sub readBalanced { # Not sure if this code still applies within scan_toks??? elsif (!$LaTeXML::ALIGN_STATE # SHOULD count nesting of { }!!! when SCANNED (not digested) && $LaTeXML::READING_ALIGNMENT - && (($atoken, $atype, $ahidden) = $self->isColumnEnd($token))) { - $self->handleTemplate($LaTeXML::READING_ALIGNMENT, $token, $atype, $ahidden); } + && (($atoken, $atype, $ahidden) = isColumnEnd($self, $token))) { + handleTemplate($self, $LaTeXML::READING_ALIGNMENT, $token, $atype, $ahidden); } ## Note: use general-purpose lookup, since we may reexamine $defn below elsif ($expanded && $LaTeXML::Core::State::CATCODE_ACTIVE_OR_CS[$cc] && defined($defn = $STATE->lookupMeaning($token)) && ((ref $defn) ne 'LaTeXML::Core::Token') # an actual definition - && $$defn{isExpandable} + && $defn->isExpandable && (!$$defn{isProtected})) { # is this the right logic here? don't expand unless di local $LaTeXML::CURRENT_TOKEN = $token; my $r; no warnings 'recursion'; - my @expansion = map { (($r = ref $_) eq 'LaTeXML::Core::Token' ? $_ - : ($r eq 'LaTeXML::Core::Tokens' ? @$_ - : Error('misdefined', $r, undef, "Expected a Token, got " . Stringify($_), - "in " . ToString($defn)) || T_OTHER(Stringify($_)))) } - $defn->invoke($self); - next unless @expansion; + my $expansion = $defn->invoke($self); + next unless $expansion; # If a special \the type command, push the expansion directly into the result # Well, almost directly: handle any MARKER tokens now, and possibly un-pack T_PARAM if ($$DEFERRED_COMMANDS{ $$defn{cs}[0] }) { - foreach my $t (@expansion) { + foreach my $t (@$expansion) { my $cc = $$t[1]; - if ($cc == CC_MARKER) { $self->handleMarker($t); } + if ($cc == CC_MARKER) { handleMarker($self, $t); } elsif (($cc == CC_PARAM) && $macrodef) { push(@tokens, $t, $t); } # "unpack" to cover the packParameters at end! else { push(@tokens, $t); } } } else { # otherwise, prepend to pushback to be expanded further. - unshift(@{ $$self{pushback} }, @expansion); } } + unshift(@{ $$self{pushback} }, @$expansion); } } else { if ($expanded && ($$token[1] == CC_CS) && !(defined $defn)) { $STATE->generateErrorStub($self, $token); } # cs SHOULD have defn by now; report early! @@ -477,10 +468,10 @@ sub readBalanced { if ($level > 0) { # TODO: The current implementation has a limitation where if the balancing end is in a different mouth, # it will not be recognized. - my $loc_message = $startloc ? ("Started at " . ToString($startloc)) : ("Ended at " . ToString($self->getLocator)); + my $loc_message = $startloc ? ("Started at " . ToString($startloc)) : ("Ended at " . ToString(getLocator($self))); Error('expected', "}", $self, "Gullet->readBalanced ran out of input in an unbalanced state.", $loc_message); } - return ($macrodef ? Tokens(@tokens)->packParameters : Tokens(@tokens)); } + return ($macrodef ? TokensI(@tokens)->packParameters : TokensI(@tokens)); } #====================================================================== @@ -499,7 +490,7 @@ sub readRawLine { # If we still have peeked tokens, we ONLY want to combine it with the remainder # of the current line from the Mouth (NOT reading a new line) if (@tokens) { - return ToString(Tokens(@tokens)) . $$self{mouth}->readRawLine(1); } + return ToString(TokensI(@tokens)) . $$self{mouth}->readRawLine(1); } # Otherwise, read the next line from the Mouth. else { return $$self{mouth}->readRawLine; } } @@ -514,20 +505,20 @@ sub readRawLine { sub readNonSpace { my ($self) = @_; my $token; - do { $token = $self->readToken(); + do { $token = readToken($self); } while (defined $token && $$token[1] == CC_SPACE); # Inline ->getCatcode! return $token; } sub readXNonSpace { my ($self) = @_; my $token; - do { $token = $self->readXToken(0); + do { $token = readXToken($self, 0); } while (defined $token && $$token[1] == CC_SPACE); # Inline ->getCatcode! return $token; } sub skipSpaces { my ($self) = @_; - my $tok = $self->readNonSpace; + my $tok = readNonSpace($self); unshift(@{ $$self{pushback} }, $tok) if defined $tok; # Unread return; } @@ -535,15 +526,15 @@ sub skipSpaces { # if $expanded is true, it acts like , expanding the next token. sub skip1Space { my ($self, $expanded) = @_; - my $token = ($expanded ? $self->readXToken : $self->readToken); - unshift(@{ $$self{pushback} }, $token) if $token && !Equals($token, T_SPACE); + my $token = ($expanded ? readXToken($self) : readToken($self)); + unshift(@{ $$self{pushback} }, $token) if $token && !$token->defined_as(T_SPACE); return; } # = | \relax sub skipFiller { my ($self) = @_; while (1) { - my $tok = $self->readNonSpace; + my $tok = readNonSpace($self); return unless defined $tok; # Should \foo work too (where \let\foo\relax) ?? if (!$tok->equals(T_CS('\relax'))) { @@ -554,7 +545,7 @@ sub skipFiller { sub ifNext { my ($self, $token) = @_; - if (my $tok = $self->readToken()) { + if (my $tok = readToken($self)) { unshift(@{ $$self{pushback} }, $tok); # Unread return $tok->equals($token); } else { return 0; } } @@ -566,11 +557,11 @@ sub readMatch { my @tomatch = $choice->unlist; my @matched = (); my $token; - while (@tomatch && defined($token = $self->readToken) + while (@tomatch && defined($token = readToken($self)) && push(@matched, $token) && ($token->equals($tomatch[0]))) { shift(@tomatch); if ($$token[1] == CC_SPACE) { # If this was space, SKIP any following!!! - while (defined($token = $self->readToken) && ($$token[1] == CC_SPACE)) { + while (defined($token = readToken($self)) && ($$token[1] == CC_SPACE)) { push(@matched, $token); } unshift(@{ $$self{pushback} }, $token) if $token; } # Unread } @@ -584,14 +575,14 @@ sub readMatch { # AND, macros are expanded. sub readKeyword { my ($self, @keywords) = @_; - $self->skipSpaces; + skipSpaces($self); foreach my $keyword (@keywords) { $keyword = ToString($keyword) if ref $keyword; my @tomatch = split('', uc($keyword)); my @matched = (); my $tok; - while (@tomatch && defined($tok = $self->readXToken(0)) && push(@matched, $tok) - && (uc($tok->toString) eq $tomatch[0])) { + while (@tomatch && defined($tok = readXToken($self, 0)) && push(@matched, $tok) + && (uc($$tok[0]) eq $tomatch[0])) { shift(@tomatch); } return $keyword unless @tomatch; # All matched!!! unshift(@{ $$self{pushback} }, @matched); # Put 'em back and try next! @@ -611,16 +602,16 @@ sub readUntil { my $ntomatch = scalar(@want); if ($ntomatch == 1) { # Common, easy case: read till we match a single token my $want = $want[0]; - # while(($token = $self->readToken) && !$token->equals($want)){ + # while(($token = readToken($self)) && !$token->equals($want)){ while (($token = shift(@{ $$self{pushback} }) || $$self{mouth}->readToken()) && !$token->equals($want)) { my $cc = $$token[1]; if ($cc == CC_MARKER) { # would have been handled by readToken, but we're bypassing - $self->handleMarker($token); } + handleMarker($self, $token); } elsif ($$token[1] == CC_BEGIN) { # And if it's a BEGIN, copy till balanced END push(@tokens, $token); $nbraces++; - push(@tokens, $self->readBalanced, T_END); } + push(@tokens, readBalanced($self)->unlist, T_END); } else { push(@tokens, $token); } } } else { @@ -628,11 +619,11 @@ sub readUntil { my @ring = (); while (1) { # prefill the required number of tokens - while ((scalar(@ring) < $ntomatch) && ($token = $self->readToken)) { + while ((scalar(@ring) < $ntomatch) && ($token = readToken($self))) { if ($$token[1] == CC_BEGIN) { # read balanced, and refill ring. $nbraces++; - push(@tokens, @ring, $token, $self->readBalanced, T_END); # Copy directly to result - @ring = (); } # and retry + push(@tokens, @ring, $token, readBalanced($self)->unlist, T_END); # Copy directly to result + @ring = (); } # and retry else { push(@ring, $token); } } my $i; @@ -641,24 +632,48 @@ sub readUntil { last unless $token; push(@tokens, shift(@ring)); } } if (!defined $token) { # Ran out! - $self->unread(@tokens); # Not more correct, but maybe less confusing? + unread($self, @tokens); # Not more correct, but maybe less confusing? return; } # Notice that IFF the arg looks like {balanced}, the outer braces are stripped # so that delimited arguments behave more similarly to simple, undelimited arguments. if (($nbraces == 1) && ($tokens[0][1] == CC_BEGIN) && ($tokens[-1][1] == CC_END)) { shift(@tokens); pop(@tokens); } - return Tokens(@tokens); } + return TokensI(@tokens); } sub readUntilBrace { my ($self) = @_; my @tokens = (); my $token; - while (defined($token = $self->readToken())) { + while (defined($token = readToken($self))) { if ($$token[1] == CC_BEGIN) { # INLINE Catcode unshift(@{ $$self{pushback} }, $token); # Unread last; } push(@tokens, $token); } - return Tokens(@tokens); } + return TokensI(@tokens); } + +use constant T_csname => T_CS('\csname'); +use constant T_endcsname => T_CS('\endcsname'); + +sub readCSName { + my ($self) = @_; + my $token; + # Deyan Ginev & Dennis Mueller were right! Or partly so. + # TeX does NOT store the csname with the leading `\`, BUT stores active chars with a flag + # However, so long as the Mouth's CS and \string properly respect \escapechar, all's well! + my $cs = '\\'; + while (($token = readXToken($self, 1)) && (!$token->defined_as(T_endcsname))) { + my $cc = $$token[1]; + if ($cc == CC_CS) { + if (defined $STATE->lookupDefinition($token)) { + Error('unexpected', $token, $self, + "The control sequence " . ToString($token) + . " should not appear between \\csname and \\endcsname"); } + else { + Error('undefined', $token, $self, + "The token " . Stringify($token) . " is not defined"); } } + elsif ($cc == CC_SPACE) { $cs .= ' '; } # Keep newlines from having \n! + else { $cs .= $$token[0]; } } + return T_CS($cs); } #********************************************************************** # Higher-level readers: Read various types of things from the input: @@ -666,11 +681,11 @@ sub readUntilBrace { #********************************************************************** sub readArg { my ($self) = @_; - my $token = $self->readNonSpace; + my $token = readNonSpace($self); if (!defined $token) { return; } elsif ($$token[1] == CC_BEGIN) { # Inline ->getCatcode! - return $self->readBalanced(0); } + return readBalanced($self, 0); } else { return Tokens($token); } } @@ -678,11 +693,11 @@ sub readArg { # otherwise $default or undef. sub readOptional { my ($self, $default) = @_; - my $tok = $self->readNonSpace; + my $tok = readNonSpace($self); if (!defined $tok) { return; } elsif (($tok->equals(T_OTHER('[')))) { - return $self->readUntil(T_OTHER(']')); } + return readUntil($self, T_OTHER(']')); } else { unshift(@{ $$self{pushback} }, $tok); # Unread return $default; } } @@ -693,59 +708,69 @@ sub readOptional { #********************************************************************** sub readValue { my ($self, $type) = @_; - if ($type eq 'Number') { return $self->readNumber; } - elsif ($type eq 'Dimension') { return $self->readDimension; } - elsif ($type eq 'Glue') { return $self->readGlue; } - elsif ($type eq 'MuGlue') { return $self->readMuGlue; } - elsif ($type eq 'Tokens') { return $self->readTokensValue; } + if ($type eq 'Number') { return readNumber($self); } + elsif ($type eq 'Dimension') { return readDimension($self); } + elsif ($type eq 'Glue') { return readGlue($self); } + elsif ($type eq 'MuGlue') { return readMuGlue($self); } + elsif ($type eq 'Tokens') { return readTokensValue($self); } elsif ($type eq 'Token') { - my $token = $self->readToken; - if (Equals($token, T_CS('\csname'))) { - my $cstoken = $STATE->lookupDefinition($token)->invoke($self); - $self->unread($cstoken->unlist); - return $self->readToken; } + my $token = readToken($self); + if ($token->defined_as(T_csname)) { + return readCSName($self); } else { return $token; } } - elsif ($type eq 'any') { return $self->readArg; } + elsif ($type eq 'any') { return readArg($self); } else { Error('unexpected', $type, $self, "Gullet->readValue Didn't expect this type: $type"); return; } } +# Read a value from a numeric register, possibly changing sign, +# possibly coercing from a bigger type (eg. a Number from a Dimension) +our %RegisterCoercionTypes = ( + Number => { Dimension => \&Number, Glue => \&Number }, + Dimension => { Glue => \&Dimension }, + MuDimension => { MuGlue => \&MuDimension }, +); + sub readRegisterValue { - my ($self, $type) = @_; - my $token = $self->readXToken; + my ($self, $type, $sign, $coerce) = @_; + my $token = readXToken($self); return unless defined $token; - my $defn = $STATE->lookupDefinition($token); - if ((defined $defn) && ($defn->isRegister eq $type)) { + my ($defn, $rtype, $coercer); + if (($defn = $STATE->lookupDefinition($token)) + && ($rtype = $defn->isRegister) # Got a register? + && (($rtype eq $type) || ($coerce && ($coercer = $RegisterCoercionTypes{$type}{$rtype})))) { + $sign = +1 unless defined $sign; local $LaTeXML::CURRENT_TOKEN = $token; my $parms = $$defn{parameters}; - return $defn->valueOf(($parms ? $parms->readArguments($self) : ())); } + my $value = $defn->valueOf(($parms ? $parms->readArguments($self) : ())); + if ($type eq $rtype) { + return ($sign < 0 ? $value->negate : $value); } + else { + return &$coercer($sign * $value->valueOf); } } else { unshift(@{ $$self{pushback} }, $token); # Unread return; } } # Apparent behaviour of a token value (ie \toks#=) +# Expand except within braces? sub readTokensValue { my ($self) = @_; - my $token = $self->readNonSpace; + my $token = readNonSpace($self); if (!defined $token) { return; } elsif ($$token[1] == CC_BEGIN) { # Inline ->getCatcode! - return $self->readBalanced; } + return readBalanced($self); } elsif (my $defn = $STATE->lookupDefinition($token)) { if ($defn->isRegister eq 'Tokens') { my $parms = $$defn{parameters}; return $defn->valueOf(($parms ? $parms->readArguments($self) : ())); } elsif ($defn->isExpandable) { if (my $x = $defn->invoke($self)) { - $self->unread($x->unlist); } - return $self->readTokensValue; } - elsif (Equals($token, T_CS('\csname'))) { - my $cstoken = $defn->invoke($self); - $self->unread($cstoken->unlist); - return $self->readToken; } + unread($self, $x->unlist); } + return readTokensValue($self); } else { return $token; } } # ? else { @@ -762,9 +787,9 @@ sub readTokensValue { sub readOptionalSigns { my ($self) = @_; my ($sign, $t) = ("+1", ''); - while (defined($t = $self->readXToken) - && (($t->getString eq '+') || ($t->getString eq '-') || Equals($t, T_SPACE))) { - $sign = -$sign if ($t->getString eq '-'); } + while (defined($t = readXToken($self)) + && (($$t[0] eq '+') || ($$t[0] eq '-') || $t->defined_as(T_SPACE))) { + $sign = -$sign if ($$t[0] eq '-'); } unshift(@{ $$self{pushback} }, $t) if $t; # Unread return $sign; } @@ -773,9 +798,9 @@ sub readDigits { my ($self, $range, $skip) = @_; my $string = ''; my ($token, $digit); - while (($token = $self->readXToken) && (($digit = $token->toString) =~ /^[$range]$/)) { + while (($token = readXToken($self)) && (($digit = $$token[0]) =~ /^[$range]$/)) { $string .= $digit; } - unshift(@{ $$self{pushback} }, $token) if $token && !($skip && Equals($token, T_SPACE)); #Inline + unshift(@{ $$self{pushback} }, $token) if $token && !($skip && $token->defined_as(T_SPACE)); #Inline return $string; } # = | @@ -783,17 +808,17 @@ sub readDigits { # Return a number (perl number) sub readFactor { my ($self) = @_; - my $string = $self->readDigits('0-9'); - my $token = $self->readXToken; - if ($token && $token->getString =~ /^[\.\,]$/) { - $string .= '.' . $self->readDigits('0-9'); - $token = $self->readXToken; } + my $string = readDigits($self, '0-9'); + my $token = readXToken($self); + if ($token && $$token[0] =~ /^[\.\,]$/) { + $string .= '.' . readDigits($self, '0-9'); + $token = readXToken($self); } if (length($string) > 0) { unshift(@{ $$self{pushback} }, $token) if $token && $$token[1] != CC_SPACE; # Inline ->getCatcode, unread return $string; } else { unshift(@{ $$self{pushback} }, $token); # Unread - my $n = $self->readNormalInteger; + my $n = readNormalInteger($self); return (defined $n ? $n->valueOf : undef); } } #====================================================================== @@ -805,15 +830,14 @@ sub readFactor { sub readNumber { my ($self) = @_; - my $s = $self->readOptionalSigns; - if (defined(my $n = $self->readNormalInteger)) { return ($s < 0 ? $n->negate : $n); } - elsif (defined($n = $self->readInternalDimension)) { return Number($s * $n->valueOf); } - elsif (defined($n = $self->readInternalGlue)) { return Number($s * $n->valueOf); } + my $s = readOptionalSigns($self); + if (defined(my $n = readNormalInteger($self))) { return ($s < 0 ? $n->negate : $n); } + elsif (defined($n = readRegisterValue($self, 'Number', $s, 1))) { return $n; } else { - my $next = $self->readToken(); + my $next = readToken($self); unshift(@{ $$self{pushback} }, $next); # Unread Warn('expected', '', $self, "Missing number, treated as zero", - "while processing " . ToString($LaTeXML::CURRENT_TOKEN), $self->showUnexpected); + "while processing " . ToString($LaTeXML::CURRENT_TOKEN), showUnexpected($self)); return Number(0); } } # = | @@ -822,28 +846,24 @@ sub readNumber { # Return a Number or undef sub readNormalInteger { my ($self) = @_; - my $token = $self->readXToken; # expand more + my $token = readXToken($self); # expand more if (!defined $token) { return; } - elsif (($$token[1] == CC_OTHER) && ($token->toString =~ /^[0-9]$/)) { # Read decimal literal - return Number(int($token->getString . $self->readDigits('0-9', 1))); } - elsif ($token->equals(T_OTHER("'"))) { # Read Octal literal - return Number(oct($self->readDigits('0-7', 1))); } - elsif ($token->equals(T_OTHER("\""))) { # Read Hex literal - return Number(hex($self->readDigits('0-9A-F', 1))); } - elsif ($token->equals(T_OTHER("`"))) { # Read Charcode - my $next = $self->readToken; - my $s = ($next && $next->toString) || ''; + elsif (($$token[1] == CC_OTHER) && ($$token[0] =~ /^[0-9]$/)) { # Read decimal literal + return Number(int($$token[0] . readDigits($self, '0-9', 1))); } + elsif ($token->equals(T_OTHER("'"))) { # Read Octal literal + return Number(oct(readDigits($self, '0-7', 1))); } + elsif ($token->equals(T_OTHER("\""))) { # Read Hex literal + return Number(hex(readDigits($self, '0-9A-F', 1))); } + elsif ($token->equals(T_OTHER("`"))) { # Read Charcode + my $next = readToken($self); + my $s = ($next && $$next[0]) || ''; $s =~ s/^\\//; - $self->skip1Space(1); + skip1Space($self, 1); return Number(ord($s)); } # Only a character token!!! NOT expanded!!!! else { unshift(@{ $$self{pushback} }, $token); # Unread - return $self->readInternalInteger; } } - -sub readInternalInteger { - my ($self) = @_; - return $self->readRegisterValue('Number'); } + return readRegisterValue($self, 'Number'); } } #====================================================================== # Float, a floating point number. @@ -851,19 +871,19 @@ sub readInternalInteger { # This is NOT part of TeX, but is convenient. sub readFloat { my ($self) = @_; - my $s = $self->readOptionalSigns; - my $string = $self->readDigits('0-9'); - my $token = $self->readXToken; - if ($token && $token->getString =~ /^[\.]$/) { - $string .= '.' . $self->readDigits('0-9'); - $token = $self->readXToken; } + my $s = readOptionalSigns($self); + my $string = readDigits($self, '0-9'); + my $token = readXToken($self); + if ($token && $$token[0] =~ /^[\.]$/) { + $string .= '.' . readDigits($self, '0-9'); + $token = readXToken($self); } my $n; if (length($string) > 0) { unshift(@{ $$self{pushback} }, $token) if $token && $$token[1] != CC_SPACE; # Inline ->getCatcode, unread $n = $string; } else { unshift(@{ $$self{pushback} }, $token) if $token; # Unread - $n = $self->readNormalInteger; + $n = readNormalInteger($self); $n = $n->valueOf if defined $n; } return (defined $n ? Float($s * $n) : undef); } @@ -875,19 +895,17 @@ sub readFloat { # = sub readDimension { my ($self) = @_; - my $s = $self->readOptionalSigns; - if (defined(my $d = $self->readInternalDimension)) { - return ($s < 0 ? $d->negate : $d); } - elsif (defined($d = $self->readInternalGlue)) { - return Dimension($s * $d->valueOf); } - elsif (defined($d = $self->readFactor)) { - my $unit = $self->readUnit; + my $s = readOptionalSigns($self); + if (defined(my $d = readRegisterValue($self, 'Dimension', $s, 1))) { + return $d; } + elsif (defined($d = readFactor($self))) { + my $unit = readUnit($self); if (!defined $unit) { # but leave undefined (effectively not rescaled) Warn('expected', '', $self, "Illegal unit of measure (pt inserted)."); } return Dimension(fixpoint($s * $d, $unit)); } else { Warn('expected', '', $self, "Missing number (Dimension), treated as zero.", - "while processing " . ToString($LaTeXML::CURRENT_TOKEN), $self->showUnexpected); + "while processing " . ToString($LaTeXML::CURRENT_TOKEN), showUnexpected($self)); return Dimension(0); } } # = @@ -899,30 +917,21 @@ sub readDimension { # Read a unit, returning the equivalent number of scaled points, sub readUnit { my ($self) = @_; - if (defined(my $u = $self->readKeyword('ex', 'em'))) { - $self->skip1Space(1); + if (defined(my $u = readKeyword($self, 'ex', 'em'))) { + skip1Space($self, 1); return $STATE->convertUnit($u); } - elsif (defined($u = $self->readInternalInteger)) { + elsif (defined($u = readRegisterValue($self, 'Number', +1, 1))) { return $u->valueOf; } # These are coerced to number=>sp - elsif (defined($u = $self->readInternalDimension)) { - return $u->valueOf; } - elsif (defined($u = $self->readInternalGlue)) { - return $u->valueOf; } else { - $self->readKeyword('true'); # But ignore, we're not bothering with mag... + readKeyword($self, 'true'); # But ignore, we're not bothering with mag... my $units = $STATE->lookupValue('UNITS'); - $u = $self->readKeyword(keys %$units); + $u = readKeyword($self, keys %$units); if ($u) { - $self->skip1Space(1); + skip1Space($self, 1); return $STATE->convertUnit($u); } else { return; } } } -# Return a dimension value or undef -sub readInternalDimension { - my ($self) = @_; - return $self->readRegisterValue('Dimension'); } - #====================================================================== # Mu Dimensions #====================================================================== @@ -933,24 +942,24 @@ sub readInternalDimension { # = sub readMuDimension { my ($self) = @_; - my $s = $self->readOptionalSigns; - if (defined(my $m = $self->readFactor)) { - my $munit = $self->readMuUnit; + my $s = readOptionalSigns($self); + if (defined(my $m = readFactor($self))) { + my $munit = readMuUnit($self); if (!defined $munit) { Warn('expected', '', $self, "Illegal unit of measure (mu inserted)."); } return MuDimension(fixpoint($s * $m, $munit)); } - elsif (defined($m = $self->readInternalMuGlue)) { - return MuDimension($s * $m->valueOf); } + elsif (defined($m = readRegisterValue($self, 'MuDimension', $s, 1))) { + return $m; } else { Warn('expected', '', $self, "Expecting mudimen; assuming 0"); return MuDimension(0); } } sub readMuUnit { my ($self) = @_; - if (my $m = $self->readKeyword('mu')) { - $self->skip1Space(1); + if (my $m = readKeyword($self, 'mu')) { + skip1Space($self, 1); return $UNITY; } # effectively, scaled mu - elsif ($m = $self->readInternalMuGlue) { + elsif ($m = readRegisterValue($self, 'MuGlue')) { return $m->valueOf; } else { return; } } @@ -963,45 +972,40 @@ sub readMuUnit { # = minus | minus | sub readGlue { my ($self) = @_; - my $s = $self->readOptionalSigns; + my $s = readOptionalSigns($self); my $n; - if (defined($n = $self->readInternalGlue)) { - return ($s < 0 ? $n->negate : $n); } + if (defined($n = readRegisterValue($self, 'Glue', $s))) { + return $n; } else { - my $d = $self->readDimension; + my $d = readDimension($self); if (!$d) { Warn('expected', '', $self, "Missing number (Glue), treated as zero.", - "while processing " . ToString($LaTeXML::CURRENT_TOKEN), $self->showUnexpected); + "while processing " . ToString($LaTeXML::CURRENT_TOKEN), showUnexpected($self)); return Glue(0); } $d = $d->negate if $s < 0; my ($r1, $f1, $r2, $f2); - ($r1, $f1) = $self->readRubber if $self->readKeyword('plus'); - ($r2, $f2) = $self->readRubber if $self->readKeyword('minus'); + ($r1, $f1) = readRubber($self) if readKeyword($self, 'plus'); + ($r2, $f2) = readRubber($self) if readKeyword($self, 'minus'); return Glue($d->valueOf, $r1, $f1, $r2, $f2); } } my %FILLS = (fil => 1, fill => 2, filll => 3); # [CONSTANT] sub readRubber { my ($self, $mu) = @_; - my $s = $self->readOptionalSigns; - my $f = $self->readFactor; + my $s = readOptionalSigns($self); + my $f = readFactor($self); if (!defined $f) { - $f = ($mu ? $self->readMuDimension : $self->readDimension); + $f = ($mu ? readMuDimension($self) : readDimension($self)); return ($f->valueOf * $s, 0); } - elsif (defined(my $fil = $self->readKeyword('filll', 'fill', 'fil'))) { + elsif (defined(my $fil = readKeyword($self, 'filll', 'fill', 'fil'))) { return (fixpoint($s * $f), $FILLS{$fil}); } else { - my $u = ($mu ? $self->readMuUnit : $self->readUnit); + my $u = ($mu ? readMuUnit($self) : readUnit($self)); if (!defined $u) { Warn('expected', '', $self, "Illegal unit of measure (" . ($mu ? 'mu' : 'pt') . " inserted)."); } return (fixpoint($s * $f, $u), 0); } } -# Return a glue value or undef. -sub readInternalGlue { - my ($self) = @_; - return $self->readRegisterValue('Glue'); } - #====================================================================== # Mu Glue #====================================================================== @@ -1010,32 +1014,52 @@ sub readInternalGlue { # = minus | minus | sub readMuGlue { my ($self) = @_; - my $s = $self->readOptionalSigns; + my $s = readOptionalSigns($self); my $n; - if (defined($n = $self->readInternalMuGlue)) { + if (defined($n = readRegisterValue($self, 'MuGlue'))) { return ($s < 0 ? $n->negate : $n); } else { - my $d = $self->readMuDimension; + my $d = readMuDimension($self); if (!$d) { Warn('expected', '', $self, "Missing number (MuGlue), treated as zero.", - "while processing " . ToString($LaTeXML::CURRENT_TOKEN), $self->showUnexpected); + "while processing " . ToString($LaTeXML::CURRENT_TOKEN), showUnexpected($self)); return MuGlue(0); } $d = $d->negate if $s < 0; my ($r1, $f1, $r2, $f2); - ($r1, $f1) = $self->readRubber(1) if $self->readKeyword('plus'); - ($r2, $f2) = $self->readRubber(1) if $self->readKeyword('minus'); + ($r1, $f1) = readRubber($self, 1) if readKeyword($self, 'plus'); + ($r2, $f2) = readRubber($self, 1) if readKeyword($self, 'minus'); return MuGlue($d->valueOf, $r1, $f1, $r2, $f2); } } -# Return a muglue value or undef. -sub readInternalMuGlue { - my ($self) = @_; - return $self->readRegisterValue('MuGlue'); } - #====================================================================== # See pp 272-275 for lists of the various registers. # These are implemented in Primitive.pm #********************************************************************** +# Deprecated +sub readInternalInteger { + my ($self) = @_; + Deprecated('readInternalInteger', '0.8.8', + "Please use \$gullet->readRegisterValue('Number')"); + return readRegisterValue($self, 'Number'); } + +sub readInternalDimension { + my ($self) = @_; + Deprecated('readInternalDimension', '0.8.8', + "Please use \$gullet->readRegisterValue('Dimension')"); + return readRegisterValue($self, 'Dimension'); } + +sub readInternalGlue { + my ($self) = @_; + Deprecated('readInternalGlue', '0.8.8', + "Please use \$gullet->readRegisterValue('Glue')"); + return readRegisterValue($self, 'Glue'); } + +sub readInternalMuGlue { + my ($self) = @_; + Deprecated('readInternalMuGlue', '0.8.8', + "Please use \$gullet->readRegisterValue('MuGlue')"); + return readRegisterValue($self, 'MuGlue'); } + 1; __END__ diff --git a/lib/LaTeXML/Core/Mouth.pm b/lib/LaTeXML/Core/Mouth.pm index 17d0c19cf..4264a62bf 100644 --- a/lib/LaTeXML/Core/Mouth.pm +++ b/lib/LaTeXML/Core/Mouth.pm @@ -24,6 +24,9 @@ use base qw(LaTeXML::Common::Object); our $READLINE_PROGRESS_QUANTUM = 25; +# NOTE: that the following methods are (potentially) polymorphic w/Mouty types; Use $self->method +# new, initialize, finish, hasMoreInput, getNextLine, getLocator, getSource, stringify + # Factory method; # Create an appropriate Mouth # options are @@ -60,7 +63,7 @@ sub new { at_letter => ($options{at_letter} ? 1 : 0), notes => ($options{notes} ? 1 : 0), }, $class; - $self->openString($string); + openString($self, $string); $self->initialize; return $self; } @@ -147,7 +150,7 @@ sub getNextLine { sub hasMoreInput { my ($self) = @_; - return !$self->isEOL || scalar(@{ $$self{buffer} }); } + return !isEOL($self) || scalar(@{ $$self{buffer} }); } # Get the next character & it's catcode from the input, # handling TeX's "^^" encoding. @@ -353,7 +356,7 @@ sub readToken { sub readTokens { my ($self) = @_; my @tokens = (); - while (defined(my $token = $self->readToken())) { + while (defined(my $token = readToken($self))) { push(@tokens, $token); } while (@tokens && $tokens[-1]->getCatcode == CC_SPACE) { # Remove trailing space pop(@tokens); } diff --git a/lib/LaTeXML/Core/State.pm b/lib/LaTeXML/Core/State.pm index 3d8f7c496..20d6fcf98 100644 --- a/lib/LaTeXML/Core/State.pm +++ b/lib/LaTeXML/Core/State.pm @@ -377,7 +377,7 @@ sub lookupDefinition { return unless $token; my $defn; my $entry; - # my $inmath = $self->lookupValue('IN_MATH'); + # my $inmath = lookupValue($self, 'IN_MATH'); my $cc = $$token[1]; my $lookupname = ($CATCODE_ACTIVE_OR_CS[$cc] @@ -416,7 +416,7 @@ sub lookupExpandable { && ($defn = $$entry[0]) # Can only be a token or definition; we want defns! && ((ref $defn) ne 'LaTeXML::Core::Token') - && $$defn{isExpandable} + && $defn->isExpandable && ($toplevel || !$$defn{isProtected})) { # is this the right logic here? don't expand unless digesting? return $defn; } return; } @@ -429,14 +429,14 @@ sub isDontExpandable { return unless $token; my $defn; my $entry; - # my $inmath = $self->lookupValue('IN_MATH'); + # my $inmath = lookupValue($self, 'IN_MATH'); my $cc = $$token[1]; if ($CATCODE_ACTIVE_OR_CS[$cc]) { my $lookupname = $$token[0]; if ($lookupname && ($entry = $$self{meaning}{$lookupname}) && ($defn = $$entry[0])) { - return ((ref $defn) ne 'LaTeXML::Core::Token') && $$defn{isExpandable}; } + return ((ref $defn) ne 'LaTeXML::Core::Token') && $defn->isExpandable; } else { return 1; } } return; } @@ -451,13 +451,13 @@ sub lookupDigestableDefinition { return unless $token; my $defn; my $entry; - # my $inmath = $self->lookupValue('IN_MATH'); + # my $inmath = lookupValue($self, 'IN_MATH'); my $cc = $$token[1]; my $name = $$token[0]; my $lookupname = (($CATCODE_ACTIVE_OR_CS[$cc] - || ($CATCODE_LETTER_OR_OTHER[$cc] && $self->lookupValue('IN_MATH') - && (($self->lookupMathcode($name) || 0) == 0x8000))) + || ($CATCODE_LETTER_OR_OTHER[$cc] && lookupValue($self, 'IN_MATH') + && ((lookupMathcode($self, $name) || 0) == 0x8000))) ? $name : $CATCODE_EXECUTABLE_PRIMITIVE_NAME[$cc]); if ($lookupname && ($entry = $$self{meaning}{$lookupname}) @@ -481,12 +481,12 @@ sub installDefinition { # my $cs = $definition->getCS->getCSName; my $token = $definition->getCS; my $cs = ($LaTeXML::Core::Token::CATCODE_PRIMITIVE_NAME[$$token[1]] || $$token[0]); - if ($self->lookupValue("$cs:locked") && !$LaTeXML::Core::State::UNLOCKED) { - my $s = $self->getStomach->getGullet->getSource; + if (lookupValue($self, "$cs:locked") && !$LaTeXML::Core::State::UNLOCKED) { + my $s = $$self{stomach}->getGullet->getSource; # report if the redefinition seems to come from document source if (((!defined($s)) || ($s =~ /\.(tex|bib)$/)) && ($s !~ /\.code\.tex$/)) { - Info('ignore', $cs, $self->getStomach, "Ignoring redefinition of $cs"); } + Info('ignore', $cs, $$self{stomach}, "Ignoring redefinition of $cs"); } return; } assign_internal($self, 'meaning', $cs => $definition, $scope); return; } @@ -507,21 +507,21 @@ sub installDefinition { sub generateErrorStub { my ($self, $caller, $token, $params) = @_; my $cs = $token->getCSName; - $self->noteStatus(undefined => $cs); + noteStatus($self, undefined => $cs); # To minimize chatter, go ahead and define it... if ($cs =~ /^\\if(.*)$/) { # Apparently an \ifsomething ??? my $name = $1; Error('undefined', $token, $caller, "The token " . $token->stringify . " is not defined.", "Defining it now as with \\newif"); - $self->installDefinition(LaTeXML::Core::Definition::Expandable->new( + installDefinition($self, LaTeXML::Core::Definition::Expandable->new( T_CS('\\' . $name . 'true'), undef, '\let' . $cs . '\iftrue')); - $self->installDefinition(LaTeXML::Core::Definition::Expandable->new( + installDefinition($self, LaTeXML::Core::Definition::Expandable->new( T_CS('\\' . $name . 'false'), undef, '\let' . $cs . '\iffalse')); LaTeXML::Package::Let($token, T_CS('\iffalse')); } else { Error('undefined', $token, $caller, "The token " . $token->stringify . " is not defined.", "Defining it now as "); - $self->installDefinition(LaTeXML::Core::Definition::Constructor->new($token, $params, + installDefinition($self, LaTeXML::Core::Definition::Constructor->new($token, $params, sub { $_[0]->makeError('undefined', $cs); }, sizer => 'X'), 'global'); } @@ -538,7 +538,7 @@ sub pushFrame { sub popFrame { my ($self) = @_; if ($$self{undo}[0]{_FRAME_LOCK_}) { - Error('unexpected', '', $self->getStomach, + Error('unexpected', '', $$self{stomach}, "Attempt to pop last locked stack frame"); } else { my $undo = shift(@{ $$self{undo} }); @@ -562,19 +562,19 @@ sub getFrameDepth { sub beginSemiverbatim { my ($self, @extraspecials) = @_; # Is this a good/safe enough shorthand, or should we really be doing beginMode? - $self->pushFrame; - $self->assignValue(MODE => 'text'); - $self->assignValue(IN_MATH => 0); - map { $self->assignCatcode($_ => CC_OTHER, 'local') } - @{ $self->lookupValue('SPECIALS') }, @extraspecials; - $self->assignMathcode('\'' => 0x8000, 'local'); + pushFrame($self); + assignValue($self, MODE => 'text'); + assignValue($self, IN_MATH => 0); + map { assignCatcode($self, $_ => CC_OTHER, 'local') } + @{ lookupValue($self, 'SPECIALS') }, @extraspecials; + assignMathcode($self, '\'' => 0x8000, 'local'); # try to stay as ASCII as possible - $self->assignValue(font => $self->lookupValue('font')->merge(encoding => 'ASCII'), 'local'); + assignValue($self, font => lookupValue($self, 'font')->merge(encoding => 'ASCII'), 'local'); return; } sub endSemiverbatim { my ($self) = @_; - $self->popFrame; + popFrame($self); return; } #====================================================================== @@ -597,7 +597,7 @@ sub pushDaemonFrame { unshift(@{ $$hash{$key} }, daemon_copy($value)); } } } } # And push new binding. # Record the contents of LaTeXML::Package::Pool as preloaded my $pool_preloaded_hash = { map { $_ => 1 } keys %LaTeXML::Package::Pool:: }; - $self->assignValue('_PRELOADED_POOL_', $pool_preloaded_hash, 'global'); + assignValue($self, '_PRELOADED_POOL_', $pool_preloaded_hash, 'global'); # Now mark the top frame as LOCKED!!! $$frame{_FRAME_LOCK_} = 1; return; } @@ -615,22 +615,22 @@ sub daemon_copy { sub popDaemonFrame { my ($self) = @_; while (!$$self{undo}[0]{_FRAME_LOCK_}) { - $self->popFrame; } + popFrame($self); } if (scalar(@{ $$self{undo} } > 1)) { delete $$self{undo}[0]{_FRAME_LOCK_}; # Any non-preloaded Pool routines should be wiped away, as we # might want to reuse the Pool namespaces for the next run. - my $pool_preloaded_hash = $self->lookupValue('_PRELOADED_POOL_'); - $self->assignValue('_PRELOADED_POOL_', undef, 'global'); + my $pool_preloaded_hash = lookupValue($self, '_PRELOADED_POOL_'); + assignValue($self, '_PRELOADED_POOL_', undef, 'global'); foreach my $subname (keys %LaTeXML::Package::Pool::) { unless (exists $$pool_preloaded_hash{$subname}) { undef $LaTeXML::Package::Pool::{$subname}; delete $LaTeXML::Package::Pool::{$subname}; } } # Finally, pop the frame - $self->popFrame; } + popFrame($self); } else { - Fatal('unexpected', '', $self->getStomach, + Fatal('unexpected', '', $$self{stomach}, "Daemon Attempt to pop last stack frame"); } return; } @@ -684,7 +684,7 @@ sub deactivateScope { shift(@{ $$self{$table}{$key} }); $$frame{$table}{$key}--; } else { - Warn('internal', $key, $self->getStomach, + Warn('internal', $key, $$self{stomach}, "Unassigning wrong value for $key from table $table in deactivateScope", "value is $value but stack is " . join(', ', @{ $$self{$table}{$key} })); } } } } return; } @@ -707,11 +707,11 @@ sub convertUnit { $unit = lc($unit); # Put here since it could concievably evolve to depend on the current font. # Eventually try to track font size? - if ($unit eq 'em') { return $self->lookupValue('font')->getEMWidth; } - elsif ($unit eq 'ex') { return $self->lookupValue('font')->getEXHeight; } - elsif ($unit eq 'mu') { return $self->lookupValue('font')->getMUWidth; } + if ($unit eq 'em') { return lookupValue($self, 'font')->getEMWidth; } + elsif ($unit eq 'ex') { return lookupValue($self, 'font')->getEXHeight; } + elsif ($unit eq 'mu') { return lookupValue($self, 'font')->getMUWidth; } else { - my $units = $self->lookupValue('UNITS'); + my $units = lookupValue($self, 'UNITS'); my $sp = $$units{$unit}; if (!$sp) { Warn('expected', '', undef, "Illegal unit of measure '$unit', assuming pt."); diff --git a/lib/LaTeXML/Core/Stomach.pm b/lib/LaTeXML/Core/Stomach.pm index 2ed763606..ad35520c6 100644 --- a/lib/LaTeXML/Core/Stomach.pm +++ b/lib/LaTeXML/Core/Stomach.pm @@ -89,7 +89,7 @@ sub getScriptLevel { sub digestNextBody { my ($self, $terminal) = @_; no warnings 'recursion'; - my $startloc = $self->getLocator; + my $startloc = getLocator($self); my $initdepth = scalar(@{ $$self{boxing} }); my $token; local @LaTeXML::LIST = (); @@ -104,7 +104,7 @@ sub digestNextBody { # So if we already have some digested boxes available, return them here. $$self{gullet}->unread($token); return @LaTeXML::LIST; } - my @r = $self->invokeToken($token); + my @r = invokeToken($self, $token); push(@LaTeXML::LIST, @r); push(@aug, $token, @r); last if $terminal and Equals($token, $terminal); @@ -133,7 +133,7 @@ sub digest { local @LaTeXML::LIST = (); while (defined(my $token = $$self{gullet}->getPendingComment || $$self{gullet}->readXToken(1))) { - push(@LaTeXML::LIST, $self->invokeToken($token)); + push(@LaTeXML::LIST, invokeToken($self, $token)); last if $initdepth > scalar(@{ $$self{boxing} }); } # if we've closed the initial mode. List(@LaTeXML::LIST, mode => ($ismath ? 'math' : 'text')); }); } @@ -168,13 +168,13 @@ INVOKE: my $meaning = $STATE->lookupDigestableDefinition($token); if (!$meaning) { - @result = $self->invokeToken_undefined($token); } + @result = invokeToken_undefined($self, $token); } elsif ($meaning->isaToken) { # Common case my $cc = $meaning->getCatcode; if ($cc == CC_CS) { - @result = $self->invokeToken_undefined($token); } + @result = invokeToken_undefined($self, $token); } elsif ($CATCODE_ABSORBABLE[$cc]) { - @result = $self->invokeToken_simple($token, $meaning); } + @result = invokeToken_simple($self, $token, $meaning); } else { # Special error guard for the align char "&": # Locally deactivate to avoid a flurry of errors in the same table. @@ -185,7 +185,7 @@ INVOKE: if Equals($token, T_ALIGN); Error('misdefined', $token, $self, "The token " . Stringify($token) . " should never reach Stomach!"); - @result = $self->invokeToken_simple($token, $meaning); } } + @result = invokeToken_simple($self, $token, $meaning); } } # A math-active character will (typically) be a macro, # but it isn't expanded in the gullet, but later when digesting, in math mode (? I think) elsif ($meaning->isExpandable) { @@ -221,7 +221,7 @@ sub makeMisdefinedError { sub invokeToken_undefined { my ($self, $token) = @_; $STATE->generateErrorStub($self, $token); - $self->getGullet->unread($token); # Retry + $$self{gullet}->unread($token); # Retry return; } sub invokeToken_simple { @@ -233,7 +233,7 @@ sub invokeToken_simple { if ($STATE->lookupValue('IN_MATH')) { # (but in Preamble, OK ?) return (); } else { - return Box($meaning->toString, $font, $self->getGullet->getLocator, $meaning); } } + return Box($meaning->toString, $font, $$self{gullet}->getLocator, $meaning); } } elsif ($cc == CC_COMMENT) { # Note: Comments need char decoding as well! my $comment = LaTeXML::Package::FontDecodeString($meaning->toString, undef, 1); # However, spaces normally would have be digested away as positioning... @@ -269,7 +269,7 @@ sub pushStackFrame { $STATE->assignValue(afterAssignment => undef, 'local'); # ALWAYS bind this! $STATE->assignValue(groupNonBoxing => $nobox, 'local'); # ALWAYS bind this! $STATE->assignValue(groupInitiator => $LaTeXML::CURRENT_TOKEN, 'local'); - $STATE->assignValue(groupInitiatorLocator => $self->getLocator, 'local'); + $STATE->assignValue(groupInitiatorLocator => getLocator($self), 'local'); push(@{ $$self{boxing} }, $LaTeXML::CURRENT_TOKEN) unless $nobox; # For begingroup/endgroup return; } @@ -324,7 +324,7 @@ sub egroup { if ( ##$STATE->isValueBound('MODE', 0) || # Last stack frame was a mode switch!?!?! $STATE->lookupValue('groupNonBoxing')) { # or group was opened with \begingroup Error('unexpected', $LaTeXML::CURRENT_TOKEN, $self, "Attempt to close boxing group", - $self->currentFrameMessage); } + currentFrameMessage($self)); } else { # Don't pop if there's an error; maybe we'll recover? popStackFrame($self, 0); } $LaTeXML::ALIGN_STATE--; @@ -340,7 +340,7 @@ sub endgroup { if ( ##$STATE->isValueBound('MODE', 0) || # Last stack frame was a mode switch!?!?! !$STATE->lookupValue('groupNonBoxing')) { # or group was opened with \bgroup Error('unexpected', $LaTeXML::CURRENT_TOKEN, $self, "Attempt to close non-boxing group", - $self->currentFrameMessage); } + currentFrameMessage($self)); } else { # Don't pop if there's an error; maybe we'll recover? popStackFrame($self, 1); } return; } @@ -379,8 +379,8 @@ sub setMode { sub beginMode { my ($self, $mode) = @_; - $self->pushStackFrame; # Effectively bgroup - $self->setMode($mode); + pushStackFrame($self); # Effectively bgroup + setMode($self, $mode); return; } sub endMode { @@ -388,9 +388,9 @@ sub endMode { if ((!$STATE->isValueBound('MODE', 0)) # Last stack frame was NOT a mode switch!?!?! || ($STATE->lookupValue('MODE') ne $mode)) { # Or was a mode switch to a different mode Error('unexpected', $LaTeXML::CURRENT_TOKEN, $self, "Attempt to end mode $mode", - $self->currentFrameMessage); } + currentFrameMessage($self)); } else { # Don't pop if there's an error; maybe we'll recover? - $self->popStackFrame; } # Effectively egroup. + popStackFrame($self); } # Effectively egroup. return; } #********************************************************************** diff --git a/lib/LaTeXML/Core/Token.pm b/lib/LaTeXML/Core/Token.pm index 7dbb99970..6ebd6f1f1 100644 --- a/lib/LaTeXML/Core/Token.pm +++ b/lib/LaTeXML/Core/Token.pm @@ -19,6 +19,7 @@ package LaTeXML::Core::Token; use strict; use warnings; use LaTeXML::Global; +use LaTeXML::Core::State; use LaTeXML::Common::Error; use LaTeXML::Common::Object; use base qw(LaTeXML::Common::Object); @@ -325,6 +326,21 @@ sub equals { && ($$a[1] == $$b[1]) && (($$a[1] == CC_SPACE) || ($$a[0] eq $$b[0])); } +# Check whether $self is defined_as $token, +# that is, equal to $token, or \let to $token. +# $token is is presumed to be some "constant", explicit token, +# such as T_SPACE, T_CS('\endcsname'). +sub defined_as { + my ($self, $token) = @_; + return unless $token; + my $cc = $$self[1]; + my $occ = $$token[1]; + return 1 if ($cc == $occ) && (($occ == CC_SPACE) || ($$self[0] eq $$token[0])); + if (my $defn = (($cc == CC_CS) || ($cc == CC_ACTIVE)) && $STATE->lookupMeaning($self)) { + my $letto = ((ref $defn eq 'LaTeXML::Core::Token') ? $defn : $defn->getCS); + return 1 if ($$letto[1] == $occ) && (($occ == CC_SPACE) || ($$letto[0] eq $$token[0])); } + return; } + my @CONTROLNAME = ( #[CONSTANT] qw( NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US)); diff --git a/lib/LaTeXML/Core/Tokens.pm b/lib/LaTeXML/Core/Tokens.pm index 2c2d5f053..053309fa1 100644 --- a/lib/LaTeXML/Core/Tokens.pm +++ b/lib/LaTeXML/Core/Tokens.pm @@ -101,8 +101,7 @@ sub isBalanced { # Using inline accessors on those assumptions sub substituteParameters { my ($self, @args) = @_; - my @in = @{$self}; # ->unlist - return $self unless grep { $$_[1] == CC_ARG; } @in; + my @in = @{$self}; # ->unlist my @result = (); while (my $token = shift(@in)) { if ($$token[1] != CC_ARG) { # Non-match; copy it diff --git a/lib/LaTeXML/Package/TeX.pool.ltxml b/lib/LaTeXML/Package/TeX.pool.ltxml index 67fbad11e..9bc2497bb 100644 --- a/lib/LaTeXML/Package/TeX.pool.ltxml +++ b/lib/LaTeXML/Package/TeX.pool.ltxml @@ -858,7 +858,7 @@ our @CATCODE_MEANING = ( DefMacro('\meaning Token', sub { my ($gullet, $tok) = @_; my $meaning = 'undefined'; - if (my $definition = (Equals($tok, T_ALIGN) ? $tok : LookupMeaning($tok))) { + if (my $definition = ($tok->defined_as(T_ALIGN) ? $tok : LookupMeaning($tok))) { my $type = ref $definition; $type =~ s/^LaTeXML:://; # Pre-step: We can't extract the bodies of definitions which are defined via Perl subroutines. @@ -930,25 +930,7 @@ DefMacro('\meaning Token', sub { $meaning =~ s/\s/ /g; return Explode($meaning); }); -DefParameterType('CSName', sub { - my ($gullet) = @_; - my $token; - # Deyan Ginev & Dennis Mueller were right! Or partly so. - # TeX does NOT store the csname with the leading `\`, BUT stores active chars with a flag - # However, so long as the Mouth's CS and \string properly respect \escapechar, all's well! - my $cs = '\\'; - # keep newlines from having \n inside! - while (($token = $gullet->readXToken(1)) && (!Equals($token, T_CS('\endcsname')))) { - my $cc = $token->getCatcode; - if ($cc == CC_CS) { - if (defined $STATE->lookupDefinition($token)) { - Error('unexpected', $token, $gullet, -"The control sequence " . ToString($token) . " should not appear between \\csname and \\endcsname"); } - else { - Error('undefined', $token, $gullet, "The token " . Stringify($token) . " is not defined"); } } - # Keep newlines from having \n! - $cs .= ($cc == CC_SPACE ? ' ' : $token->toString); } - T_CS($cs); }); +DefParameterType('CSName', sub { $_[0]->readCSName; }); DefMacro('\csname CSName', sub { my ($gullet, $token) = @_; @@ -981,6 +963,31 @@ DefMacro('\expandafter Token Token', sub { else { ($tok, $xtok); } }); +use constant T_expandafter => T_CS('\expandafter'); +DefMacro('\expandafter Token Token', sub { + no warnings 'recursion'; + my ($gullet, $tok, $xtok) = @_; + my $defn; + my @skipped = ($tok); + while ($xtok->defined_as(T_expandafter)) { + push(@skipped, $gullet->readToken); + $xtok = $gullet->readToken; } + if (defined($defn = $STATE->lookupExpandable($xtok))) { + my @x = (); + { + local $LaTeXML::CURRENT_TOKEN = $xtok; + @x = $defn->invoke($gullet, 1); # Expand $xtok ONCE ONLY! + } + (@skipped, @x); } + elsif (!$STATE->lookupMeaning($xtok)) { + # Undefined token is an error, as expansion is expected. + # BUT The unknown token is NOT consumed, (see TeX B book, item 367) + # since probably in a real TeX run it would have been defined. + $STATE->generateErrorStub($gullet, $xtok); + (@skipped, $xtok); } + else { + (@skipped, $xtok); } }); + # If next token is expandable, prefix it with the internal marker \dont_expand # That token is never defined, explicitly handled in Gullet & should never escape the Gullet DefMacroI('\noexpand', undef, sub { @@ -1979,7 +1986,7 @@ sub readBoxContents { no warnings 'recursion'; my ($gullet, $everybox) = @_; my $t; - while (($t = $gullet->readToken) && !Equals($t, T_BEGIN)) { } # Skip till { or \bgroup + while (($t = $gullet->readToken) && !$t->defined_as(T_BEGIN)) { } # Skip till { or \bgroup # Now, insert some extra tokens, if any, possibly from \afterassignment if (my $token = LookupValue('BeforeNextBox')) { AssignValue(BeforeNextBox => undef, 'global'); @@ -1996,14 +2003,6 @@ DefParameterType('VBoxContents', sub { readBoxContents($_[0], LookupValue('\everyvbox')); }, undigested => 1); # Cause it already is digested! -# DefParameterType('BoxContents',sub { -# my($gullet)=@_; -# my $t; -# while(($t=$gullet->readToken) && !Equals($t,T_BEGIN)){} # Skip till { or \bgroup -# my($contents,@stuff) = $STATE->getStomach->invokeToken(T_BEGIN); -# $contents; }, -# undigested=>1); # Cause it already is digested! - # This re-binds a number of important control sequences to their default text binding. # This is useful within common boxing or footnote macros that can appear within # alignments or special environments that have redefined many of these. @@ -3068,6 +3067,7 @@ DefPrimitiveI('\@@eat@space', undef, sub { #DefMacroI('\@@eat@space',undef,undef); +use constant T_hfil => T_CS('\hfil'); # Yet more special case hacking. Sometimes the order of tokens works for # TeX, but confuses us... In particular the order of $ and \hfil! sub beforeCellUnlist { @@ -3076,7 +3076,7 @@ sub beforeCellUnlist { my @toks = $tokens->unlist; my @new = (); while (my $t = shift(@toks)) { - if (Equals($t, T_MATH) && @toks && Equals($toks[0], T_CS('\hfil'))) { + if ($t->defined_as(T_MATH) && @toks && $toks[0]->defined_as(T_hfil)) { push(@new, shift(@toks)); unshift(@toks, $t); } else { push(@new, $t); } } @@ -3088,7 +3088,7 @@ sub afterCellUnlist { my @toks = $tokens->unlist; my @new = (); while (my $t = pop(@toks)) { - if (Equals($t, T_MATH) && @toks && Equals($toks[-1], T_CS('\hfil'))) { + if ($t->defined_as(T_MATH) && @toks && $toks[-1]->defined_as(T_hfil)) { unshift(@new, pop(@toks)); push(@toks, $t); } else { unshift(@new, $t); } } @@ -3134,7 +3134,7 @@ DefConstructor('\halign BoxSpecification', sub parseHAlignTemplate { my ($gullet, $whatsit) = @_; my $t = $gullet->readNonSpace; - Error('expected', '\bgroup', $gullet, "Missing \\halign box") unless Equals($t, T_BEGIN); + Error('expected', '\bgroup', $gullet, "Missing \\halign box") unless $t->defined_as(T_BEGIN); my $before = 1; # true if we're before a # in current column my @pre = (); my @post = (); @@ -3226,6 +3226,8 @@ sub cRevert { local $LaTeXML::DUAL_BRANCH = 'content'; return Revert($arg); } +use constant T_close_alignment => T_CS('\@close@alignment'); + sub digestAlignmentBody { my ($stomach, $whatsit) = @_; my $gullet = $stomach->getGullet; @@ -3258,15 +3260,15 @@ sub digestAlignmentBody { extractAlignmentColumn($alignment, $cell); $lastwascr = undef; if (!$type && (!$next - || Equals($next, T_END) # End of alignment - || Equals($next, T_CS('\@close@alignment')))) { # End of alignment + || $next->defined_as(T_END) # End of alignment + || $next->defined_as(T_close_alignment))) { # End of alignment $alignment->endRow(); last; } elsif ($type eq 'align') { $alignment->endColumn(); if (!$hidden) { - push(@reversion, $next); # and record the & - push(@creversion, $next); } } # and record the & + push(@reversion, $next); # and record the & + push(@creversion, $next); } } # and record the & elsif ($type eq 'insert') { $alignment->endColumn(); } elsif (($type eq 'cr') || ($type eq 'crcr')) { @@ -3289,6 +3291,12 @@ sub digestAlignmentBody { . "=> " . join(',', map { Stringify($_); } @reversion)) if $LaTeXML::DEBUG{halign}; return; } +use constant T_crcr => T_CS('\crcr'); +use constant T_hidden_crcr => T_CS('\hidden@crcr'); +use constant T_omit => T_CS('\omit'); +use constant T_noalign => T_CS('\noalign'); +use constant T_hidden_noalign => T_CS('\hidden@noalign'); + # Read & digest an alignment column's data, # accommodating the current template and any special cs's # Returns the column's digested boxes, the ending token, and it's alignment type. @@ -3308,13 +3316,13 @@ sub digestAlignmentColumn { if ($token->equals(T_SPACE) # Skip leading space. || $token->equals(T_CS('\par')) # Skip or blank line(?) || ($lastwascr && # Or \crcr following a \cr - (Equals($token, T_CS('\crcr')) || Equals($token, T_CS('\hidden@crcr'))))) { + ($token->defined_as(T_crcr) || $token->defined_as(T_hidden_crcr)))) { } - elsif (Equals($token, T_CS('\omit'))) { # \omit removes template for this column. + elsif ($token->defined_as(T_omit)) { # \omit removes template for this column. Debug("Halign $alignment: OMIT at " . Stringify($token)) if $LaTeXML::DEBUG{halign}; $alignment->startRow() unless $$alignment{in_row}; $alignment->omitNextColumn; } - elsif (Equals($token, T_CS('\noalign'))) { # \puts something in vertical list + elsif ($token->defined_as(T_noalign)) { # \puts something in vertical list Debug("Halign $alignment: noalign at " . Stringify($token)) if $LaTeXML::DEBUG{halign}; $alignment->endRow() if $$alignment{in_row}; $alignment->startColumn(1); @@ -3322,13 +3330,13 @@ sub digestAlignmentColumn { my $r = $stomach->digest($gullet->readArg); $alignment->endRow(); return ($r, T_CS('\cr'), 'cr'), undef; } # Pretend this is a whole row??? - elsif (Equals($token, T_CS('\hidden@noalign'))) { # \puts something in vertical list + elsif ($token->defined_as(T_hidden_noalign)) { # \puts something in vertical list Debug("Halign $alignment: COLUMN invisible noalign") if $LaTeXML::DEBUG{halign}; push(@LaTeXML::LIST, $stomach->invokeToken($token)); } else { last; } } Debug("Halign $alignment: COLUMN end scan at " . Stringify($token)) if $LaTeXML::DEBUG{halign}; - if (!$token || Equals($token, T_END) || Equals($token, T_CS('\@close@alignment'))) { + if (!$token || $token->defined_as(T_END) || $token->defined_as(T_close_alignment)) { return (undef, $token, undef, undef); } # Next column, unless spanning (then combine columns) if ($spanning) { @@ -3353,7 +3361,7 @@ sub digestAlignmentColumn { . " => " . ToString(List(@LaTeXML::LIST))) if $LaTeXML::DEBUG{halign}; return (List(@LaTeXML::LIST, mode => ($ismath ? 'math' : 'text')), $token, $type, $hidden); } } - elsif (Equals($token, T_CS('\hidden@noalign'))) { # \puts something in vertical list + elsif ($token->defined_as(T_hidden_noalign)) { # \puts something in vertical list Debug("Halign $alignment: COLUMN invisible noalign") if $LaTeXML::DEBUG{halign}; push(@LaTeXML::LIST, $stomach->invokeToken($token)); } else { # Else, we're getting some actual content for the column @@ -3467,8 +3475,7 @@ sub extractAlignmentColumn { # Note that the 1st $ is switching OUT of math mode! sub stripDupMath { my (@tokens) = @_; - my @poss = grep { Equals($tokens[$_], T_MATH) } 0 .. $#tokens; -### pop(@poss) if scalar(@poss) % 2; # Get pairs! + my @poss = grep { $tokens[$_]->defined_as(T_MATH) } 0 .. $#tokens; shift(@poss) if scalar(@poss) % 2; # Get pairs! while (@poss) { my ($p2, $p1) = (pop(@poss), pop(@poss)); @@ -4225,21 +4232,20 @@ DefMacroI('\eqno', undef, sub { my @stuff = (); # This is risky!!! while (my $t = $gullet->readXToken(0)) { - if (Equals($t, T_BEGIN)) { + if ($t->defined_as(T_BEGIN)) { push(@stuff, $t, $gullet->readBalanced, T_END); } # What do I need to explicitly list here!?!?!? UGGH! - elsif (Equals($t, T_MATH) - || Equals($t, T_CS('\]')) + elsif ($t->defined_as(T_MATH) + || $t->defined_as(T_CS('\]')) # UGH from 2022: also don't jump over rows - || Equals($t, T_CS('\cr')) + || $t->defined_as(T_CS('\cr')) # see arXiv:math/0001062, for one example - || Equals($t, T_CS('\hidden@cr')) - || Equals($t, T_CS('\@@ENDDISPLAYMATH')) - || Equals($t, T_CS('\begingroup')) # Totally wrong, but to catch expanded environments + || $t->defined_as(T_CS('\hidden@cr')) + || $t->defined_as(T_CS('\@@ENDDISPLAYMATH')) + || $t->defined_as(T_CS('\begingroup')) # Totally wrong, but to catch expanded environments || (ToString($t) =~ /^\\(?:begin|end)\{/) # any sort of environ begin or end??? # This seems needed within AmSTeX environs ) { - # || Equals($t,T_CS('\end{equation}'))|| Equals($t,T_CS('\end{equation*}'))){ return (Invocation(T_CS('\@@eqno'), Tokens(@stuff)), $t); } else { push(@stuff, $t); } } @@ -4375,12 +4381,12 @@ sub revertScript { my @tokens = $script->revert; my @t = @tokens; my $l; - if (Equals($t[0], T_BEGIN)) { + if ($t[0]->defined_as(T_BEGIN)) { $l++; shift(@t); } while (@t && $l) { my $t = shift(@t); - if (Equals($t, T_BEGIN)) { $l++; } - elsif (Equals($t, T_END)) { $l--; } } + if ($t->defined_as(T_BEGIN)) { $l++; } + elsif ($t->defined_as(T_END)) { $l--; } } return (@tokens && !@t ? @tokens : (T_BEGIN, @tokens, T_END)); } # Compute the 'advance' of this script. @@ -7636,7 +7642,7 @@ sub SplitTokens { while ($t = shift(@tokens)) { if (grep { Equals($t, $_) } @delims) { push(@items, [@toks]); @toks = (); } - elsif (Equals($t, T_BEGIN)) { + elsif ($t->defined_as(T_BEGIN)) { push(@toks, $t); my $level = 1; while ($level && defined($t = shift(@tokens))) { @@ -7644,7 +7650,7 @@ sub SplitTokens { $level++ if $cc == CC_BEGIN; $level-- if $cc == CC_END; push(@toks, $t); } } - elsif (Equals($t, T_MATH)) { + elsif ($t->defined_as(T_MATH)) { push(@toks, $t); while (defined($t = shift(@tokens))) { my $cc = $t->getCatcode; diff --git a/lib/LaTeXML/Package/pdfTeX.pool.ltxml b/lib/LaTeXML/Package/pdfTeX.pool.ltxml index d3f05dded..e3bc2543e 100644 --- a/lib/LaTeXML/Package/pdfTeX.pool.ltxml +++ b/lib/LaTeXML/Package/pdfTeX.pool.ltxml @@ -187,8 +187,8 @@ DefPrimitive('\pdfendlink', undef); DefMacro('\pdfinfo{}', ''); DefMacro('\pdfcatalog{} OpenActionSpecification', ''); -DefMacro('\pdfnames{}', {}); -DefMacro('\pdftrailer{}', {}); +DefMacro('\pdfnames{}', ''); +DefMacro('\pdftrailer{}', ''); DefMacro('\pdfmapfile{}', ''); DefMacro('\pdfmapline{}', ''); # \pdffontattr font general text diff --git a/lib/LaTeXML/Package/pstricks.sty.ltxml b/lib/LaTeXML/Package/pstricks.sty.ltxml index c81bbc125..740837e14 100644 --- a/lib/LaTeXML/Package/pstricks.sty.ltxml +++ b/lib/LaTeXML/Package/pstricks.sty.ltxml @@ -43,10 +43,8 @@ sub ReadPSDimension { my ($gullet, $scale) = @_; $scale = LookupRegister('\psunit') unless $scale; my $s = $gullet->readOptionalSigns; - if (defined(my $d = $gullet->readInternalDimension)) { - return ($s < 0 ? $d->negate : $d); } - elsif (defined($d = $gullet->readInternalGlue)) { - return Dimension($s * $d->valueOf); } + if (defined(my $d = $gullet->readRegisterValue('Dimension', $s, 1))) { + return $d; } elsif (defined($d = $gullet->readFloat)) { if (my $unit = $gullet->readUnit) { return Dimension($s * $d->valueOf * $unit); }