home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / lyx21041.zip / XFree86 / lib / X11 / lyx / reLyX / Text / TeX.pm (.txt) < prev   
LaTeX Document  |  1999-04-26  |  55KB  |  1,164 lines

  1. package Text::TeX;
  2. # This file is copyright (c) 1997-8 Ilya Zakharevich
  3. # Modifications for reLyX by Amir Karger
  4. # You are free to use and modify this code under the terms of
  5. # the GNU General Public Licence version 2 or later.
  6. #use strict;
  7. #use vars qw($VERSION @ISA @EXPORT);
  8. #require Exporter;
  9. #require # AutoLoader;    # To quiet AutoSplit.
  10. # @ISA = qw
  11. # (Exporter AutoLoader);
  12. # Items to export into callers namespace by default. Note: do not export
  13. # names by default without a very good reason. Use EXPORT_OK instead.
  14. # Do not simply export all your public functions/methods/constants.
  15. @EXPORT = qw(
  16. $VERSION = '0.01';
  17. # Preloaded methods go here.
  18. # Does not deal with verbatims
  19. # Spaces are treated bad.
  20. #####################      GENERAL NOTES      ##################################
  21. # Each package describes a different sort of token.
  22. # Packages:
  23. #    Chunk        - default, just used as an ISA
  24. #    Text         - plain text, made up of TT::$usualtokenclass stuff
  25. #    Paragraph    - new paragraph starting (cuz you got \n\n in a latex file)
  26. #    Token        - simple token, like ~ or \blah
  27. #    EndLocal     - pseudotoken meaning that the scope of a local command (like
  28. #                   '\large') has ended
  29. #    BegArgsToken - pseudotoken which takes one or more arguments, like \section
  30. #    ArgToken     - pseudotoken returned in between arguments to a BegArgsToken
  31. #    EndArgsToken - pseudotoken returned after we finish getting arguments
  32. #                   to a BegArgsToken
  33. #    LookAhead    - a special kind of EndArgsToken when you want to look ahead
  34. #    BegArgsTokenLookedAhead - special kind of BegArgsToken (see man page)
  35. #    Begin::Group - Beginning of a group, i.e., '{'
  36. #    End::Group   - End of a group, i.e., '}'
  37. #    Begin::Group::Args - begin group but get args first, i.e., '\begin'
  38. #    End::Group::Args   - end group but get args first, i.e., '\end'
  39. #    SelfMatch    - e.g., '$'. Matches itself, but otherwise like a Begin::Group
  40. #    Separator    - e.g., '&' (not used in reLyX)
  41. #    Comment      - (not used in reLyX)
  42. # The main package is TT::OpenFile. It contains the subroutines that do
  43. #    most of the parsing work. TT::GetParagraph does some stuff too, but
  44. #    it's not a token you'd expect the code to return
  45. # Package subroutines (other than 'new'):
  46. #    refine - takes a token to a more specific kind of token type
  47. #               e.g., '{' goes from TT::Token to TT::Begin::Group
  48. #    digest - extra actions to do once you've eaten the token.
  49. #               e.g., eating arguments of \begin, or popping various
  50. #               stacks when you get to an End::Group
  51. #    print  - how to print the token (e.g., the text making up the token)
  52. #    exact_print - print the token exactly as it appeared in the file.
  53. #               Usually involves adding whitespace
  54. # Token and pseudotokens have some more subs:
  55. #    base_token   - the token this token is created from. It's the token
  56. #                   itself for a Token, but not for pseudotokens
  57. #    token_name   - the name of the base_token
  58. # Token structure:
  59. # $tok->[0] will usually be the word (e.g., '\blah') the parser read
  60. #      For pseudotokens, it's something more complicated
  61. #      (some tokens, like Paragraph have nothing there, though)
  62. # $tok->[1] will be any comment (usually ignored)
  63. # $tok->[2] will be the exact thing the parser read (usu. [0] plus whitespace)
  64. # $tok->[3] stores arguments for Begin::Group::Args and End::Group::Args
  65. # $tok->[4] stores pointer to beginning token for End::Group::Args
  66. #    A TT::Group is a reference to an array of tokens. Often (but not always),
  67. # the first and last groups are Begin::Group and End::Group tokens respectively.
  68. # Pseudotokens are objects, one of whose fields is a reference to the token
  69. # that created the pseudotoken
  70. # BegArgToken, ArgToken, EndArgToken pseudotokens:
  71. # $tok->[0][0] - token (e.g. a TT::Token) that begins this group
  72. # $tok->[0][1] - number of arguments that that token takes
  73. # $tok->[0][2] - (found only in ArgToken) number of arguments to the token
  74. #                that have been read so far
  75. ################################################################################
  76. ########################   GLOBAL VARIABLES   ##################################
  77. # Sorts of text you find in a LaTeX file. For matching
  78. $notusualtoks = "\\\\" . '\${}^_~&@%'; # Why \\\\? double interpretation!
  79. $notusualtokenclass = "[$notusualtoks]";
  80. $usualtokenclass = "[^$notusualtoks]";
  81. # Original $macro wouldn't recognize, e.g., '\section*'. Added '\*?' - Ak
  82. # (Had to add it for \section and \\ separately.)
  83. #    \" or \frac, e.g. Note that it eats whitespace AFTER the token. This is
  84. # correct LaTeX behavior, but if text follows such a macro, and you just
  85. # print out the macro & then the text, they will run together.
  86. $macro = '\\\\(?:[^a-zA-Z]\*?|([a-zA-Z]+\*?)\s*)'; # Has one level of grouping
  87. #$macro = '\\\\(?:[^a-zA-Z]|([a-zA-Z]+)\s*)'; # Contains one level of grouping
  88. # active is a backslashed macro or $$ (same as \[) or ^^ followed by a char
  89. #    (^^A means ASCII(1), e.g. See the TeXbook) or a special character like ~
  90. $active = "$macro|\\\$\\\$|\\^\\^.|$notusualtokenclass"; # 1 level of grouping
  91. # In TeX, ^joe is equivalent to ^{j}oe, so sometimes we use tokenpattern
  92. #     instead of multitokenpattern to get just one character
  93. $tokenpattern = "($usualtokenclass)|$active"; # Two levels of grouping
  94. $multitokenpattern = "($usualtokenclass+)|$active"; # Two levels of grouping
  95. # Note: In original (CPAN) version, $commentpattern had "". It needs ''
  96. # or otherwise '\s' gets translated to 's'
  97. $commentpattern = '(?:%.*\n\s*)+'; #one or more comment lines
  98. $whitespaceAndComment = '\s*(%.*\n[ \t]*)+';
  99. # matches either nothing OR an argument in brackets ($1 doesn't include [])
  100. $optionalArgument = "(?:\\[([^]]*)\\])?"; # Contains one level of grouping
  101. # These tokens are built from other tokens, so they're pseudotokens
  102. #    (except BegArgsToken actually does have text!?)
  103. for (qw(Text::TeX::ArgToken Text::TeX::BegArgsToken Text::TeX::EndArgsToken )) {
  104.   $pseudo{$_} = 1;
  105. # More global variables can be found at the end of the file
  106. # E.g., the main Tokens hash
  107. #######################   Token Packages   #####################################
  108.   package Text::TeX::Comment;
  109.   $ignore = 1;
  110.   package Text::TeX::Chunk;
  111.   sub refine {}
  112.   sub digest {}
  113.   sub collect {$_[0]->[0]}
  114.   sub new {
  115.     my $class = shift;
  116.     bless [@_], $class;
  117.   sub print {$_[0]->[0]}
  118.   # exact_print prints the *exact* text read, including whitespace
  119.   #     (but not including comments...)
  120.   sub exact_print {$_[0]->[2]}
  121.   # print the comment that came before a token
  122.   sub comment {$_[0]->[1]}
  123.   package Text::TeX::Token;
  124.   @ISA = ('Text::TeX::Chunk');
  125.   sub refine {
  126.     my $self = shift;
  127.     return undef unless defined $self->[0];
  128.     my $txt = shift;
  129.     my $type;
  130.     if (defined ($tok = $txt->{tokens}->{$self->[0]}) 
  131.     and defined $tok->{class}) {
  132.       bless $self, $tok->{class};
  133.     }
  134.   } # end sub refine
  135.   # Name of the token. Same as print for Token, but ArgToken and
  136.   # EndArgsToken, e.g., print nothing!
  137.   sub token_name {
  138.       my $tok = shift->base_token;
  139.       return $tok->print;
  140.   sub base_token {
  141.   # For pseudotokens, this sub is more complicated, but a token is just a token.
  142.       return shift;
  143.   # return the syntax argument created by reLyX
  144.   # Return "" if relyx_args is empty, i.e., if the token takes no args
  145.   # Return undef if relyx_args doesn't exist, i.e., if the token is unknown
  146.   sub relyx_args {
  147.       warn "not enough args to Text::TeX::relyx_args" unless @_==2;
  148.       my ($tok,$object) = (shift, shift);
  149.       my $name;
  150.       # Test copied from TT::OpenFile::eat
  151.       if (defined ($name = $tok->token_name)) {
  152.       #print "$name is defined\n";
  153.       if (defined ($entry = $object->{"tokens"}->{$name})) {
  154.           #print "Entry in MyTokens is defined\n";
  155.           if (exists ($entry->{"relyx_args"})) { # even if it's empty...
  156.           #print "the args are '",$entry->{"relyx_args"},"'\n";
  157.           return $entry->{"relyx_args"}
  158.           }
  159.       }
  160.       # else...
  161.       #print "did not exist";
  162.       return undef;
  163.   } # end sub relyx_args
  164.   sub next_args {
  165.   # Return the next argument(s) expected by this token.
  166.   # For regular Tokens: /^o*$/. 
  167.   # For BegArgsTokens and ArgTokens: /^o*[rR]$/
  168.   # For EndArgsTokens: /^o*/. (in case opt args come after last required arg)
  169.     my ($eaten,$fileobject) = (shift,shift);
  170.     # Get the number & type of arguments of this token == /^[or]*$/
  171.     # If it takes no args, just return
  172.     # Will also return if curr_args is called for plain Text for some reason
  173.     my $syntax = $eaten->relyx_args($fileobject) or return "";
  174.     # If it takes just optional args, return them (it's a plain Token)
  175.     return $syntax if $syntax =~ /^o+$/;
  176.     # Number of arguments we've already read (== 0 for BegArgsToken)
  177.     # Note that we only get here for Beg/EndArgsToken or ArgToken
  178.     my $arg_num = $eaten->args_done;
  179.     # Split args into single "argument sets", each of which is 0 or more
  180.     # optional arguments followed by 0 or 1 required argument.
  181.     @args = ($syntax =~ /o*[rR]?/g);
  182.     push (@args,""); # necessary for EndArgsToken if $syntax ends with "r"
  183.     # Now return the n'th argument set
  184.     #    e.g., if 0 args have been eaten, return the 0th element of @args,
  185.     # which is the first argument
  186.     return $args[$arg_num];
  187.   } # end sub curr_args
  188. } # end package Text::TeX::Token
  189.   package Text::TeX::BegArgsToken;
  190.   @ISA = ('Text::TeX::Token');
  191.   sub print {
  192.       my $tok = shift->base_token; # Token this pseudotoken was made from
  193.       return $tok->print;
  194.   sub exact_print {
  195.       my $tok = shift->base_token;
  196.       return $tok->exact_print;
  197.   # How many arguments we've read already.
  198.   # Obviously zero before we've begun to read the arguments
  199.   sub args_done {return 0}
  200.   sub base_token { return shift->[0]->[0] }
  201.   sub comment { return shift->base_token->comment }
  202.   package Text::TeX::ArgToken;
  203.   @ISA = ('Text::TeX::Token');
  204.   # This token isn't made from actual text, so it prints nothing
  205.   sub print {return ''}
  206.   sub exact_print {return ''}
  207.   # How many arguments we've read already.
  208.   # Luckily, this number is stored in the ArgToken token
  209.   sub args_done { return shift->[0]->[2] }
  210.   sub base_token { return shift->[0]->[0] }
  211.   package Text::TeX::EndArgsToken;
  212.   @ISA = ('Text::TeX::Token');
  213.   # This token isn't made because of real text, so it prints nothing
  214.   sub print {return ''}
  215.   sub exact_print {return ''}
  216.   # How many arguments we've read already.
  217.   # Obviously the total number of arguments, since we're done
  218.   sub args_done {return shift->[0]->[1]}
  219.   sub base_token { return shift->[0]->[0] }
  220.   package Text::TeX::EndLocal;
  221.   @ISA = ('Text::TeX::Token');
  222.   # No text in this token
  223.   sub print {return ''}
  224.   sub exact_print {return ''}
  225.   sub base_token { return shift->[0] }
  226.   package Text::TeX::Group;
  227.   sub new {shift; my $in = shift; bless $in}
  228.   sub print {
  229.     local @arr; #arr becomes global for called subroutines
  230.     foreach (@{ $_[0] }) {
  231.       push(@arr, $_->print);
  232.     }
  233.     "`" . join("',`", @arr) . "'";
  234.   # exact_print prints w/out the quotes
  235.   sub exact_print {
  236.     local @arr; #arr becomes global for called subroutines
  237.     foreach (@{ $_[0] }) {
  238.       push(@arr, $_->exact_print);
  239.     }
  240.     join("", @arr); # ... and return it
  241.   # Not created straight from LaTeX, so it'll never have a comment
  242.   # (although comments can be in the subtokens in the group)
  243.   sub comment {undef}
  244.   # Return what's in the group, i.e. strip out the '{' and '}' tokens
  245.   # if they exist. Return an array of tokens or just one token
  246.   sub contents {
  247.       #strip off TT::Begin::Group and TT::End::Group from beginning and end
  248.       # if they exist. eatBalanced will return Tokens, so don't worry about
  249.       # stripping too much from a group like {{foo} bar}. And eatGroup
  250.       # will return Begin::Group, Group, End::Group, so after stripping one,
  251.       # don't have to worry about stripping another.
  252.       $group = shift;
  253.       if (ref($group->[0] ) eq "Text::TeX::Begin::Group" and
  254.           ref($group->[-1]) eq "Text::TeX::End::Group")
  255.       {
  256.       shift @$group;
  257.       pop @$group;
  258.       }
  259.       if (wantarray) {
  260.           return @$group;
  261.       } elsif (!@$group) { # group was '{}'
  262.           return new Text::TeX::Token '','',''; # send back an empty token
  263.       } else {
  264.           warn "Text::TeX -- more than one token in group!" if $#$group > 1;
  265.           return $$group[0];
  266.       }
  267.   package Text::TeX::End::Group;
  268.   @ISA = ('Text::TeX::Chunk');
  269.   sub new {shift; my $in = shift; bless \$in}
  270.   sub digest {            # 0: the token, 1: text object
  271.     # If there are any EndLocal tokens in $txt->{presynthetic}, do them first
  272.     # See TT::OpenFile::check_presynthetic for details
  273.     return if $_[1]->check_presynthetic($_[0]);    # May change $_[0]
  274.     my $wa = $_[1]->curwaitforaction;
  275.     my $w = $_[1]->popwait;
  276.     warn "Expecting `$w', got `$_[0][0]'=`$_[0][0][0]' in `$ {$_[1]->{paragraph}}'" 
  277.       if $w ne $_[0]->[0];
  278.     &$wa if defined $wa; # i.e., do $txt->{waitforactions}[-1] if it exists
  279.   package Text::TeX::End::Group::Args;
  280.   @ISA = ('Text::TeX::End::Group');
  281.   sub digest {            # 0: the token, 1: text object
  282.     # If there are any EndLocal tokens in $txt->{presynthetic}, do them first
  283.     #    (Lamport p. 27 says \em is ended by '\end{blah}', not just '}')
  284.     # check_presynthetic will put the End::Group::Args token into pending_in
  285.     #    so it'll be read on the next pass through eat. Since sub digest will
  286.     #    be called again on this token, don't read the argument to \end{}
  287.     #    on the first call to sub digest
  288.     # See TT::OpenFile::check_presynthetic for details
  289.     return if $_[1]->check_presynthetic($_[0]);    # May change $_[0]
  290.     my $Token = $_[1]->{tokens}->{$_[0]->[0]};
  291.     my $count = $Token->{eatargs};
  292.     my ($tok, @arr);
  293.     # Read environment you're ending (just like in Begin::Group::Args)
  294.     while ($count--) {
  295.       $tok = $_[1]->eatGroup(1);
  296.       if (@$tok == 3 and $tok->[0]->[0] eq '{') { # Special case for {\a}
  297.     $tok = $tok->[1];
  298.       }
  299.       push(@arr,$tok);
  300.     }
  301.     #$_[0]->[0] .= ' ' . join ' ', map $_->[0], @arr;
  302.     $_[0]->[3] = \@arr;
  303.     my $s = $_[1]->starttoken;
  304.     # like TT::End::Group
  305.     my $wa = $_[1]->curwaitforaction;
  306.     my $w = $_[1]->popwait;
  307.     # If you got '}' when you wanted '\end'
  308.     warn "Expecting `$w', got $_[0]->[0] in `$ {$_[1]->{paragraph}}'" 
  309.       if $w ne $_[0]->[0];
  310.     # If you got \end{foo} when you wanted \end{bar}
  311.     if ($Token->{selfmatch} and $s->environment ne $_[0]->environment) {
  312.       warn "Expecting `$w" , "{", $s->environment,"}', got $_[0]->[0]",
  313.     "{", $_[0]->environment , "} in `$ {$_[1]->{paragraph}}'";
  314.     }
  315.     # If there was a waitforaction then do it now
  316.     &$wa if defined $wa;
  317.     $_[0]->[4] = $s;        # Put the start data into the token
  318.   sub print { # need special print to print name of environment
  319.       my $obj = $_[0];
  320.       my $env = $obj->environment; # assume we've already digested it
  321.       # Use the method for printing a regular old token, but append env. name
  322.       return $obj->SUPER::print . "{$env}";
  323.   sub exact_print {
  324.       my $obj = $_[0];
  325.       my $env = $obj->environment; # assume we've already digested it
  326.       # Use the method for printing a regular old token, but append env. name
  327.       return $obj->SUPER::exact_print . "{$env}";
  328.   sub environment {
  329.   # this group's environment
  330.       return $_[0]->[3]->[0]->[0];
  331. } # end package TT::End::Group::Args
  332.   package Text::TeX::Begin::Group::Args;
  333.   @ISA = ('Text::TeX::Begin::Group');
  334.   sub digest {            # 0: the token, 1: text object
  335.     my $Token = $_[1]->{tokens}->{$_[0]->[0]};
  336.     my $count = $Token->{eatargs};
  337.     my ($tok, @arr);
  338.     # Read the arguments, e.g., read "{blah}" for "\begin{blah}"
  339.     while ($count--) {
  340.       $tok = $_[1]->eatGroup(1);
  341.       if (@$tok == 3 and $tok->[0]->[0] eq '{') { # Special case for {\a}
  342.     $tok = $tok->[1];
  343.       }
  344.       push(@arr,$tok);
  345.     }
  346.     # $_[0]->[0] .= ' ' . join ' ', map $_->[0], @arr;
  347.     $_[0]->[3] = \@arr;
  348.     $_[0]->SUPER::digest($_[1]); # i.e. do Begin::Group stuff (pushwait)
  349.   sub print { # need special print to print name of environment
  350.       my $obj = $_[0];
  351.       my $env = $obj->environment; # assume we've already digested it
  352.       # Use the method for printing a regular old token, but append env. name
  353.       return $obj->SUPER::print . "{$env}";
  354.   sub exact_print {
  355.       my $obj = $_[0];
  356.       my $env = $obj->environment; # assume we've already digested it
  357.       # Use the method for printing a regular old token, but append env. name
  358.       return $obj->SUPER::exact_print . "{$env}";
  359.   sub environment {
  360.   # this group's environment
  361.       return $_[0]->[3]->[0]->[0];
  362. } # end package TT::Begin::Group::Args
  363.   package Text::TeX::Begin::Group;
  364.   @ISA = ('Text::TeX::Chunk');
  365.   # 0: the token, 1: text object
  366.   sub digest {
  367.       my ($tok, $txt) = (shift, shift);
  368.       # $dummy = the anonymous hash associated with this token in the %Tokens
  369.       my $dummy = $txt->{tokens}->{$tok->[0]};
  370.       # see if this group requires different actions
  371.       my $newaction; # action to do while parsing this group
  372.       my $waitaction; # action to do when you hit the matching End::Group
  373.       undef $waitaction; undef $newaction;
  374.       if (defined $dummy) {
  375.           if (exists $dummy->{newaction}) {
  376.           $newaction = $dummy->{newaction};
  377.       if (exists $dummy->{waitaction}) {
  378.           $waitaction = $dummy->{waitaction};
  379.       }
  380.       # push stuff onto stacks for this group
  381.       $txt->pushwait($tok, $newaction, $waitaction);
  382.   package Text::TeX::SelfMatch;
  383.   @ISA = ('Text::TeX::Chunk');
  384.   sub refine {
  385.   # This subroutine is never used. See sub digest below
  386.     if ($_[1]->curwait eq $_[0]->[0]) {  #if you match what you're waiting for
  387.       bless $_[0], Text::TeX::End::Group;
  388.     } else { #you need to BE matched
  389.       bless $_[0], Text::TeX::Begin::Group;
  390.     }
  391.   # 0: the token, 1: text object
  392.   # Unfortunately, this sub IS necessary, because originally, a '$' (e.g.)
  393.   #    is type TT::Token. Calling refine calls Chunk::refine, which blesses
  394.   #    it to SelfMatch, but then SelfMatch::refine is never called! -Ak
  395.   sub digest {            # XXXX Should not be needed?
  396.     # curwait returns undefined if not waiting for anything
  397.     if (defined ($cwt = $_[1]->curwait) && $cwt eq $_[0]->[0]) { 
  398.       bless $_[0], Text::TeX::End::Group;
  399.       $_[0]->Text::TeX::End::Group::digest($_[1]);
  400.     } else {
  401.       bless $_[0], Text::TeX::Begin::Group;
  402.       $_[1]->pushwait($_[0]);
  403.     }
  404. @Text::TeX::Text::ISA = ('Text::TeX::Chunk');
  405. @Text::TeX::Paragraph::ISA = ('Text::TeX::Chunk');
  406. @Text::TeX::BegArgsTokenLookedAhead::ISA = ('Text::TeX::BegArgsToken');
  407. @Text::TeX::LookAhead::ISA = ('Text::TeX::EndArgsToken');
  408. @Text::TeX::Separator::ISA = ('Text::TeX::Chunk');
  409. ########################   MAIN CODE   #########################################
  410.   package Text::TeX::GetParagraph;
  411.   # Get a new paragraph from the LaTeX file
  412.   # Get stuff until a non-empty line which follows an empty line
  413.   sub new {
  414.     shift; 
  415.     my $file = shift;
  416.     my $fh;
  417.     $fh = $ {$file->{fhs}}[-1] if @{$file->{fhs}};
  418.     return undef if (not defined $fh or eof($fh)) and $file->{readahead} eq "";
  419.     # See below: every time we call GetParagraph, we read one extra (non-empty)
  420.     #    line, which we store in readahead for next time
  421.     my $string = $file->{readahead};
  422.     $file->{readahead} = ""; #default in case eof($fh) or !defined($fh)
  423.     if (defined $fh) { # i.e., if eof($fh) just return readahead from last time
  424.       # Read until an empty line (or eof)
  425.       while (defined ($in = <$fh>)  && ($in =~ /\S/)) { # $in undefined at eof
  426.     $string .= $in;
  427.       }
  428.       # $in has the empty line we just read in. Add it for verbatim copying
  429.       $string .= $in if defined $in; # add whitespace
  430.       # Now read until NON-empty line (or eof)
  431.       while (defined ($in = <$fh>) && ($in !~ /\S/)) {
  432.     $string .= $in;
  433.       }
  434.       # Next time, the paragraph will begin with the non-empty line we just read
  435.       $file->{readahead} = $in if defined $in; # readahead stays "" at eof
  436.     }
  437.     bless \$string; # ... and return it
  438.   package Text::TeX::OpenFile;
  439.   $refgen = "TeXOpenFile0000";
  440.   sub new {
  441. # Description of OpenFile object:
  442. # readahead - every time we read a paragraph we read one extra token. This 
  443. #             token goes into 'readahead' and is prepended to the next paragraph
  444. #             we read
  445. # paragraph - stores the paragraph we're currently parsing
  446. # actions   - what to do. TT::OpenFile->process calls the function pointed
  447. #             to by actions on each token it eats
  448. # tokens    - reference to a hash describing all tokens that the parser
  449. #             should recognize
  450. # presynthetic - holds pseudotokens to deliver before a block ends.
  451. #             Specifically, it holds EndLocal tokens, so that we know to end
  452. #             a command like \em just before the '}' which ends a group
  453. # synthetic - holds pseudotokens to deliver after block ends - specifically,
  454. #             it holds ArgToken (and EndArgsToken) tokens, which it returns
  455. #             in between arguments (and after all arguments) to a command.
  456. #             (also holds LookAhead tokens, which are like EndArgsTokens)
  457. # pending_in - pseudotokens for input. Stuff is put here from synthetic or
  458. #             from pending_out, and if there's something in pending_in, sub
  459. #             eat doesn't bother eating a new token
  460. # pending_out - pseudotokens for output -- stuff put here from presynthetic
  461. #             If there's anything in pending_out it gets returned or put into
  462. #             pending_in, and sub eat doesn't bother eating a new token
  463.     shift; my $file = shift; my %opt = @_;
  464.     if (defined $file) {
  465.        ++$refgen;
  466.        open("::$refgen",$file) || die "Cannot open $file: $!";
  467.        die "End of file `$file' during opening" if eof("::$refgen");
  468.     }
  469.     my $fhs = defined $file ? ["::$refgen"] : [];
  470.     bless {  fhs => $fhs, 
  471.          readahead => ($opt{string} || ""), 
  472.          files => [$file],
  473.          "paragraph" => undef, 
  474.          "tokens" => ($opt{tokens} || \%Text::TeX::Tokens),
  475.          waitfors => [], options => \%opt,
  476.          waitforactions => [],
  477.          defaultacts => [$opt{defaultact}],    # The last element is
  478.                                                 # the default action
  479.                                                 # for next deeper
  480.                                                 # level
  481.          actions => [defined $opt{action} ? 
  482.              $opt{action} : 
  483.              $opt{defaultact}],
  484.          waitargcounts => [0],
  485.          pending_out => [],
  486.          pending_in => [],
  487.          synthetic => [[]],
  488.          presynthetic => [[]],
  489.        };
  490.   sub DESTROY {
  491.     my $in = shift; my $i = 0;
  492.     for (@{$in->{fhs}}) {
  493.       close($_)
  494.     || die "Cannot close $ {$in->{files}}[$i]: $!";
  495.       $i++;
  496.     }
  497. # Return the paragraph we're currently reading
  498. #    If called with an argument, get a new paragraph at end of par, otherwise
  499. # don't. (Useful for looking ahead without affecting the file we're reading)
  500. # Either way, return nothing at end of par.
  501.   sub paragraph {
  502.     my $in = shift;
  503.     my $get_paragraph = defined(shift);
  504.     #print "ep.in=$in\n";
  505.     # Return something if not at end of par
  506.     if ($in->{"paragraph"} and $ {$in->{"paragraph"}} ne "") {
  507.       $in->{"paragraph"};
  508.     # Done with all files and readahead?
  509.     } elsif (@{$in->{fhs}} and eof($ {$in->{fhs}}[-1]) and !$in->{readahead}) {
  510.       undef;
  511.     # No files and done with readahead?
  512.     } elsif (!@{$in->{fhs}} and $in->{readahead} eq '') {
  513.       undef;
  514.     } else {
  515.       if ($get_paragraph) {
  516.       #warn "getting new\n";
  517.       $in->{"paragraph"} = new Text::TeX::GetParagraph $in;
  518.       }
  519.       return "";
  520.     }
  521. # pushwait means don't do stuff you've got waiting (like EndLocal tokens)
  522. #    until you're done with something else
  523. # If Arg2 exists, then the upcoming group will have it as its action
  524. # If Arg3 exists, then we'll do it when we get to the end of the upcoming group
  525.   sub pushwait {        # 0: text object, 1: token, 2: ????
  526.     push(@{ $_[0]->{starttoken} }, $_[1]);
  527.     push(@{ $_[0]->{waitfors} }, $_[0]->{tokens}{$_[1]->[0]}{waitfor});
  528.     push(@{ $_[0]->{actions} }, 
  529.      defined $_[2] ? $_[2] : $_[0]->{defaultacts}[-1]);
  530.     push(@{ $_[0]->{waitforactions} }, $_[3]);
  531.     push(@{ $_[0]->{synthetic} }, []);
  532.     push(@{ $_[0]->{presynthetic} }, []); # so that a local argument won't
  533.                                  # finish at end of the nested group
  534. # You've finished a group, so pop all the stuff pushwait pushed on
  535.   sub popwait {
  536.     if ($#{ $_[0]->{waitfors} } < 0) {
  537.       warn "Got negative depth"; return;
  538.     }
  539.     my $rest = pop(@{ $_[0]->{synthetic} });
  540.     warn "Not enough arguments" if @$rest;
  541.     $rest = pop(@{ $_[0]->{presynthetic} });
  542.     warn "Presynthetic events remaining" if @$rest;
  543.     pop(@{ $_[0]->{starttoken} });
  544.     pop(@{ $_[0]->{actions} });
  545.     pop(@{ $_[0]->{waitforactions} });
  546.     pop(@{ $_[0]->{waitfors} });
  547. # If there's anything in synthetic, pop it, reverse it, push it onto pending_out
  548.   sub popsynthetic {
  549.     my $rest = $ { $_[0]->{synthetic} }[-1];
  550.     if (@$rest) {
  551.       push @{ $_[0]->{pending_out} }, reverse @{ pop @$rest };
  552.     } 
  553.   sub pushsynthetic {        # Add new list of events to do *after* the
  554.                                 # next end of group.
  555.     my $rest = $ { shift->{synthetic} }[-1];
  556.     push @$rest, [@_];
  557.   sub addpresynthetic {        # Add to the list of events to do *before*
  558.                                 # the next end of group $uplevel above.
  559.     my ($txt) = (shift);
  560.     my $rest = $ { $txt->{presynthetic} }[-1];
  561.     push @$rest, @_;
  562. #    if (@$rest) {
  563. #      push @{ @$rest->[-1] }, @_;
  564. #    } else {
  565. #      push @$rest, [@_];
  566. #    }
  567. # If anything exists in presynthetic[-1], pop it and CHANGE $_[1] to that.
  568. #    Push $_[1] AND (reverse of) anything else in presynthetic[-1] onto
  569. #    pending_in so that we do it before any more tokens are read.
  570. # Otherwise, just return false.
  571. # BUG?! I don't understand why we do reverse. It makes stuff come out FIFO!
  572.   sub check_presynthetic {    # 0: text, 1: end token. Returns true on success
  573.     if (@{ $_[0]->{presynthetic}[-1] }) {
  574.       my $rest = $_[0]->{presynthetic}[-1];
  575.       my $next = pop @$rest;
  576.       push @{ $_[0]->{pending_in} }, $_[1], (reverse @$rest);
  577.       $#$rest = -1;        # Delete them
  578.       $_[1] = $next;
  579.       return 1;
  580.     }
  581.   sub curwait {
  582.   # return what we're currently waiting for. Returns undef if not waiting
  583.     my $ref = $_[0]->{waitfors}; $$ref[-1];
  584.   sub curwaitforaction {
  585.     my $ref = $_[0]->{waitforactions}; $$ref[-1];
  586.   sub starttoken {
  587.     my $ref = $_[0]->{starttoken}; $$ref[-1];
  588.   # These are default bindings. You probably should override it.
  589. # Eat '[blah]' or nothing. Brackets aren't returned in token's [0]
  590. #    but they are returned in [2], so exact_print will print them.
  591.   sub eatOptionalArgument {
  592.     # Call with no arg. Don't get new paragraph if at end of par
  593.     my $in = shift->paragraph;
  594.     return undef unless defined $in;
  595.     my $comment = ( $$in =~ s/^\s*($Text::TeX::commentpattern)//o );
  596.     if ($$in =~ s/^\s*$Text::TeX::optionalArgument//o) {
  597.       new Text::TeX::Token $1, $comment, $&;
  598.     } else {
  599.       warn "No optional argument found";
  600.       if ($comment) {new Text::TeX::Token undef, $comment}
  601.       else {undef}
  602.     } 
  603. # eat {blah} when it's an argument to a BegArgsToken.
  604. # Returns a TT::Group of refined tokens
  605. #    This sub calls popsynthetic, so an ArgToken or EndArgsToken will be
  606. # popped from synthetic into pending_in. This means that the ArgToken or
  607. # EndArgsToken will be the next token returned by sub eat!
  608.   sub eatRequiredArgument {
  609.       my $txt = shift;
  610.       my $group = $txt->eatGroup(@_);
  611.       $txt->popsynthetic;
  612.       return $group;
  613.   sub eatFixedString {
  614.     # Call with no arg. Don't get new paragraph if at end of par
  615.     my $in = shift->paragraph;
  616.     return undef unless defined $in;
  617.     my $str = shift;
  618.     my ($comment) = ( $$in =~ s/^\s*($Text::TeX::commentpattern)//o );
  619.     if ($$in =~ s/^\s*$str//) {new Text::TeX::Token $&, $comment, $&}
  620.     else {
  621.       warn "String `$str' expected, not found";
  622.       if ($comment) {new Text::TeX::Token undef, $comment}
  623.       else {undef}
  624.     } 
  625. # Eat '{blah}'. Braces aren't returned. Stuff is returned as a Group,
  626. #   where each member is an (unrefined) TT::Text or Token
  627.   sub eatBalanced {
  628.     my $txt = shift;
  629.     my ($in);
  630.     warn "Did not get `{' when expected", return undef
  631.       unless defined ($in = $txt->eatFixedString('{')) && defined ($in->[0]);
  632.     $txt->eatBalancedRest;
  633. # Eat 'blah}'
  634.   sub eatBalancedRest {
  635.     my $txt = shift;
  636.     my ($count,$in,@in) = (1);
  637.   EAT:
  638.     {
  639.       warn "Unfinished balanced next", last EAT 
  640.     unless defined ($in = $txt->eatMultiToken) && defined $in->[0];
  641.       push(@in,$in);
  642.       $count++,redo if $in->[0] eq '{';
  643.       $count-- if $in->[0] eq '}';
  644.       # if !$count, remove '}' you just read and exit, else keep going
  645.       pop(@in), last EAT unless $count;
  646.       redo EAT;
  647.     }
  648.     bless \@in, 'Text::TeX::Group';
  649. # Eat stuff, either a token or a group (within {})
  650. #    Tokens will be refined.
  651. #    Braces ARE in the group
  652.   sub eatGroup {        # If arg2==1 will eat exactly one
  653.                                 # group, otherwise a group or a
  654.                                 # multitoken.
  655.     my $txt = shift;
  656.     local ($in,$r,@in); #Note, this is a stupid way to name variables -Ak
  657.     if (defined ($in[0] = $txt->eatMultiToken(shift)) and defined $in[0]->[0]) {
  658.       $in[0]->refine($txt);
  659.       if (ref $in[0] ne 'Text::TeX::Begin::Group') {
  660.     return $in[0];
  661.       } else { #it is the beginning of a group. So recurse until End::Group
  662.     while (defined ($r=ref($in = $txt->eatGroup)) # Eat many groups
  663.            && $r ne 'Text::TeX::End::Group') {
  664.       push(@in,$in);
  665.     if (defined $r) {push(@in,$in)}
  666.     else {warn "Uncompleted group"}
  667.       } # end if Begin::Group
  668.     } else {
  669.       warn "Got nothing when argument expected";
  670.       return undef;
  671.     }
  672.     bless \@in, 'Text::TeX::Group';
  673.   sub eatUntil {        # We suppose that the text to match
  674.                 # fits in a paragraph 
  675.     my $txt = shift;
  676.     my $m = shift;
  677.     my ($in,@in);
  678.     while ( (!defined $txt->{'paragraph'} || $ {$txt->{'paragraph'}} !~ /$m/)
  679.        && defined ($in = $txt->eatGroup(1))) {
  680.       push(@in,@$in);
  681.     }
  682.     ($ {$txt->{'paragraph'}} =~ s/$m//) || warn "Delimiter `$m' not found";
  683.     bless \@in, 'Text::TeX::Group';
  684. # return next token without eating it. Return '' if end of paragraph
  685.   sub lookAheadToken {        # If arg2, will eat one token - WHY!? -Ak
  686.     my $txt = shift;
  687.     # Call paragraph with no argument to say we're "just looking"
  688.     my $in = $txt->paragraph;
  689.     return '' unless $in;    # To be able to match without warnings
  690.     my $comment = undef;
  691.     if ($$in =~ 
  692.     /^(?:\s*)(?:$Text::TeX::commentpattern)?($Text::TeX::tokenpattern)/o) {
  693.       if (defined $2) {return $1} #if 1 usualtokenclass char, return it ($1==$2)
  694.       elsif (defined $3) {return "\\$3"} # Multiletter (\[a-zA-Z]+)
  695.       elsif (defined $1) {return $1} # \" or notusualtokenclass
  696.     }
  697.     return '';
  698. # This is the main subroutine for eating a token.
  699. # It returns a token as either TT::Text or TT::Token.
  700. # Or it returns TT::Paragraph if it had to read a new paragraph in the TeX file.
  701.   sub eatMultiToken {        # If arg2, will eat one token
  702.     my $txt = shift;
  703.     # call paragraph with an arg so it gets new paragraph if necessary
  704.     my $in = $txt->paragraph(1);
  705.     return undef unless defined $in;
  706.     return new Text::TeX::Paragraph unless $in; #i.e., if it's a new paragraph
  707.     my $comment = undef;
  708.     # eat a comment that comes before the token we're about to read
  709.     $comment = $2 if $$in =~ s/^(\s*)($Text::TeX::commentpattern)/$1/o;
  710.     my $nomulti = shift; #if arg2, eat one token
  711.     # Eat text or a token
  712.     # Cannot use if () BLOCK, because $& is local.
  713.     $got = $$in =~ s/^\s*($Text::TeX::tokenpattern)//o    if $nomulti;
  714.     $got = $$in =~ s/^\s*($Text::TeX::multitokenpattern)//o    unless $nomulti;
  715.     # $1 = \[^a-zA-Z] or special char like ~
  716.     # $2 = regular text. Return $& to include leading space!
  717.     # $3 = [a-zA-Z]+ which followed a backslash, i.e., a 'multiletter' command
  718.     if ($got and defined $2) {new Text::TeX::Text $&, $comment, $&}
  719.     elsif ($got and defined $3) {new Text::TeX::Token "\\$3", $comment, $&}
  720.     elsif ($got and defined $1) {new Text::TeX::Token $1, $comment, $&}
  721.     elsif ($comment) {new Text::TeX::Token undef, $comment, ""}
  722.     else {undef}
  723. # This is the main subroutine for eating the file.
  724. # It eats tokens and returns them. Sometimes it also returns pseudotokens.
  725. # Basic rundown:
  726. #  - if there's stuff in pending_out, return it
  727. #  - otherwise get stuff from pending_in OR eat a new token
  728. #  - refine the token, then digest it
  729. # (- pop stuff from synthetic into pending_out for next time UNLESS
  730. #      you read a new command that takes arguments. E.g. x^\sqrt)
  731. #  - return the token unless it's special & has a 'type'
  732. #  - based on the type, set up one or more tokens to be handled later
  733. #    so that, e.g., type 'report_args' returns BegArgsToken, followed
  734. #    later by some number of ArgToken's, followed by an EndArgsToken
  735. #    LookAhead tokens can be used for _^. If you have x^a_b, the EndArgsToken
  736. # for the ^ will be changed to a LookAhead, which notes that a _ is next.
  737. # The _ has a BegArgsLookedAhead token instead of BegArgsToken. If anything
  738. # other than _ or ^ follows the argument to the LookAhead token (for example,
  739. # x^2+b, a regular old EndArgsToken is returned for the ^. reLyX doesn't use
  740. # the LookAhead functionality. (phew!)
  741.   sub eat {
  742.     my $txt = shift;
  743.     if ( @{ $txt->{pending_out} } ) {
  744.       my $out = pop @{ $txt->{pending_out} };
  745.       # E.g., if you have x^\sqrt2 -- when you pop and return the \sqrt
  746.       # EndArgsToken, you need to make sure the ^ EndArgsToken falls out next.
  747.       #    But if pending_out is an ArgToken, *don't* pop the next thing 
  748.       # (next ArgToken or EndArgsToken) out of synthetic yet
  749.       # Most often, synthetic will be empty, so popsynthetic will do nothing
  750.       $txt->popsynthetic if ref($out) eq 'Text::TeX::EndArgsToken';
  751.       if (ref $out eq 'Text::TeX::LookAhead') {
  752.     my $in = $txt->lookAheadToken;
  753.     if (defined ($res = $out->[0][2]{$in})) {
  754.       push @{$out->[0]}, $in, $res;
  755.       # actually eat what you looked ahead
  756.       $in = $txt->eatMultiToken(1);    # XXXX may be wrong if next
  757.                                         # token needs to be eaten in
  758.                                         # the style `multi', like \left.
  759.       # Put it at beginning of pending_in so we do E.g., EndLocals first
  760.       splice @{ $txt->{pending_in} }, 
  761.         0, 0, (bless \$in, 'Text::TeX::LookedAhead');
  762.       return $out;
  763.     } else {
  764.       return bless $out, 'Text::TeX::EndArgsToken';
  765.       } else {
  766.     return $out;
  767.       }
  768.     } # end if pending_out
  769.     # We didn't get & return stuff from pending_out. So try to get stuff
  770.     #    from pending_in. If there's nothing there, eat a new token.
  771.     my $in = pop @{ $txt->{pending_in} };
  772.     my $after_lookahead;
  773.     if (defined $in) {
  774.       # after_lookahead is true if we got a LookedAhead token from pending_out
  775.       #    because we looked ahead when there was a LookAhead token
  776.       $in = $$in, $after_lookahead = 1 
  777.     if ref $in eq 'Text::TeX::LookedAhead';
  778.     } else {
  779.       my $one;
  780.       # This will happen if we did pushsynthetic on the last token.
  781.       # That happened for report_args tokens, i.e., things that require
  782.       #     arguments. \frac, e.g., will read either a character or
  783.       #     a token *or* the '{' that begins a group, then popsynthetic below.
  784.       # \frac puts *two* tokens in {synthetic} so $one will be set TWICE
  785.       $one = 1 if @{ $txt->{synthetic}[-1] }; # Need to eat a group.
  786.       $in = $txt->eatMultiToken($one);
  787.     }
  788.     return undef unless defined $in;
  789.     $in->refine($txt);
  790.     $in->digest($txt);
  791.     my ($Token, $type, @arr);
  792.     unless (defined $in
  793.             && defined $in->[0] 
  794.         && $in->[0] =~ /$Text::TeX::active/o
  795.         && defined ( $Token = $txt->{tokens}->{$in->[0]} )
  796.         && exists ($Token->{"Type"})
  797.         ) {
  798.     $txt->popsynthetic;
  799.     return $in;
  800.     }
  801.     $type = $Token->{Type};
  802.     $txt->popsynthetic unless $type eq 'report_args';
  803.     # If the token is special enough that it's got a 'type', do more stuff
  804.     my $out = $in;
  805.     if ($type eq 'action') {
  806. #      return &{$Token->{sub}}($in);
  807.       return &{$Token->{'sub'}}($in); #Without 's it breaks strict refs -Ak
  808.     } elsif ($type eq 'argmask') {
  809.       # eatWithMask;        # ????
  810.     } elsif ($type eq 'args') {
  811.       # Args eaten already
  812.     } elsif ($type eq 'local') {
  813.       $txt->addpresynthetic(new Text::TeX::EndLocal $in);
  814.     } elsif ($type eq 'report_args') {
  815.       my $count = $Token->{count};
  816.       my $ordinal = $count;
  817.       my $res;
  818.       if ($res = $Token->{lookahead}) {
  819.     $txt->pushsynthetic(new Text::TeX::LookAhead [$in, $count, $res]);
  820.       } else {
  821.     # This will fall out after we read all the args this token needs
  822.     $txt->pushsynthetic(new Text::TeX::EndArgsToken [$in, $count]);    
  823.       }
  824.       # One of these tokens will fall out after we finish each arg (except last)
  825.       # Push on 3,2,1, so that when we *popsynthetic*, 1 will come off first
  826.       # followed by 2, 3
  827.       # ArgToken->[0][2] will then be the number of args read so far for
  828.       # the token held in ArgToken->[0][0]
  829.       while (--$ordinal) {
  830.     $txt->pushsynthetic(new Text::TeX::ArgToken [$in, $count, $ordinal]);
  831.       }
  832.       if ($after_lookahead) {
  833.     $out = new Text::TeX::BegArgsTokenLookedAhead [$in, $count];
  834.       } else {
  835.     $out = new Text::TeX::BegArgsToken [$in, $count];
  836.       }
  837.     } else {
  838.       warn "Format of token data unknown for `", $in->[0], "'"; 
  839.     }
  840.     return $out;
  841.   sub report_arg {
  842.     my $n = shift;
  843.     my $max = shift;
  844.     my $act = shift;
  845.     my $lastact = shift;
  846.     if ($n == $max) {
  847.       &$lastact($n);
  848.     } else {
  849.       &$act($n,$max);
  850.     }
  851.   sub eatDefine {
  852.     my $txt = shift;
  853.     my ($args, $body);
  854.     warn "No `{' found after defin", return undef 
  855.       unless $args = $txt->eatUntil('{');
  856.     warn "Argument list @$args too complicated", return undef 
  857.       unless @$args == 1 && $$args[0] =~ /^(\ \#\d)*$/;
  858.     warn "No `}' found after defin", return undef 
  859.       unless $body = $txt->eatBalancedRest;
  860.     #my @args=split(/(\#[\d\#])/,$$);       # lipa
  861. # This is the main subroutine called by parsing programs. Basically, it
  862. #     keeps eating tokens, then calling $txt->actions on that token
  863.   sub process {
  864.     my ($txt, $eaten, $act) = (shift);
  865.     while (defined ($eaten = $txt->eat)) {
  866.       if (defined ($act = $txt->{actions}[-1])) {
  867.     &$act($eaten,$txt);
  868.       }
  869.     }
  870. } #END Text::TeX::OpenFile
  871. #####################    MORE GLOBAL STUFF    ##################################
  872. %super_sub_lookahead = qw( ^ 1 _ 0 \\sb 0 \\sp 1 \\Sp 1 \\Sb 0 );
  873. # class => 'where to bless to', Type => how to process
  874. # eatargs => how many args to swallow before digesting
  875. %Tokens = (
  876.   '{' => {'class' => 'Text::TeX::Begin::Group', 'waitfor' => '}'},
  877.   '}' => {'class' => 'Text::TeX::End::Group'},
  878.   "\$" => {'class' => 'Text::TeX::SelfMatch', waitfor => "\$"},
  879.   '$$' => {'class' => 'Text::TeX::SelfMatch', waitfor => '$$'},
  880.   '\begin' => {class => 'Text::TeX::Begin::Group::Args', 
  881.            eatargs => 1, 'waitfor' => '\end', selfmatch => 1},
  882.   '\end' => {class => 'Text::TeX::End::Group::Args', eatargs => 1, selfmatch => 1},
  883.   '\left' => {class => 'Text::TeX::Begin::Group::Args', 
  884.            eatargs => 1, 'waitfor' => '\right'},
  885.   '\right' => {class => 'Text::TeX::End::Group::Args', eatargs => 1},
  886.   '\frac' => {Type => 'report_args', count => 2},
  887.   '\sqrt' => {Type => 'report_args', count => 1},
  888.   '\text' => {Type => 'report_args', count => 1},
  889.   '\operatorname' => {Type => 'report_args', count => 1},
  890.   '\operatornamewithlimits' => {Type => 'report_args', count => 1},
  891.   '^' => {Type => 'report_args', count => 1, 
  892.       lookahead => \%super_sub_lookahead },
  893.   '_' => {Type => 'report_args', count => 1, 
  894.       lookahead => \%super_sub_lookahead },
  895.   '\em' => {Type => 'local'},
  896.   '\bold' => {Type => 'local'},
  897.   '\it' => {Type => 'local'},
  898.   '\rm' => {Type => 'local'},
  899.   '\mathcal' => {Type => 'local'},
  900.   '\mathfrak' => {Type => 'local'},
  901.   '\mathbb' => {Type => 'local'},
  902.   '\\\\' => {'class' => 'Text::TeX::Separator'},
  903.   '&' => {'class' => 'Text::TeX::Separator'},
  904. ##############   I NEVER USE ANYTHING BELOW THIS LINE!! -Ak   ##################
  905.   my $i = 0;
  906.   @symbol = (
  907.        (undef) x 8,        # 1st row
  908.        (undef) x 8,
  909.        (undef) x 8,        # 2nd row
  910.        (undef) x 8,
  911.        undef, undef, '\forall', undef, '\exists', undef, undef, '\???', # 3rd: symbols
  912.        (undef) x 8,
  913.        (undef) x 8,     # 4th: numbers and symbols
  914.        (undef) x 8,
  915.        '\???', ( map {"\\$_"} 
  916.          qw(Alpha Beta Chi Delta Epsilon Phi Gamma 
  917.          Eta Iota vartheta Kappa Lambda Mu Nu Omicron 
  918.          Pi Theta Rho Sigma Tau Ypsilon varsigma Omega
  919.          Xi Psi Zeta)), undef, '\therefore', undef, '\perp', undef,
  920.        undef, ( map {"\\$_"} 
  921.             qw(alpha beta chi delta varepsilon phi gamma
  922.            eta iota varphi kappa lambda mu nu omicron
  923.            pi theta rho sigma tau ypsilon varpi omega
  924.            xi psi zeta)), undef, undef, undef, undef, undef,
  925.        (undef) x 8,        # 9st row
  926.        (undef) x 8,
  927.        (undef) x 8,        # 10nd row
  928.        (undef) x 8,
  929.        undef, undef, undef, '\leq', undef, '\infty', undef, undef, # 11th row
  930.        undef, undef, undef, undef, '\from', undef, '\to', undef,
  931.        '\circ', '\pm', undef, '\geq', '\times', undef, '\partial', '\bullet', # 12th row
  932.        undef, '\neq', '\equiv', '\approx', '\dots', '\mid', '\hline', undef,
  933.        '\Aleph', undef, undef, undef, '\otimes', '\oplus', '\empty', '\cap', # 13th row
  934.        '\cup', undef, undef, undef, undef, undef, '\in', '\notin',
  935.        undef, '\nabla', undef, undef, undef, '\prod', undef, '\cdot', # 14th row
  936.        undef, '\wedge', '\vee', undef, undef, undef, undef, undef,
  937.        undef, '\<', undef, undef, undef, '\sum', undef, undef, # 15th row
  938.        (undef) x 8,
  939.        undef, '\>', '\int', (undef) x 5, # 16th row
  940.        (undef) x 8,
  941.       );
  942.   for (@symbol) {
  943.     $xfont{$_} = ['symbol', chr($i)] if defined $_;
  944.     $i++;
  945. # This list was autogenerated by the following script:
  946. # Some handediting is required since MSSYMB.TEX is obsolete.
  947. ## Usage is like:
  948. ##        extract_texchar.pl  PLAIN.TEX MSSYMB.TEX
  949. ##$family = shift;
  950. #%fonts = (2 => "cmsy", 3 => "cmex", '\\msx@' => msam, '\\msy@' => msbm, );
  951. #while (defined ($_ = <ARGV>)) {
  952. #  $list{$fonts{$2}}[hex $3] = $1
  953. #    if /^\s*\\mathchardef(\\\w+)=\"\d([23]|\\ms[xy]\@)([\da-fA-F]+)\s+/o;
  954. #for $font (keys %list) {
  955. #  print "\@$font = (\n  ";
  956. #  for $i (0 .. $#{$list{$font}}/8) {
  957. #    print join ', ', map {packit($_)} @{$list{$font}}[ 8*$i .. 8*$i+7 ];
  958. #    print ",\n  ";
  959. #  print ");\n\n";
  960. #sub packit {
  961. #  my $cs = shift;
  962. #  if (defined $cs) {
  963. #    #$cs =~ s/\\\\/\\\\\\\\/g;
  964. #    "'$cs'";
  965. #  } else {
  966. #    'undef';
  967. @cmsy = (
  968.   undef, '\cdotp', '\times', '\ast', '\div', '\diamond', '\pm', '\mp',
  969.   '\oplus', '\ominus', '\otimes', '\oslash', '\odot', '\bigcirc', '\circ', '\bullet',
  970.   '\asymp', '\equiv', '\subseteq', '\supseteq', '\leq', '\geq', '\preceq', '\succeq',
  971.   '\sim', '\approx', '\subset', '\supset', '\ll', '\gg', '\prec', '\succ',
  972.   '\leftarrow', '\rightarrow', '\uparrow', '\downarrow', '\leftrightarrow', '\nearrow', '\searrow', '\simeq',
  973.   '\Leftarrow', '\Rightarrow', '\Uparrow', '\Downarrow', '\Leftrightarrow', '\nwarrow', '\swarrow', '\propto',
  974.   '\prime', '\infty', '\in', '\ni', '\bigtriangleup', '\bigtriangledown', '\not', '\mapstochar',
  975.   '\forall', '\exists', '\neg', '\emptyset', '\Re', '\Im', '\top', '\perp',
  976.   '\aleph', undef, undef, undef, undef, undef, undef, undef,
  977.   undef, undef, undef, undef, undef, undef, undef, undef,
  978.   undef, undef, undef, undef, undef, undef, undef, undef,
  979.   undef, undef, undef, '\cup', '\cap', '\uplus', '\wedge', '\vee',
  980.   '\vdash', '\dashv', undef, undef, undef, undef, undef, undef,
  981.   '\langle', '\rangle', '\mid', '\parallel', undef, undef, '\setminus', '\wr',
  982.   undef, '\amalg', '\nabla', '\smallint', '\sqcup', '\sqcap', '\sqsubseteq', '\sqsupseteq',
  983.   undef, '\dagger', '\ddagger', undef, '\clubsuit', '\diamondsuit', '\heartsuit', '\spadesuit',
  984. @cmex = (
  985.   undef, undef, undef, undef, undef, undef, undef, undef, # 0-7
  986.   undef, undef, undef, undef, undef, undef, undef, undef, # 8-15
  987.   undef, undef, undef, undef, undef, undef, undef, undef, # 16-23
  988.   undef, undef, undef, undef, undef, undef, undef, undef, # 24-31
  989.   undef, undef, undef, undef, undef, undef, undef, undef, # 32-39
  990.   undef, undef, undef, undef, undef, undef, undef, undef, # 40-47
  991.   undef, undef, undef, undef, undef, undef, undef, undef, # 48-55
  992.   undef, undef, undef, undef, undef, undef, undef, undef, # 56-64
  993.   undef, undef, undef, undef, undef, undef, '\bigsqcup', undef,    # 64-71
  994.   '\ointop', undef, '\bigodot', undef, '\bigoplus', undef, '\bigotimes', undef,    # 72-79
  995.   '\sum', '\prod', '\intop', '\bigcup', '\bigcap', '\biguplus', '\bigwedge', '\bigvee',    # 80-87
  996.   undef, undef, undef, undef, undef, undef, undef, undef,
  997.   '\coprod', undef, undef, undef, undef, undef, undef, undef,
  998. @msam = (
  999.   '\boxdot', '\boxplus', '\boxtimes', '\square', '\blacksquare', '\centerdot', '\lozenge', '\blacklozenge',
  1000.   '\circlearrowright', '\circlearrowleft', '\rightleftharpoons', '\leftrightharpoons', '\boxminus', '\Vdash', '\Vvdash', '\vDash',
  1001.   '\twoheadrightarrow', '\twoheadleftarrow', '\leftleftarrows', '\rightrightarrows', '\upuparrows', '\downdownarrows', '\upharpoonright', '\downharpoonright',
  1002.   '\upharpoonleft', '\downharpoonleft', '\rightarrowtail', '\leftarrowtail', '\leftrightarrows', '\rightleftarrows', '\Lsh', '\Rsh',
  1003.   '\rightsquigarrow', '\leftrightsquigarrow', '\looparrowleft', '\looparrowright', '\circeq', '\succsim', '\gtrsim', '\gtrapprox',
  1004.   '\multimap', '\therefore', '\because', '\doteqdot', '\triangleq', '\precsim', '\lesssim', '\lessapprox',
  1005.   '\eqslantless', '\eqslantgtr', '\curlyeqprec', '\curlyeqsucc', '\preccurlyeq', '\leqq', '\leqslant', '\lessgtr',
  1006.   '\backprime', undef, '\risingdotseq', '\fallingdotseq', '\succcurlyeq', '\geqq', '\geqslant', '\gtrless',
  1007.   '\sqsubset', '\sqsupset', '\vartriangleright', '\vartriangleleft', '\trianglerighteq', '\trianglelefteq', '\bigstar', '\between',
  1008.   '\blacktriangledown', '\blacktriangleright', '\blacktriangleleft', undef, undef, '\vartriangle', '\blacktriangle', '\triangledown',
  1009.   '\eqcirc', '\lesseqgtr', '\gtreqless', '\lesseqqgtr', '\gtreqqless', '\yen', '\Rrightarrow', '\Lleftarrow',
  1010.   '\checkmark', '\veebar', '\barwedge', '\doublebarwedge', '\angle', '\measuredangle', '\sphericalangle', '\varpropto',
  1011.   '\smallsmile', '\smallfrown', '\Subset', '\Supset', '\Cup', '\Cap', '\curlywedge', '\curlyvee',
  1012.   '\leftthreetimes', '\rightthreetimes', '\subseteqq', '\supseteqq', '\bumpeq', '\Bumpeq', '\lll', '\ggg',
  1013.   '\ulcorner', '\urcorner', '\circledR', '\circledS', '\pitchfork', '\dotplus', '\backsim', '\backsimeq',
  1014.   '\llcorner', '\lrcorner', '\maltese', '\complement', '\intercal', '\circledcirc', '\circledast', '\circleddash',
  1015. @msbm = (
  1016.   '\lvertneqq', '\gvertneqq', '\nleq', '\ngeq', '\nless', '\ngtr', '\nprec', '\nsucc',
  1017.   '\lneqq', '\gneqq', '\nleqslant', '\ngeqslant', '\lneq', '\gneq', '\npreceq', '\nsucceq',
  1018.   '\precnsim', '\succnsim', '\lnsim', '\gnsim', '\nleqq', '\ngeqq', '\precneqq', '\succneqq',
  1019.   '\precnapprox', '\succnapprox', '\lnapprox', '\gnapprox', '\nsim', '\ncong', undef, undef,
  1020.   '\varsubsetneq', '\varsupsetneq', '\nsubseteqq', '\nsupseteqq', '\subsetneqq', '\supsetneqq', '\varsubsetneqq', '\varsupsetneqq',
  1021.   '\subsetneq', '\supsetneq', '\nsubseteq', '\nsupseteq', '\nparallel', '\nmid', '\nshortmid', '\nshortparallel',
  1022.   '\nvdash', '\nVdash', '\nvDash', '\nVDash', '\ntrianglerighteq', '\ntrianglelefteq', '\ntriangleleft', '\ntriangleright',
  1023.   '\nleftarrow', '\nrightarrow', '\nLeftarrow', '\nRightarrow', '\nLeftrightarrow', '\nleftrightarrow', '\divideontimes', '\varnothing',
  1024.   '\nexists', undef, undef, undef, undef, undef, undef, undef,
  1025.   undef, undef, undef, undef, undef, undef, undef, undef,
  1026.   undef, undef, undef, undef, undef, undef, undef, undef,
  1027.   undef, undef, undef, undef, undef, undef, undef, undef,
  1028.   undef, undef, undef, undef, undef, undef, '\mho', '\eth',
  1029.   '\eqsim', '\beth', '\gimel', '\daleth', '\lessdot', '\gtrdot', '\ltimes', '\rtimes',
  1030.   '\shortmid', '\shortparallel', '\smallsetminus', '\thicksim', '\thickapprox', '\approxeq', '\succapprox', '\precapprox',
  1031.   '\curvearrowleft', '\curvearrowright', '\digamma', '\varkappa', undef, '\hslash', '\hbar', '\backepsilon',
  1032. # Temporary workaround against Tk's \n (only cmsy contains often-used \otimes):
  1033. $cmsy[ord "\n"] = undef;
  1034. for $font (qw(cmsy cmex msam msbm)) {
  1035.   for $num (0 .. $#{$font}) {
  1036.     $xfont{$$font[$num]} = [$font, chr($num)] if defined $$font[$num];
  1037. %aliases = qw(
  1038.           \int \intop \oint \ointop \restriction \upharpoonright
  1039.           \Doteq \doteqdot \doublecup \Cup \doublecap \Cap
  1040.           \llless \lll \gggtr \ggg \lnot \neg \land \wedge
  1041.           \lor \vee \le \leq \ge \geq \owns \ni \gets \leftarrow
  1042.           \to \rightarrow \< \langle \> \rangle \| \parallel
  1043.          );
  1044. for $from (keys %aliases) {
  1045.   $xfont{$from} = $xfont{$aliases{$from}} if exists $xfont{$aliases{$from}};
  1046. # Autoload methods go after =cut, and are processed by the autosplit program.
  1047. __END__
  1048. =head1 NAME
  1049. Text::TeX -- Perl module for parsing of C<TeX>.
  1050. =head1 SYNOPSIS
  1051.   use Text::TeX;
  1052.   sub report {
  1053.     my($eaten,$txt) = (shift,shift);
  1054.     print "Comment: `", $eaten->[1], "'\n" if defined $eaten->[1];
  1055.     print "@{$txt->{waitfors}} ", ref $eaten, ": `", $eaten->[0], "'";
  1056.     if (defined $eaten->[3]) {
  1057.       my @arr = @{ $eaten->[3] };
  1058.       foreach (@arr) {
  1059.     print " ", $_->print;
  1060.       }
  1061.     }
  1062.     print "\n";
  1063.   my $file = new Text::TeX::OpenFile 'test.tex',
  1064.     'defaultact' => \&report;
  1065.   $file->process;
  1066. =head1 DESCRIPTION
  1067. A new C<TeX> parser is created by
  1068.   $file = new Text::TeX::OpenFile $filename, attr1 => $val1, ...;
  1069. $filename may be C<undef>, in this case the text to parse may be
  1070. specified in the attribute C<string>.
  1071. Recognized attributes are:
  1072. =over 12
  1073. =item C<string>
  1074. contains the text to parse before parsing $filename.
  1075. =item C<defaultact>
  1076. denotes a procedure to submit C<output tokens> to.
  1077. =item C<tokens>
  1078. gives a hash of C<descriptors> for C<input token>. A sane default is
  1079. provided.
  1080. =back
  1081. A call to the method C<process> launches the parser.
  1082. =head2 Tokenizer
  1083. When the parser is running, it processes input stream by splitting it
  1084. into C<input tokens> using some I<heuristics> similar to the actual
  1085. rules of TeX tokenizer. However, since it does not use I<the exact
  1086. rules>, the resulting tokens may be wrong if some advanced TeX command
  1087. are used, say, the character classes are changed.
  1088. This should not be of any concern if the stream in question is a
  1089. "user" file, but is important for "packages".
  1090. =head2 Digester
  1091. The processed C<input tokens> are handled to the digester, which
  1092. handles them according to the provided C<tokens> attribute.
  1093. =head2 C<tokens> attribute
  1094. This is a hash reference which describes how the C<input tokens>
  1095. should be handled. A key to this hash is a literal like C<^> or
  1096. C<\fraction>. A value should be another hash reference, with the
  1097. following keys recognized:
  1098. =over 7
  1099. =item class
  1100. Into which class to bless the token. Several predefined classes are
  1101. provided. The default is C<Text::TeX::Token>.
  1102. =item Type
  1103. What kind of special processing to do with the input after the
  1104. C<class> methods are called. Recognized C<Type>s are:
  1105. =over 10
  1106. =item report_args
  1107. When the token of this C<Type> is encountered, it is converted into
  1108. C<Text::Tex::BegArgsToken>. Then the arguments are processed as usual,
  1109. and an C<output token> of type C<Text::Tex::ArgToken> is inserted
  1110. between them. Finally, after all the arguments are processed, an
  1111. C<output token> C<Text::Tex::EndArgsToken> is inserted.
  1112. The first element of these simulated C<output tokens> is an array
  1113. reference with the first element being the initial C<output token>
  1114. which generated this sequence. The second element of the internal
  1115. array is the number of arguments required by the C<input token>. The
  1116. C<Text::Tex::ArgToken> token has a third element, which is the ordinal
  1117. of the argument which ends immediately before this token.
  1118. If requested, a token C<Text::Tex::LookAhead> may be returned instead
  1119. of C<Text::Tex::EndArgsToken>. The additional elements of
  1120. C<$token->[0]> are: the reference to the corresponding C<lookahead>
  1121. attribute, the relevant key (text of following token) and the
  1122. corresponding value.
  1123. In such a case the input token which was looked-ahead would generate
  1124. an output token of type C<Text::Tex::BegArgsTokenLookedAhead> (if it
  1125. usually generates C<Text::Tex::BegArgsToken>).
  1126. =item local
  1127. Means that these macro introduces a local change, which should be
  1128. undone at the end of enclosing block. At the end of the block an
  1129. output event C<Text::TeX::EndLocal> is delivered, with C<$token->[0]>
  1130. being the output token for the I<local> event starting.
  1131. Useful for font switching. 
  1132. =back
  1133. =back
  1134. Some additional keys may be recognized by the code for the particular
  1135. C<class>.
  1136. =over 12
  1137. =item C<count>
  1138. number of arguments to the macro.
  1139. =item C<waitfor>
  1140. gives the matching token for a I<starting delimiter> token.
  1141. =item C<eatargs>
  1142. number of tokens to swallow literally and put into the relevant slot
  1143. of the C<output token>. The surrounding braces are stripped.
  1144. =item C<selfmatch>
  1145. is used with C<eatargs==1>. Denotes that the matching token is also
  1146. C<eatargs==1>, and the swallowed tokens should coinside (like with
  1147. C<\begin{blah} ... \end{blah}>).
  1148. =item C<lookahead>
  1149. is a hash with keys being texts of tokens which need to be treated
  1150. specially after the end of arguments for the current token. If the
  1151. corresponding text follows the token indeed, a token
  1152. C<Text::Tex::LookAhead> is returned instead of
  1153. C<Text::Tex::EndArgsToken>.
  1154. =back
  1155. =head2 Symbol font table
  1156. The hash %Text::TeX::xfont contains the translation table from TeX
  1157. tokens into the corresponding font elements. The values are array
  1158. references of the form C<[fontname, char]>, Currently the only font
  1159. supported is C<symbol>.
  1160. =head1 AUTHOR
  1161. Ilya Zakharevich, ilya@math.ohio-state.edu
  1162. =head1 SEE ALSO
  1163. perl(1).
  1164.