Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ReadCommands.pm   Sprache: unbekannt

 
# This file is part of reLyX
# Copyright (c) 1998-9 Amir Karger karger@post.harvard.edu
# You are free to use and modify this code under the terms of
# the GNU General Public Licence version 2 or later.

package ReadCommands;
# Read a file containing LaTeX commands and their syntax
# Also, read a file containing LyX layouts and their LaTeX equivalents

use strict; 

# Variables needed by other modules
# ToLayout is a ref to a hash which contains LyX layouts & LaTeX equivalents
# regular_env is a list of environments that have "reLyXable" LaTeX in them
# math_trans is a hash of math commands and what they're translated to
use vars qw($ToLayout @regular_env %math_trans);

#    CommandHash is the hash of LaTeX commands and their syntaxes which
# this package builds.
my %CommandHash;

# Name of the environment containing names of regular environments :)
my $regenv_name = "reLyXre";
# Name of environment containing translations of math commands
my $math_trans_name = "reLyXmt";
# Variables set when in the above environments
my ($in_regular_env, $in_math_trans);
my @Environments = qw($regenv_name $math_trans_name);

# This word in a command's argument (in the syntax file) means that reLyX
# should translate that argument as regular LaTeX
my $Translate_Word = "translate";

#########################  READ COMMAND SYNTAX  ################################
sub read_syntax_files {
# This subroutine calls the TeX parser & translator to read LaTeX syntax file(s)
#    It sets the list of "regular" environments, environments which aren't known
# by reLyX, but which contain text that reLyX can translate.
#    It also reads math commands which should be translated (e.g., \sp -> ^)
#
# @_ contains the syntax file(s) to read

    my @syntaxfiles = @_;

# Before anything else, set the package-wide variables based on the
#    user-given flags
    # opt_d is set to 1 if '-d' option given, else (probably) undefined
    my $debug_on = (defined($main::opt_d) && $main::opt_d);

    # opt_r is a group of environments to copy like regular text
    # If opt_r wasn't given, then there are no such special environments
    @regular_env = (defined $main::opt_r ? (split(/,/, $main::opt_r)) : () );

    # The only important commands to pass are \begin and \end, so that the
    # parser realizes they start/end environments, as opposed to being
    # regular old tokens.
    my %MyTokens = ( '{' => $Text::TeX::Tokens{'{'},
                     '}' => $Text::TeX::Tokens{'}'},
                     '\begin' => $Text::TeX::Tokens{'\begin'},
                     '\end' => $Text::TeX::Tokens{'\end'},
    );
    
    my $InFileName;
    foreach $InFileName (@syntaxfiles) {
 die "could not find syntax file $InFileName" unless -e $InFileName;
 my $zzz=$debug_on ? "from $InFileName " :"";
 warn "Reading LaTeX command syntax $zzz\n";

 # Open the file to turn into LyX.
 my $infile = new Text::TeX::OpenFile $InFileName,
     'defaultact' => \&read_commands,
     'tokens' => \%MyTokens;

 # When we start (each file), we're not reading regular environments yet
 $in_regular_env = 0;

 # Process the file
 $infile->process;
    }

    if ($debug_on) {
 print "Regular environments: @regular_env\n";
 my @mathkeys = keys(%math_trans);
 print "     Math command     |  translation\n" if @mathkeys;
 foreach (@mathkeys) { printf("%20s       %s\n",$_,$math_trans{$_}) }
    }

    #warn "Done reading commands\n";
    return;
} # end subroutine call_parser

