Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Here's the idea: Like most people, I use a lot of custom macros that are often nothing more than shortened versions of commands that I use often. This is great for me, but makes the latex cryptic for others who need to read it.

Does anyone know of a way to resolve/expand a certain bunch of macros? It could take as input a tex file and one or more sty file containing macros, and it eliminates from the tex file all the macros in the sty file, leaving a tex file that no longer needs the sty files.

share|improve this question
1  
You mean something like "compiling" your tex document to make it autonomous? –  ℝaphink Jun 26 '11 at 7:18
    
I would do this using Perl (or similar programming language), but for this the macro definition and usage must be streight-forward. Teaching Perl all the nuances of TeX is more or less impossible. E.g. catcode changes and other things would not be supported. –  Martin Scharrer Jun 26 '11 at 9:06
    
The idea is a source-to-source transformation where some macros get expanded while others are retained. (There might be a connection to so-called partial evaluation of program code.) I think this would be very useful but given the complexity of TeX I doubt this can be reliably implemented outside a TeX engine itself. –  Christian Lindig Jun 26 '11 at 10:00
    
A TeX based solution I just imagined would be to use a similar technqie as listings for syntax highlighting or ltxdoc for macro indexing: read the file verbatim but detect macro name by looking for \ followed by letters. Then expand that macro once if it is in a list. This might need multiple passes. –  Martin Scharrer Jul 5 '11 at 14:24
    
@Christian Lindig: You're right that a general solution would be very difficult to implement outside of TeX. But the OP doesn't need a general solution, just something that works on a small number of macros that he's defined himself. –  Ben Crowell Jul 23 '11 at 17:08
show 1 more comment

2 Answers

up vote 5 down vote accepted

The following is a very simpleminded perl function that I use for this. apply_tex_macros($macros,$tex) takes TeX code in $macros and $tex, reads macro definitions out of $macros, and expands those macros wherever they occur in $tex. It's not completely general, but it works well enough for my application.

sub apply_tex_macros {
  my $macros = shift;
  my $tex = shift;
  my $orig = $tex;
  $macros =~ s/(?<!\\)\%.*//; # remove comments
  my $curly = "(?:(?:{[^{}]*}|[^{}]*)*)"; # match anything, as long as curly braces in it are matched, and not nested
  my $curlycurly = "(?:(?:{$curly}|$curly)*)"; # allow one level of nesting
  my $curlycurlycurly = "(?:(?:{$curlycurly}|$curlycurly)*)"; # allow two levels of nesting
  my $before;
  while ($macros =~ /\\newcommand{\\([a-zA-Z\-]+)}\s*(?:\[(\d+)\])\s*(?:\[($curlycurlycurly)\])\s*{($curlycurlycurly)}/g) {
    my $command = $1;
    if ($tex=~/\\$command/) {print STDERR "Warning in footex, command $command with optional arguments is not supported, in input $tex.\n"}
  }
  my $depth = 0;
  do {
    $before = $tex;
    while ($macros =~ /\\newcommand{\\([a-zA-Z\-]+)}\s*(?:\[(\d+)\])?\s*{($curlycurlycurly)}/g) {
      my ($command,$nargs,$def) = ($1,$2,$3);
      if (!$nargs) {
        $tex =~ s/\\$command/$def/g;
      }
      if ($nargs==1) {
        while ($tex =~ m/\\($command\{($curlycurlycurly)\})/) {
          my ($macro,$arg) = ($1,$2);
          my $result = $def;
          $result =~ s/#1/$arg/g;
          my $foo = quotemeta $macro;
          $tex =~ s/\\$foo/$result/g;
        }
      }
      if ($nargs==2) {
        while ($tex =~ m/\\($command\{($curlycurlycurly)\}\{($curlycurlycurly)\})/) {
          my ($macro,$arg1,$arg2) = ($1,$2,$3);
          my $result = $def;
          $result =~ s/#1/$arg1/g;
          $result =~ s/#2/$arg2/g;
          my $foo = quotemeta $macro;
          $tex =~ s/\\$foo/$result/g;
        }
      }
      if ($nargs>2 && $tex=~/\\$command/) {print STDERR "Warning in footex, command $command with more than 2 arguments is not supported, in input $orig.\n"}
    }
    ++$depth;
    print STDERR "Warning, macro expansion isn't bottoming out, depth=$depth, tex=$tex\n" if $depth>=28;
  } while ($tex ne $before && $depth<30);
  return $tex;
}
share|improve this answer
add comment

I've written a few scripts along these lines, though my aim has been more conversion between LaTeX and some other markup format (Markdown or some variant thereof). I started with a simple perl script that expanded some LaTeX macros (could handle basic stuff), then a PHP program that converted some LaTeX stuff to XHTML+MathML, next a Perl script that attempted to emulate TeX (catcodes and all, so essentially teaching Perl all the nuances of TeX), and finally a style file that can be run with TeX itself and that outputs a text file (well, actually a PDF but with fixed-width fonts and no hyphenation so that pdftotext converts it perfectly to a text file) that is suitable for inputting to the next markup processor.

One component of my desired output is the itexToMML program which converts a subset of LaTeX mathematical output to MathML. Therefore, one thing I've had to figure out is how to "escape" certain macros so that the aren't processed whilst allowing other macros to be processed. This is sort of what you're asking for, only it works by a whitelist of macros not to be processed than a blacklist of macros that should be processed.

If any of the above sounds useful, feel free to contact me for more details.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.