File: Expandable.pm

package info (click to toggle)
latexml 0.8.8-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 31,920 kB
  • sloc: xml: 109,048; perl: 30,224; sh: 179; javascript: 28; makefile: 13
file content (150 lines) | stat: -rw-r--r-- 6,262 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# /=====================================================================\ #
# |  LaTeXML::Core::Definition                                          | #
# | Representation of definitions of Control Sequences                  | #
# |=====================================================================| #
# | Part of LaTeXML:                                                    | #
# |  Public domain software, produced as part of work done by the       | #
# |  United States Government & not subject to copyright in the US.     | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov>                        #_#     | #
# | http://dlmf.nist.gov/LaTeXML/                              (o o)    | #
# \=========================================================ooo==U==ooo=/ #

package LaTeXML::Core::Definition::Expandable;
use strict;
use warnings;
use LaTeXML::Global;
use LaTeXML::Common::Object;
use LaTeXML::Common::Error;
use LaTeXML::Core::Token;
use LaTeXML::Core::Tokens;
use LaTeXML::Core::Parameters;
use LaTeXML::Package qw(TokenizeInternal);
use base             qw(LaTeXML::Core::Definition);

sub new {
  my ($class, $cs, $parameters, $expansion, %traits) = @_;
  my $source = $STATE->getStomach->getGullet->getMouth;
  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 ($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'),
    hasCCARG    => (($type ne 'CODE') && (grep { $$_[1] == CC_ARG; } $expansion->unlist) ? 1 : 0),
    %traits }, $class; }

sub isExpandable {
  return 1; }

sub getExpansion {
  my ($self) = @_;
  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) = @_;
  # shortcut for "trivial" macros; but only if not tracing & profiling!!!!
  my $_tracing  = $STATE->lookupValue('TRACING') || 0;
  my $tracing   = ($_tracing & TRACE_MACROS);
  my $profiled  = ($_tracing & TRACE_PROFILE) && ($LaTeXML::CURRENT_TOKEN || $$self{cs});
  my $expansion = $$self{expansion};
  my $etype     = ref $expansion;
  my $result;
  my $parms = $$self{parameters};

  LaTeXML::Core::Definition::startProfiling($profiled, 'expand') if $profiled;
  if ($etype eq 'CODE') {
    # Harder to emulate \tracingmacros here.
    my @args = ($parms ? $parms->readArguments($gullet, $self) : ());
    $result = Tokens(&$expansion($gullet, @args));
    if ($tracing) {
      Debug($self->tracingCSName . ' ==> ' . tracetoString($result));
      Debug($self->tracingArgs(@args)) if @args; } }
  elsif (!$parms) {    # Trivial macro
    Debug($self->tracingCSName . ' ->' . tracetoString($expansion)) if $tracing;
    # For trivial expansion, make sure we don't get \cs or \relax\cs direct recursion!
    if (!$onceonly && $$self{cs}) {
      my ($t0, $t1) = ($etype eq 'LaTeXML::Core::Tokens'
        ? ($$expansion[0], $$expansion[1]) : ($expansion, undef));
      if ($t0 && ($t0->equals($$self{cs})
          || ($t1 && $t1->equals($$self{cs}) && $t0->equals(T_CS('\protect'))))) {
        Error('recursion', $$self{cs}, $gullet,
          "Token " . Stringify($$self{cs}) . " expands into itself!",
          "defining as empty");
        $expansion = TokensI(); } }
    $result = $expansion; }
  else {
    my @args = $parms->readArguments($gullet, $self);
    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(@args)) if @args; } }
  # Getting exclusive profiling requires dubious Gullet support!
  $result = Tokens($result, T_MARKER($profiled)) if $profiled;
  return $result; }

# print a string of tokens like TeX would when tracing.
sub tracetoString {
  my ($tokens) = @_;
  return join('', map { ($_->getCatcode == CC_CS ? $_->toString . ' ' : $_->toString) }
      $tokens->unlist); }

sub equals {
  my ($self, $other) = @_;
  return (defined $other && (ref $self) eq (ref $other))
    && Equals($self->getParameters, $other->getParameters)
    && Equals($$self{expansion},    $$other{expansion}); }

#======================================================================
1;

__END__

=pod

=head1 NAME

C<LaTeXML::Core::Definition::Expandable>  - Expandable Control sequence definitions.

=head1 DESCRIPTION

These represent macros and other expandable control sequences
that are carried out in the Gullet during expansion. The results of invoking an
C<LaTeXML::Core::Definition::Expandable> should be a list of C<LaTeXML::Core::Token>s.
See L<LaTeXML::Package> for the most convenient means to create Expandables.

It extends L<LaTeXML::Core::Definition>.

=head1 AUTHOR

Bruce Miller <bruce.miller@nist.gov>

=head1 COPYRIGHT

Public domain software, produced as part of work done by the
United States Government & not subject to copyright in the US.

=cut