sub read_commands {
# This subroutine is called by Text::TeX::process
# Arg0 is the token we just ate
# Arg1 is the file object we're reading from
#
#    We create a hash, where each command is a key. The value is just a string
# of zero or more 'o' and 'r' characters. Each 'o' stands for an optional
# argument, 'r' stands for a required argument. 'R' stands for a required
# argument whose text will be regular LaTeX, e.g., the argument to \mbox{}
#    In addition, the $regenv_name environment contains
# regular environments, like those input with the -r option.
#    Note that if a command is found more than once, then it wil be overwritten.
# This is a feature. This way, a user-defined syntax file can overwrite the
# argument list found in the default syntax file.
    my ($token,$fileobject) = (shift,shift);

    my $type = ref($token);
    $type =~ s/^Text::TeX::// or die "unknown token type $type from Text::TeX";
    #print $token->exact_print, unless $type eq "Paragraph";
    #print $token->comment if $token->comment;

    # Because there's no token list, ALL tokens will be
    #    Paragraph, Text, or Token
    SWITCH: for ($type) {
       # Handle blank lines.
        if (/Paragraph/) {
     # don't do anything
     last SWITCH;

        } elsif (/^Token/) {
     # Comment in its own paragraph... skip
     last SWITCH unless defined($token->print);

     if ($in_math_trans) { # read translations of math commands
         my $key = $token->print;
  # Translation is whatever's in the argument to the token
  # (There might be multiple tokens in there)
  my @vals = $fileobject->eatBalanced->contents;
  my $val = join ("", map {$_->exact_print} @vals);
  $math_trans{$key} = $val;
     
     } else { # regular portion of syntax file
  my ($dum2);
  my $args = "";
  # read while there are arguments
  while (($dum2 = $fileobject->lookAheadToken) &&
         ($dum2 =~ /^[[{]$/)) {
      if ($dum2 eq '[') { #eat optional argument - assumed simple
   $fileobject->eatOptionalArgument;
   $args .= "o";
      } else {
   my $tok = $fileobject->eatBalanced or warn "bad group";
   if ($tok->exact_print eq $Translate_Word) {
       $args .= "R";
   } else {
       $args .= "r";
   }
      } # end if $dummy = [{
  } # done reading command
  $CommandHash{$token->print} = $args;
     } # in math trans env or regular token?

     last SWITCH;

        } elsif (/^Begin::Group::Args/) {
     my $env = $token->environment;
     CASE: {
  $in_regular_env = 1, last CASE if $env eq $regenv_name;
  $in_math_trans = 1,  last CASE if $env eq $math_trans_name;
  warn "Unknown environment $env in syntax file";
     }

        } elsif (/^End::Group::Args/) {
     my $env = $token->environment;
     CASE: {
  $in_regular_env = 0, last CASE if $env eq $regenv_name;
  $in_math_trans = 0,  last CASE if $env eq $math_trans_name;
  warn "Unknown environment $env in syntax file";
     }

        } elsif (/^Text/) {
     # don't do anything unless we're reading environments
     if ($in_regular_env) {
  my @new_envs = (split(/\s+/, $token->print));
  @new_envs = grep ($_, @new_envs); # remove empty elements
  push @regular_env,@new_envs;
     }
     last SWITCH;
        } else {
     die "unexpected token type $type";
 }

    } # end SWITCH

} # end sub read_commands

sub Merge {
#    This sub creates a token list (which could be used to call a Text::TeX
# parser) from %CommandHash, and merges it with the input token list
#    If a command takes any required arguments, it will be a report_args,
# but if it just takes an optional argument, it can stay a regular old token.
# In either case, we insert a new field, "relyx_args", into the token list,
# which is the expected order of arguments for that command. Even if there
# are no args, we insert an empty relyx_args, so that we can differentiate
# between a truly unknown token and a known token which takes no args.
#
# We don't allow a new command to override a command that already exists in
# OldHash, i.e., one that was defined explicitly in the calling sub.
#
# Arg0 is a (reference to an) existing token list
    my $OldHashRef = shift;

    foreach (keys %CommandHash) {
 my $val = $CommandHash{$_};
 my ($entry, $count, @foo);
        if (!exists $OldHashRef->{$_}) {
     if ($count = scalar(@foo = ($val =~ /r/gi))) {
  # TeX.pm will make this a TT::BegArgsToken and $count-1
  #    TT::ArgTokens, followed by a TT::EndArgsToken
  $OldHashRef->{$_} = {"Type" => 'report_args',
                       "count" => $count,
         "relyx_args" => $val};
     } else { # only non-required args
  # Make it a regular TT::Token, but write relyx_args
  #    (even if $val is "")
  $OldHashRef->{$_} = {"relyx_args" => $val};
     }
 }
    } # end foreach

} # end sub Merge

############################  READ LAYOUTS  ####################################
sub read_layout_files {
# This subroutine reads a textclass-specific layout file and all files
# included in that file.
#    It sets up the layout hash table. For each environment, it describes which
# layout that environment refers to. It does the same for macros which
# begin LyX layouts (e.g., \section)
#    If we read a command that's not already in CommandHash, it means that this
# layout has some commands that aren't in syntax.default. If so, we ASSUME
# that the command takes just one required argument, and put it in
# CommandHash, so that &Merge will eventually put these commands into the
# token lists.
#
# TODO: we actually need to allow more sophisticated stuff. E.g. \foilhead
# is converted to Foilhead or ShortFoilHead (foils.layout) depending on whether
# the command has "r" or "or" arguments. Reading LatexParam (if it exists)
# can help us with this.
# Default is "r". Just unshift other args as you read them, since latexparam
# is put in between macro & the argument
# TODO: We need to store ToLayout s.t. we can have > 1 layout per command.
# Maybe by default just have one layout, but if (ref(layout)) then read
# more args & thereby figure out which layout?

# Arg0 is the name of the documentclass
    use FileHandle;
    use File::Basename;
    my $doc_class = shift;
    my @filestack;
    my $fh;
    my $line;
    # ToLayout{latexname} stores layout; so ReversHash{layout} = latexname
    my %ReverseHash;
    my $debug_on = (defined($main::opt_d) && $main::opt_d);

    # look for layout file in $HOME/.lyx first, then system layouts directory
    my $searchname = "$doc_class.layout";
    my @searchdirs = ();
    my $personal_layout = "$main::dot_lyxdir/layouts";
    push(@searchdirs,$personal_layout) if -e $personal_layout;
    my $system_layout = "$main::lyxdir/layouts";
    # I guess this won't exist if running reLyX without installing...
    # Of course, in that case, this will probably break
    push(@searchdirs,$system_layout) if -e $system_layout;
    my @foundfiles = grep(-e "$_/$searchname", @searchdirs) or
          die "Cannot find layout file $searchname in dir(s) @searchdirs";
    my $LayoutFileName = "$foundfiles[0]/$searchname"; # take first one we found

    $fh = new FileHandle;
    $fh->open ("<$LayoutFileName");
    my $zzz=$debug_on ? "$LayoutFileName" :"";
    warn "Reading layout file $zzz\n";
    push @filestack, $fh;

    # Read the layout file!
    my ($lyxname, $latexname, $latextype, $latexparam);
    my $fname;
    while() {
 # Read a line. If eof, pop the filestack to return to the file
 #    that included this file *or* finish if the stack's empty
        unless (defined ($line = <$fh>)) {
     $fh->close;
     pop @filestack;
     last unless ($#filestack+1); # finish when stack is empty
     $fh = $filestack[-1];
     next; # read another line from the "calling" file
 }

 # Skip blank lines
 next if $line =~ /^\s*$/;

 # Split the line. Use limit 2 since there may be whitespace in 2nd term
 my ($field_name, $field_stuff) = split(' ', $line, 2);
 $field_name = lc($field_name); # LyX is case insensitive for fields
 if (defined($field_stuff)) {
     $field_stuff =~ s/^\"(.*)\"/$1/;
     chomp ($field_stuff);
 }

 # This set of ifs deals with lines outside a style definition
 if ($field_name eq "style") { # start a style definition
     $lyxname = $field_stuff;
     # Styles in LyX have spaces, but _ in layout files
     $lyxname =~ s/_/ /g;
     $latexname  = ""; # make sure these variables are unset
     $latextype  = "";
     $latexparam = "";
 } elsif ($field_name eq "input") { #include a file
     $searchname = $field_stuff;
     @foundfiles = grep(-e "$_/$searchname", @searchdirs) or
       die "Cannot find layout file $searchname in dir(s) @searchdirs";
     $fname = "$foundfiles[0]/$searchname"; # take first one we found
     $fh = new FileHandle;
     push @filestack, $fh;
     $fh->open("<$fname");
     print "Reading included layout file $fname\n" if $debug_on;
 }

 next unless $lyxname; # not w/in style definition

 # This set of ifs deals with lines within a Style definition
 if ($field_name eq "latexname") {
     $latexname = $field_stuff;
     next;
 } elsif ($field_name eq "latexparam") {
     #$dum = $field_stuff;
     $latexparam = $field_stuff;
     next;
 } elsif ($field_name eq "latextype") {
     $latextype = $field_stuff;
     next;
 } elsif ($field_name eq "copystyle") { # copy an existing style
     # "if" is necessary in case someone tries "CopyStyle Standard"
     if (exists $ReverseHash{$field_stuff}) {
  my $layref = $ReverseHash{$field_stuff};
  $latexname  = $layref->{"name"};
  $latextype  = $layref->{"type"};
  $latexparam = $layref->{"param"};
     }

 # When you get to the end of a definition, create hash table entries
 #    (if you've found the right information)
 } elsif ($field_name eq "end") {

     if ($latextype and $latexname) {
  # Create reverse hash entry (needed for CopyStyle)
  # Do it before making modifications to $latexname, e.g.
  $ReverseHash{$lyxname} = {"name"  => $latexname,
                            "type"  => $latextype,
       "param" => $latexparam,
       };

  my ($nest, $skip) = (0,0);
  for ($latextype) { # make $_=$latextype
      if (/^Command/) {
   # Macros need a '\' before them. Environments don't
   $latexname = '\\' . $latexname;

   # Create the command if it wasn't in syntax.default
   unless (exists $CommandHash{$latexname}) {
       $CommandHash{$latexname} = "r";
   }

      } elsif (/^Environment/) {
   $nest = 1;
      } elsif (/Item_Environment/i || /List_Environment/) {
   $nest = 1;

      # layout Standard has LatexType Paragraph. It shouldn't
      #    have any hash entry at all
      } elsif (/^Paragraph$/) {
          $skip = 1;
      } else {
   warn "unknown LatexType $latextype" . 
        "for $latexname (layout $lyxname)!\n";
      }
     # Handle latexparam, if any
#     if ($dum =~ s/^"(.*)"/$1/) { # newer layout file syntax
#  while ($dum =~ /^[[{]/) {
#      $dum =~ s/\[.*?\]// && ($latexargs = "o$latexargs") or
#      $dum =~ s/\{.*?\}// && ($latexargs = "r$latexargs");
#  }
#  warn "leftover LatexParam stuff $dum" if $dum;
#     } else { # 0.12.0
#  if ($latextype eq "Command") {optarg}
#  else {req. arg}
#     }
  } #end for

  # Create the hash entry
  unless ($skip) {
      $ToLayout->{$latexname} = {"layout" => $lyxname,
          "nestable" => $nest};
  }

  # Now that we've finished the style, unset $lyxname so that
  #     we'll skip lines until the next style definition
  $lyxname = "";
     } # end if ($latextype and $latexname)
 } # end if on $line
     
    } #end while

## Print every known layout
#    print "     LatexName            Layout        Nestable?\n";
#    foreach (sort keys %$ToLayout) {
#        printf "%20s%15s     %1d\n",$_,$ToLayout->{$_}{'layout'},
#           $ToLayout->{$_}{'nestable'};
#    };
    #warn "Done reading layout files\n";
    return;
} # end sub read_layout_files



1; # return TRUE to calling routine

[ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge