home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / lyx21041.zip / XFree86 / lib / X11 / lyx / reLyX / ReadCommands.pm (.txt) < prev    next >
LaTeX Document  |  1999-03-30  |  16KB  |  362 lines

  1. # This file is part of reLyX
  2. # Copyright (c) 1998-9 Amir Karger karger@post.harvard.edu
  3. # You are free to use and modify this code under the terms of
  4. # the GNU General Public Licence version 2 or later.
  5. package ReadCommands;
  6. # Read a file containing LaTeX commands and their syntax
  7. # Also, read a file containing LyX layouts and their LaTeX equivalents
  8. use strict; 
  9. # Variables needed by other modules
  10. # ToLayout is a ref to a hash which contains LyX layouts & LaTeX equivalents
  11. # regular_env is a list of environments that have "reLyXable" LaTeX in them
  12. # math_trans is a hash of math commands and what they're translated to
  13. use vars qw($ToLayout @regular_env %math_trans);
  14. #    CommandHash is the hash of LaTeX commands and their syntaxes which
  15. # this package builds.
  16. my %CommandHash;
  17. # Name of the environment containing names of regular environments :)
  18. my $regenv_name = "reLyXre";
  19. # Name of environment containing translations of math commands
  20. my $math_trans_name = "reLyXmt";
  21. # Variables set when in the above environments
  22. my ($in_regular_env, $in_math_trans);
  23. my @Environments = qw($regenv_name $math_trans_name);
  24. # This word in a command's argument (in the syntax file) means that reLyX
  25. # should translate that argument as regular LaTeX
  26. my $Translate_Word = "translate";
  27. #########################  READ COMMAND SYNTAX  ################################
  28. sub read_syntax_files {
  29. # This subroutine calls the TeX parser & translator to read LaTeX syntax file(s)
  30. #    It sets the list of "regular" environments, environments which aren't known
  31. # by reLyX, but which contain text that reLyX can translate.
  32. #    It also reads math commands which should be translated (e.g., \sp -> ^)
  33. # @_ contains the syntax file(s) to read
  34.     my @syntaxfiles = @_;
  35. # Before anything else, set the package-wide variables based on the
  36. #    user-given flags
  37.     # opt_d is set to 1 if '-d' option given, else (probably) undefined
  38.     my $debug_on = (defined($main::opt_d) && $main::opt_d);
  39.     # opt_r is a group of environments to copy like regular text
  40.     # If opt_r wasn't given, then there are no such special environments
  41.     @regular_env = (defined $main::opt_r ? (split(/,/, $main::opt_r)) : () );
  42.     # The only important commands to pass are \begin and \end, so that the
  43.     # parser realizes they start/end environments, as opposed to being
  44.     # regular old tokens.
  45.     my %MyTokens = ( '{' => $Text::TeX::Tokens{'{'},
  46.                      '}' => $Text::TeX::Tokens{'}'},
  47.                      '\begin' => $Text::TeX::Tokens{'\begin'},
  48.                      '\end' => $Text::TeX::Tokens{'\end'},
  49.     );
  50.     my $InFileName;
  51.     foreach $InFileName (@syntaxfiles) {
  52.     die "could not find syntax file $InFileName" unless -e $InFileName;
  53.     my $zzz=$debug_on ? "from $InFileName " :"";
  54.     warn "Reading LaTeX command syntax $zzz\n";
  55.     # Open the file to turn into LyX.
  56.     my $infile = new Text::TeX::OpenFile $InFileName,
  57.         'defaultact' => \&read_commands,
  58.         'tokens' => \%MyTokens;
  59.     # When we start (each file), we're not reading regular environments yet
  60.     $in_regular_env = 0;
  61.     # Process the file
  62.     $infile->process;
  63.     }
  64.     if ($debug_on) {
  65.     print "Regular environments: @regular_env\n";
  66.     my @mathkeys = keys(%math_trans);
  67.     print "     Math command     |  translation\n" if @mathkeys;
  68.     foreach (@mathkeys) { printf("%20s       %s\n",$_,$math_trans{$_}) }
  69.     }
  70.     #warn "Done reading commands\n";
  71.     return;
  72. } # end subroutine call_parser
  73. sub read_commands {
  74. # This subroutine is called by Text::TeX::process
  75. # Arg0 is the token we just ate
  76. # Arg1 is the file object we're reading from
  77. #    We create a hash, where each command is a key. The value is just a string
  78. # of zero or more 'o' and 'r' characters. Each 'o' stands for an optional
  79. # argument, 'r' stands for a required argument. 'R' stands for a required
  80. # argument whose text will be regular LaTeX, e.g., the argument to \mbox{}
  81. #    In addition, the $regenv_name environment contains
  82. # regular environments, like those input with the -r option.
  83. #    Note that if a command is found more than once, then it wil be overwritten.
  84. # This is a feature. This way, a user-defined syntax file can overwrite the
  85. # argument list found in the default syntax file.
  86.     my ($token,$fileobject) = (shift,shift);
  87.     my $type = ref($token);
  88.     $type =~ s/^Text::TeX::// or die "unknown token type $type from Text::TeX";
  89.     #print $token->exact_print, unless $type eq "Paragraph";
  90.     #print $token->comment if $token->comment;
  91.     # Because there's no token list, ALL tokens will be
  92.     #    Paragraph, Text, or Token
  93.     SWITCH: for ($type) {
  94.        # Handle blank lines.
  95.         if (/Paragraph/) {
  96.         # don't do anything
  97.         last SWITCH;
  98.         } elsif (/^Token/) {
  99.         # Comment in its own paragraph... skip
  100.         last SWITCH unless defined($token->print);
  101.         if ($in_math_trans) { # read translations of math commands
  102.             my $key = $token->print;
  103.         # Translation is whatever's in the argument to the token
  104.         # (There might be multiple tokens in there)
  105.         my @vals = $fileobject->eatBalanced->contents;
  106.         my $val = join ("", map {$_->exact_print} @vals);
  107.         $math_trans{$key} = $val;
  108.         
  109.         } else { # regular portion of syntax file
  110.         my ($dum2);
  111.         my $args = "";
  112.         # read while there are arguments
  113.         while (($dum2 = $fileobject->lookAheadToken) &&
  114.                ($dum2 =~ /^[[{]$/)) {
  115.             if ($dum2 eq '[') { #eat optional argument - assumed simple
  116.             $fileobject->eatOptionalArgument;
  117.             $args .= "o";
  118.             } else {
  119.             my $tok = $fileobject->eatBalanced or warn "bad group";
  120.             if ($tok->exact_print eq $Translate_Word) {
  121.                 $args .= "R";
  122.             } else {
  123.                 $args .= "r";
  124.             } # end if $dummy = [{
  125.         } # done reading command
  126.         $CommandHash{$token->print} = $args;
  127.         } # in math trans env or regular token?
  128.         last SWITCH;
  129.         } elsif (/^Begin::Group::Args/) {
  130.         my $env = $token->environment;
  131.         CASE: {
  132.         $in_regular_env = 1, last CASE if $env eq $regenv_name;
  133.         $in_math_trans = 1,  last CASE if $env eq $math_trans_name;
  134.         warn "Unknown environment $env in syntax file";
  135.         }
  136.         } elsif (/^End::Group::Args/) {
  137.         my $env = $token->environment;
  138.         CASE: {
  139.         $in_regular_env = 0, last CASE if $env eq $regenv_name;
  140.         $in_math_trans = 0,  last CASE if $env eq $math_trans_name;
  141.         warn "Unknown environment $env in syntax file";
  142.         }
  143.         } elsif (/^Text/) {
  144.         # don't do anything unless we're reading environments
  145.         if ($in_regular_env) {
  146.         my @new_envs = (split(/\s+/, $token->print));
  147.         @new_envs = grep ($_, @new_envs); # remove empty elements
  148.         push @regular_env,@new_envs;
  149.         }
  150.         last SWITCH;
  151.         } else {
  152.         die "unexpected token type $type";
  153.     } # end SWITCH
  154. } # end sub read_commands
  155. sub Merge {
  156. #    This sub creates a token list (which could be used to call a Text::TeX
  157. # parser) from %CommandHash, and merges it with the input token list
  158. #    If a command takes any required arguments, it will be a report_args,
  159. # but if it just takes an optional argument, it can stay a regular old token.
  160. # In either case, we insert a new field, "relyx_args", into the token list,
  161. # which is the expected order of arguments for that command. Even if there
  162. # are no args, we insert an empty relyx_args, so that we can differentiate
  163. # between a truly unknown token and a known token which takes no args.
  164. # We don't allow a new command to override a command that already exists in
  165. # OldHash, i.e., one that was defined explicitly in the calling sub.
  166. # Arg0 is a (reference to an) existing token list
  167.     my $OldHashRef = shift;
  168.     foreach (keys %CommandHash) {
  169.     my $val = $CommandHash{$_};
  170.     my ($entry, $count, @foo);
  171.         if (!exists $OldHashRef->{$_}) {
  172.         if ($count = scalar(@foo = ($val =~ /r/gi))) {
  173.         # TeX.pm will make this a TT::BegArgsToken and $count-1
  174.         #    TT::ArgTokens, followed by a TT::EndArgsToken
  175.         $OldHashRef->{$_} = {"Type" => 'report_args',
  176.                              "count" => $count,
  177.                      "relyx_args" => $val};
  178.         } else { # only non-required args
  179.         # Make it a regular TT::Token, but write relyx_args
  180.         #    (even if $val is "")
  181.         $OldHashRef->{$_} = {"relyx_args" => $val};
  182.         }
  183.     } # end foreach
  184. } # end sub Merge
  185. ############################  READ LAYOUTS  ####################################
  186. sub read_layout_files {
  187. # This subroutine reads a textclass-specific layout file and all files
  188. # included in that file.
  189. #    It sets up the layout hash table. For each environment, it describes which
  190. # layout that environment refers to. It does the same for macros which
  191. # begin LyX layouts (e.g., \section)
  192. #    If we read a command that's not already in CommandHash, it means that this
  193. # layout has some commands that aren't in syntax.default. If so, we ASSUME
  194. # that the command takes just one required argument, and put it in
  195. # CommandHash, so that &Merge will eventually put these commands into the
  196. # token lists.
  197. # TODO: we actually need to allow more sophisticated stuff. E.g. \foilhead
  198. # is converted to Foilhead or ShortFoilHead (foils.layout) depending on whether
  199. # the command has "r" or "or" arguments. Reading LatexParam (if it exists)
  200. # can help us with this.
  201. # Default is "r". Just unshift other args as you read them, since latexparam
  202. # is put in between macro & the argument
  203. # TODO: We need to store ToLayout s.t. we can have > 1 layout per command.
  204. # Maybe by default just have one layout, but if (ref(layout)) then read
  205. # more args & thereby figure out which layout?
  206. # Arg0 is the name of the documentclass
  207.     use FileHandle;
  208.     use File::Basename;
  209.     my $doc_class = shift;
  210.     my @filestack;
  211.     my $fh;
  212.     my $line;
  213.     # ToLayout{latexname} stores layout; so ReversHash{layout} = latexname
  214.     my %ReverseHash;
  215.     my $debug_on = (defined($main::opt_d) && $main::opt_d);
  216.     # look for layout file in $HOME/.lyx first, then system layouts directory
  217.     my $searchname = "$doc_class.layout";
  218.     my @searchdirs = ();
  219.     my $personal_layout = "$main::dot_lyxdir/layouts";
  220.     push(@searchdirs,$personal_layout) if -e $personal_layout;
  221.     my $system_layout = "$main::lyxdir/layouts";
  222.     # I guess this won't exist if running reLyX without installing...
  223.     # Of course, in that case, this will probably break
  224.     push(@searchdirs,$system_layout) if -e $system_layout;
  225.     my @foundfiles = grep(-e "$_/$searchname", @searchdirs) or
  226.           die "Cannot find layout file $searchname in dir(s) @searchdirs";
  227.     my $LayoutFileName = "$foundfiles[0]/$searchname"; # take first one we found
  228.     $fh = new FileHandle;
  229.     $fh->open ("<$LayoutFileName");
  230.     my $zzz=$debug_on ? "$LayoutFileName" :"";
  231.     warn "Reading layout file $zzz\n";
  232.     push @filestack, $fh;
  233.     # Read the layout file!
  234.     my ($lyxname, $latexname, $latextype, $latexparam, $keepempty);
  235.     my $fname;
  236.     while() {
  237.     # Read a line. If eof, pop the filestack to return to the file
  238.     #    that included this file *or* finish if the stack's empty
  239.         unless (defined ($line = <$fh>)) {
  240.         $fh->close;
  241.         pop @filestack;
  242.         last unless ($#filestack+1); # finish when stack is empty
  243.         $fh = $filestack[-1];
  244.         next; # read another line from the "calling" file
  245.     # Skip blank lines
  246.     next if $line =~ /^\s*$/;
  247.     # Split the line. Use limit 2 since there may be whitespace in 2nd term
  248.     my ($field_name, $field_stuff) = split(' ', $line, 2);
  249.     $field_name = lc($field_name); # LyX is case insensitive for fields
  250.     if (defined($field_stuff)) {
  251.         $field_stuff =~ s/^\"(.*)\"/$1/;
  252.         chomp ($field_stuff);
  253.         # Since split is limited to 2 fields, there may be extra whitespace
  254.         # at end. LyX breaks on a "\layout Abstract " command!
  255.         $field_stuff =~ s/\s*$//;
  256.     # This set of ifs deals with lines outside a style definition
  257.     if ($field_name eq "style") { # start a style definition
  258.         $lyxname = $field_stuff;
  259.         # Styles in LyX have spaces, but _ in layout files
  260.         $lyxname =~ s/_/ /g;
  261.         $latexname  = ""; # make sure these variables are unset
  262.         $latextype  = "";
  263.         $latexparam = "";
  264.         $keepempty = 0;
  265.     } elsif ($field_name eq "input") { #include a file
  266.         $searchname = $field_stuff;
  267.         @foundfiles = grep(-e "$_/$searchname", @searchdirs) or
  268.           die "Cannot find layout file $searchname in dir(s) @searchdirs";
  269.         $fname = "$foundfiles[0]/$searchname"; # take first one we found
  270.         $fh = new FileHandle;
  271.         push @filestack, $fh;
  272.         $fh->open("<$fname");
  273.         print "Reading included layout file $fname\n" if $debug_on;
  274.     next unless $lyxname; # not w/in style definition
  275.     # This set of ifs deals with lines within a Style definition
  276.     if ($field_name eq "latexname") {
  277.         $latexname = $field_stuff;
  278.         next;
  279.     } elsif ($field_name eq "latexparam") {
  280.         #$dum = $field_stuff;
  281.         $latexparam = $field_stuff;
  282.         next;
  283.     } elsif ($field_name eq "latextype") {
  284.         $latextype = $field_stuff;
  285.         next;
  286.     } elsif ($field_name eq "keepempty") {
  287.         $keepempty = $field_stuff;
  288.         next;
  289.     } elsif ($field_name eq "copystyle") { # copy an existing style
  290.         # "if" is necessary in case someone tries "CopyStyle Standard"
  291.         if (exists $ReverseHash{$field_stuff}) {
  292.         my $layref = $ReverseHash{$field_stuff};
  293.         $latexname  = $layref->{"name"};
  294.         $latextype  = $layref->{"type"};
  295.         $latexparam = $layref->{"param"};
  296.         $keepempty = $layref->{"keepempty"};
  297.         }
  298.     # When you get to the end of a definition, create hash table entries
  299.     #    (if you've found the right information)
  300.     } elsif ($field_name eq "end") {
  301.         if ($latextype and $latexname) {
  302.         # Create reverse hash entry (needed for CopyStyle)
  303.         # Do it before making modifications to $latexname, e.g.
  304.         $ReverseHash{$lyxname} = {"name"  => $latexname,
  305.                                   "type"  => $latextype,
  306.                       "param" => $latexparam,
  307.                       "keepempty" => $keepempty,
  308.                       };
  309.         my ($nest, $skip) = (0,0);
  310.         for ($latextype) { # make $_=$latextype
  311.             if (/^Command/) {
  312.             # Macros need a '\' before them. Environments don't
  313.             $latexname = '\\' . $latexname;
  314.             # Create the command if it wasn't in syntax.default
  315.             unless (exists $CommandHash{$latexname}) {
  316.                 $CommandHash{$latexname} = "r";
  317.             } elsif (/^Environment/) {
  318.             $nest = 1;
  319.             } elsif (/Item_Environment/i || /List_Environment/) {
  320.             $nest = 1;
  321.             # layout Standard has LatexType Paragraph. It shouldn't
  322.             #    have any hash entry at all
  323.             } elsif (/^Paragraph$/) {
  324.                 $skip = 1;
  325.             } else {
  326.             warn "unknown LatexType $latextype" . 
  327.                  "for $latexname (layout $lyxname)!\n";
  328.             }
  329.         # Handle latexparam, if any
  330. #        if ($dum =~ s/^"(.*)"/$1/) { # newer layout file syntax
  331. #        while ($dum =~ /^[[{]/) {
  332. #            $dum =~ s/\[.*?\]// && ($latexargs = "o$latexargs") or
  333. #            $dum =~ s/\{.*?\}// && ($latexargs = "r$latexargs");
  334. #        warn "leftover LatexParam stuff $dum" if $dum;
  335. #        } else { # 0.12.0
  336. #        if ($latextype eq "Command") {optarg}
  337. #        else {req. arg}
  338. #        }
  339.         } #end for
  340.         # Create the hash entry
  341.         unless ($skip) {
  342.             $ToLayout->{$latexname} = {"layout" => $lyxname,
  343.                         "nestable" => $nest,
  344.                         "keepempty" => $keepempty};
  345.         # Now that we've finished the style, unset $lyxname so that
  346.         #     we'll skip lines until the next style definition
  347.         $lyxname = "";
  348.         } # end if ($latextype and $latexname)
  349.     } # end if on $line
  350.         
  351.     } #end while
  352. ## Print every known layout
  353. #    print "     LatexName            Layout        Keepempty?\n";
  354. #    foreach (sort keys %$ToLayout) {
  355. #        printf "%20s%15s     %1d\n",$_,$ToLayout->{$_}{'layout'},
  356. #              $ToLayout->{$_}{'keepempty'};
  357. #    };
  358.     #warn "Done reading layout files\n";
  359.     return;
  360. } # end sub read_layout_files
  361. 1; # return TRUE to calling routine
  362.