[ previous ] [ contents ] [ next ]
Often, especially when using arfg recursively to write self-configuring software, it is important to be able to write to auxiliary files. To facilitate that, it is advantageous to add simple syntax for sending a specified part of arfg's output to a file different from arfg's regular output file. One way of doing this is to include the following in your .arfg-rewrite file, provided it is written in Perl and uses $_ to hold the current line:
# writing files: s/^#startfile\s+(\S+)\s*$/<: {local *STDOUT; open (STDOUT, ">$1") :>/; s/^#endfile\s*$/<:}:>/;
With this definition, you can now, e. g., write
#startfile auxiliary.tmp This is just some auxiliary material. #endfile
to send the string This is just some auxiliary material. to file auxiliary.tmp.
A particularly useful application of the above file writing infrastructure is to accumulate m4 definitions. For this to work with little effort, however, the necessary quoting of m4 definitions must be achievable with minimum hassle.
One possibility, hinging on m4's quoting characters at time of invocation being ` and ', is to temporarily change quoting characters. Thus we might put the following into our .arfg-rewrite file (written in Perl as above):
# m4 quoting: s/^#savem4def\s+(\w+)\s*$/m4_ifdef(`$1', `#m4:m4_define(`$1',`:m4#m4_defn(`$1')#m4:'):m4#', `#m4:m4_undefine(`$1'):m4#')\n/; s/#m4:/`'m4_changequote(|m4:,:m4|)|m4:/g; s/:m4#/:m4|m4_changequote`'/g;
The above definitions both set up a convenient way of protecting content from m4 expansion by enclosing it in #m4: and :m4#, and to output a symbol's definition using the standard quoting characters.
Suppose now that we want to accumulate in an m4 definition information contained in all input files given to arfg; in this case the then current definitions must always be read in, and modified definitions must be dumped after processing. To this end we put into our .arfg-pre the call to read the old definition:
m4_sinclude(`auxiliary.m4')
This will read in and expand the contents of file auxiliary.m4. Suppose that macro aggregate, the definition of which is read from auxiliary.m4, holds the current information; obviously, your input file filename.arfg must contain code to update aggregate's definition like, e. g.:
m4_define(`aggregate', m4_defn(`aggregate')` Any information from 'm4_sourcefile` you might want to save.')
And to save the now modified definition (note the use of the m4 variable m4_sourcefile pre-defined by arfg) at the end of the processing, use your .arfg-post file:
#startfile auxiliary.m4 #savem4def aggregate #endfile
If, e. g., each of the three files a.arfg, b.arfg, and c.arfg contains the m4_define-command given above and no other statements modifying aggregate's definition, calling arfg a.arfg b.arfg c.arfg will result in auxiliary.m4 containing:
m4_define(`aggregate', ` Any information from a.arfg you might want to save. Any information from b.arfg you might want to save. Any information from c.arfg you might want to save.')
Of course, usually the text appended will be the result of more complex manipulations; indeed, when all the files contain the same re-definition command for aggregate, it is more appropriate to put that code (or at least the definition of a macro that does exactly this, in case the information is not to be included for file d.arfg) into .arfg-pre.
The key to full flexibility when using arfg is repeated application to all input files; that way it is possible for one pass's results to influence the next pass. In such a scenario it is usually desirable to go on applying arfg to its input files until the output no longer changes (which implies that m4 variable m4_arfgBuildTime must not be used or set explicitly via option --now). While arfg itself does not support this «fixed point mode», a little bit of shell scripting does the trick; an entry suitable for inclusion in a Makefile might be:
update : $$(wildcard *.arfg) @md5=/tmp/md5-arfg.$$$$ ; \ md5sum * > $$md5 2>/dev/null ; \ count=1; \ now=`date`; \ echo "******** calling arfg ($$count) ********"; \ arfg --now "$$now" *.arfg ; \ echo "**** Summary of changes:" ; \ until md5sum * | diff - $$md5 ; do \ let count=count+1; \ echo "******** calling arfg ($$count) ********"; \ md5sum * > $$md5 ; \ arfg --now "$$now" *.arfg ; \ echo "**** Summary of changes:" ; \ done ; \ echo " None." ; \ rm $$md5
With the above in a Makefile, calling make update will save md5 hashes of all the files in the current directory to a temporary file; it then goes on calling arfg on all input files until the md5 sums and, presumably, the files no longer change. Note the use of arfg's option --now to prevent a possible infinite loop from simply evaluating <:`date`:> in one of the files.
Alternatively, one might use a script like the following:
#!/bin/sh # run arfg until files no longer change:checksums=/tmp/arfg.md5.$$ checkdir=. checkfiles='*' arfgdir=.
arfgcall="arfg --now \"`date`\" $@"
cd $arfgdir touch $checksums md5sum $checkdir/$checkfiles > $checksums sh -c "$arfgcall" until md5sum $checkdir/$checkfiles | diff - $checksums >/dev/null do md5sum $checkdir/$checkfiles > $checksums sh -c "$arfgcall" done
rm $checksums
In the above script, $checkdir should be set to the directory indicated via the --dir option; $checkfiles is a shell pattern specifying the list of files that should be monitored for change; and a cd to directory $arfgdir is performed before setting out to call arfg; the checksums are kept in file $checksums, the default setting for which assumes writable /tmp. Any parameters passed to the above script will be passed on to arfg, though quoting may become tricky.
Among the numerous disadvantages of LATEX over TEX is its verbosity. Thus, e. g., where TEX marks displayed material by simply enclosing it in $$ … $$, LATEX instead needs \begin{displaymath} … \end{displaymath}. - But first of all, due to the frequency of apostrophes in English text, we have to make sure that the text being transformed does not interfere with the m4 quoting mechanism. To this end we start our .arfg-pre by switching m4's quotes to brackets:
m4_changequote([,])
And to be able to use the more convenient $$(x) … $$ notation for every \begin{x} … \end{x} pair (with (x) defaulting to (displaymath) if not given), we put into our .arfg-rewrite the following replacement rules (assuming Perl and $_ as above):
s/\$\$/[]SwitchMathDisplay[]/g; s/\[\]SwitchMathDisplay\[\]\(/[]SwitchMathDisplay(/g;
Then, all we have to do is to make sure that m4 macro SwitchMathDisplay switches its output between \begin{x} and \end{x}:
m4_define([SwitchMathDisplay], [m4_ifdef([SwitchMathDisplay_], [\end{SwitchMathDisplay_}m4_undefine([SwitchMathDisplay_])], [m4_define([SwitchMathDisplay_],m4_ifelse($1,,[displaymath],$1))#c& \begin{SwitchMathDisplay_}])]) m4_undefine([SwitchMathDisplay_])
As far as LATEX is concerned, a point worth noting is that arfg can be used to harvest the advantages of intricate macro definitions without including them in the LATEX output where they are often frowned upon by editors.
As can be seen from the sample outputs of the various backends, arfg's output often contains a great number of blank lines; also, macro expansion may lead to arbitrarily long lines. While this is usually no problem when the output files are processed only by computers, it may be necessary to re-format the output by using the option --polish. While the most simple solution is to call arfg --option1 --option2… file1.arfg file2.arfg… --polish "perl -n -e 'print unless m/^\s*$/'", this is not appropriate for TEX where blank lines mark paragraph breaks. Here is a simple script that might be used in this case as an argument to --polish; note, however, that it is certainly not foolproof (cf. commentary at beginning): only TEX knows how to parse TEX.
#!/usr/bin/perl -w # 20010919-20010920: # make TeX output look better: newlines, long lines. # to be used with arfg's option --polish # note that this will destroy \verbatim and other things where # whitespace is significant. # also, there are ways to screw up TeX comments. # btw, a TeX comment starting with two % signs (but not following a # backslash) is left intact but may be subject to line breaking # (devastating effects ensue). # use with care. $blank=1; undef $old; while (<>) { if (defined($old)) { s/^\s+//; $_ = $old.$_; undef $old; } if (m/(?!\\)%%/) { print; next; } chomp; if (m/^(.*)(?<!\\)%/) { $old=$1; next; } if (m/^\s*$/) { print "\n" unless ($blank); $blank=1; } else { $blank=0; while (length($_)) { if (m/^\s*(.{1,75})(\s(.*))?$/) { print $1,"\n"; last unless defined($2); $_ = $2; } else { print $_,"\n"; last } } }; };
The above script breaks lines at whitespace after a maximum of 75 characters, removes simple comments starting with % (unless when following a backslash) while leaving intact comments starting with %% but not following a backslash. As noted, %% comments should only be used when line breaking cannot cause spill-over.
An example of much more advanced use of arfg can be found from http://purl.oclc.org/NET/ar-hrt-1/, the very implementation of a physical theory that originally prompted writing arfg.
[ previous ] [ contents ] [ next ]
Copyright © 2001 by Albert REINER. All rights reserved.
URL: http://purl.oclc.org/NET/arfg/current/examples.html
URL: http://purl.oclc.org/NET/arfg/2.11c/examples.html
2001-12-13 17:29:48