[pageheader "expand.tcl"] [section SYNOPSIS]
tclsh expand.tcl [lb]options[rb] files...
[section VERSION] This manpage is for Expand 2.2. Changes from earlier versions are described in [hsection "CHANGES SINCE VERSION 2.0"]
[section DESCRIPTION] Expand is a macro processor, based on Tcl 8.0 or later. Expand reads the input files, writing input text to output text. Most text is output unchanged. Any text contained in square brackets, "[lb]" and "[rb]", is expanded as a macro. The result of the expansion is written to the output.
Any valid Tcl command is a valid macro; this document will use the
term "macro" for Tcl commands intended to be used within an Expand
input file, and "command" for Tcl commands intended to be called from
other Tcl code. Before reading any input files, Expand reads
any exprules.tcl file in the current directory. This
file, called the "rules" file, can define any additional macros
needed by the document being processed.
Output can be written to standard output, directed to a file, or suppressed entirely.
Expand can be used to process any kind of input text, but was specifically designed with HTML preprocessing in mind. It is especially useful in maintaining large websites with a significant amount of boilerplate used by many pages. Changing the boilerplate is as easy as redefining an Expand macro. One could define a navigation bar macro, for example, with one argument: the identity of the current page. The macro would format the navigation bar appropriately, in context.
[section OPTIONS]
-help
-rules file
exprules.tcl, if present, will be used.
-out file
nul, no output will be written. If no file is
specified, output will be written to stdout. The output file
can be changed at any time during processing using the
[command setoutput] command.
-errout mode
mode are
nothing, macro, error
or fail; fail is the default.
If mode is nothing, the erring macro
will produce no output. If mode is
macro, the macro and its arguments will be output
unchanged. If mode is error, the
macro and its arguments will be output followed by an error
message; this mode is especially useful for locating errors.
If mode is fail, the error message
will be written to standard error, and Expand will halt.
-web
[section "DEFINING MACROS"] An Expand macro is nothing more nor less than a Tcl command. Describing the Tcl language is beyond the scope of this web page; visit [link http://www.scriptics.com "Scriptics, Inc."] for more information about Tcl.
Expand macros can do anything you like, but the typical use is to format and return text. The following simple example shows how to highlight text with asterisks:
proc highlight {text} {
return "*** $text ***"
}
If some other kind of highlighting is desired later on, this macro can be changed, and all documents that use it can be updated just by running them through Expand.
It's important that the macro return the formatted text, rather than printing it out. Expand replaces text in the input with the result of this macro, and doesn't print it out until the entire file is complete. If this macro wrote its formatted text to the output itself, it would be written out of order.
Sometimes macros are executed for their side effects, rather than to
format output. Such macros should always end with an explicit
return, so that they don't add erroneous output. The
following macro adds a word to global list; if the
return command weren't used, the entire list would be
written to the output:
proc saveword {word} {
global wordList
lappend wordList $word
return
}
User-defined macros are typically placed in a rules file, such as
exprules.tcl.
[section "HOOKS"] In addition to macros, the rules file can also redefine one or more hooks. Hooks are Tcl commands defined and called by Expand that have no effect by default, but can be redefined for particular purposes. Expand defines the following hooks:
The result of [command init_hook] is never added to the output.
[section "PARSING THE COMMAND LINE"] Expand parses its own options from the command line, loads the rules file (if any), calls the [command init_hook], verifies that no unknown options remain, and assumes that any remaining command line arguments are the names of input files. Thus, a rules file can define its own command line options or in any other way modify the command line in its [command init_hook]. The easiest way to do this is to use the [command getoptions] command.
Suppose the rules will output summary information to a file if the "-summary" option is specified; the information can be "brief" or "verbose"; furthermore, the rules can be "-strict" in their interpretation of particular macros. The following [command init_hook] will parse and remove the necessary options from the command line.
proc init_hook {} {
global argv strictFlag summaryFile summaryMode
# Parse the options
getoptions argv {
{-strict strictFlag flag}
{-summary summaryFile string ""}
{-mode summaryMode enum brief verbose}
}
# Open the summary file
}
If $argv was "-summary out.text foo.in bar.in" before
the [command init_hook] was called, it will be "foo.in bar.in"
afterward.
Presumably the macros in the rules file modify their behavior
based on the strictFlag, summaryFile,
and summaryMode variables.
[section "CHANGING THE BRACKETS"] Expand was initially developed to preprocess HTML documents; for that purpose, the "[lb]" and "[rb]" characters work just fine to bracket commands, as well as being familiar to Tcl programmers. For preprocessing other kinds of files, however, other bracket tokens might work better. The [command setbrackets] command can change the bracket tokens to something more suitable: "{" and "}", or "(*" and "*)", or even "%" and newline. The only requirement is that neither token can be the empty string.
[section "MULTI-PASS PROCESSING"]
Consider a document with numbered sections: given an appropriate
section command, it's easy to generate the numbers
automatically. Now consider producing a table of contents at the
beginning of the document, with links to the sections. It cannot
be done in one pass through the input; at least two passes are
needed. The first pass accumulates the table of contents information,
and the second actually formats the document, inserting the table of
contents in its proper place.
Expand supports this kind of processing via the [command setpasses] and [command exppass] commands. [command setpasses] is called from the [command init_hook] and sets the total number of passes (the minimum, obviously, is 1). During processing, [command exppass] returns the number of the current pass; the first pass is pass 1. When there is more than one pass, Expand's algorithm is more complicated:
nul.
Note that by default, no output is produced until the final pass.
The macro used in the input files can modify their behavior based
on the value of [command exppass]; a section macro, for
example, would add information to a global table-of-contents list on
pass 1 and return the formatted section header on pass 2.
[section "THE CONTEXT STACK"] The context stack is a new feature in Expand 2.0, and solves the following problem. Often it's desirable to define a pair of macros which operate in some way on the raw text between them. Consider a set of macros for adding footnotes to a web page: in Expand 1.x, one might have implemented something like this:
Dr. Pangloss, however, thinks that this is the best of all
possible worlds.[lb]footnote "See Candide, by Voltaire"[rb]
The footnote macro would, presumably, assign a number to
this footnote and save the text to be formatted later on. However,
this solution is ugly if the footnote text is long or should contain
additional markup. Consider the following instead:
Dr. Pangloss, however, thinks that this is the best of all
possible worlds.[lb]footnote[rb]See [lb]bookTitle "Candide"[rb], by
[lb]authorsName "Voltaire"[rb], for more information.[lb]/footnote[rb]
Here the footnote text is contained between footnote and
/footnote macros, continues onto a second line, and
contains several macros of its own. This is both clearer and more
flexible; however, in Expand 1.3 there was no easy way to do it. The
footnote text would have been expanded into the output in place. In
Expand 2.0, however, the footnote macro pushes a new
context onto the context stack. Then, all expanded text gets placed
in that new context. /footnote retrieves it by popping
the context. Here's a skeleton implementation of these two macros:
proc footnote {} {
cpush footnote
}
proc /footnote {} {
set footnoteText [lb]cpop footnote[rb]
# Save the footnote text, and return an appropriate footnote
# number and link.
}
The [command cpush] command pushes a new context onto the stack; the
argument is the context's name. It can be any string, but would
typically be the name of the macro itself. Then, [command cpop]
verifies that the current context has the expected name, pops it off
of the stack, and returns the accumulated text.Expand provides several other tools related to the context stack. Suppose the first macro in a context pair takes arguments or computes values which the second macro in the pair needs. After calling [command cpush], the first macro can define one or more context variables; the second macro can retrieve their values anytime before calling [command cpop]. For example, suppose the document must specify the footnote number explicitly:
proc footnote {footnoteNumber} {
cpush footnote
csave num $footnoteNumber
# Return an appropriate link
}
proc /footnote {} {
set footnoteNumber [lb]cget num[rb]
set footnoteText [lb]cpop footnote[rb]
# Save the footnote text and its footnoteNumber for future
# output.
}
At times, it might be desirable to define macros that are valid only
within a particular context pair; such macros should verify that they
are only called within the correct context using either
[command cis] or [command cname].[section "STANDARD COMMANDS AND MACROS"] In addition to the standard Tcl commands, and the user-defined macros, Expand input and rules files can use the following commands and macros.
name must match the previous [command cpush]. See
[hsection "THE CONTEXT STACK"].
varname,
suitable for using in Tcl append and
lappend calls.
"arglist" must be the name of a variable in the current scope which contains a list of arguments. It's typically "argv".
If "-strict" is specified, unknown options are flagged as errors.
The "deflist" is a list of option definitions. Each option definition has one of the following forms. In each form, NAME is the option name, which must begin with a "-" character, and VAR is the name of a variable in the caller's scope which will receive the option's value.
{NAME VAR flag}
{NAME VAR enum VAL1 VAL2...}
If the next argument begins with a hyphen, "-",
getoption assumes that the option's value
is missing.
{NAME VAR string DEFVALUE}
If the next argument begins with a hyphen, "-",
getoption assumes that the option's value
is missing.
If any errors are found, an error message is thrown using the Tcl "error" command. Otherwise, the parsed options and their values are removed from the argument list variable.
For an example, see [hsection "PARSING THE COMMAND LINE"], above.
listvar, removing that element from the list. If
the list is empty, returns the empty string.
nothing,
macro, error or fail;
these modes are described under [hsection "OPTIONS"].
"", the empty string, output will go to
stdout; if the filename is "nul", no
output will be produced. [command setoutput] is called by
Expand just before each call to [command begin_hook].
[lb]link foobar.html "A Document"[rb]
[lb]link frobozz.html[rb]
expands to
<a href="foobar.html">A Document</a>
<a href="frobozz.html">frobozz.html</a>
[lb]mailto will@wjduquette.com[rb]
[lb]mailto will@wjduquette.com "Will Duquette"[rb]
expands to
<a href="mailto:will@wjduquette.com">will@wjduquette.com</a>
<a href="mailto:will@wjduquette.com">Will Duquette</a>
return "[lb]tag a href foobar.html[rb]foobar.html</a>"
is equivalent to
return "<a href=\"foobar.html\">foobar.html</a>"
-dir dirname
dirname instead.
-numbers
-strip
-class classname
classname is used as the value of the
<pre> tag's "class" attribute.
-style styletext
-style option is specified, the
styletext is used as the value of the
<pre> tag's "style" attribute.
-numclass classname
classname is used as the value of the
<span> tag's "class" attribute.
-numstyle styletext
-numstyle option is specified, the
styletext is used as the value of the
<span> tag's "style" attribute for all line numbers.
filenames. The same filename
can appear in more than one [command listing] block; the
contents of the file will be the concatenation of all of the
listing blocks. If no filename is given, the
listing is included in the HTML document, but is not written to
disk.The [command listing] command takes the following options:
-silent
-strip on|off
-numbers on|off
For example, the following listing is line numbered, and included in the file "hello.tcl":
[lb]listing -numbers on hello.tcl[rb]
# Your first Tcl program
puts "Hello, world!"
[lb]/listing[rb]
[lb]listing[rb]
[lb]tclsh {set a 5}[rb]
[lb]tclsh {set b 10}[rb]
[lb]tclsh {expr $a * $b}[rb]
[lb]/listing[rb]
will expand to
$ set a 5
5
$ set b 10
10
$ expr $a * $b
50
Some text [lb]aMacro [lb]anotherMacro[rb][rb] some more text
In some cases, nested macros would cause Expand 1.X to halt.
[section "HISTORY"] Expand was written by William H. Duquette; this man page was written using Expand as an HTML preprocessor.
Expand was inspired by a cursory study of the M4 macro processor. M4 is an interesting tool, but its syntax and use can become downright weird. I judged it to be an imperfect tool for preprocessing HTML documents, but it gave me some ideas about how to use Tcl in a similar way. After that, well, it just grew. Any similarity to the philosophy of XML/XSL is purely coincidental, as I didn't read about XML until afterwards.
Should you have any questions, comments, or suggestions about Expand, feel free to contact Will at [mailto will@wjduquette.com].
Expand is available from the [link "http://www.wjduquette.com/expand" "Expand Home Page"]. [copyright 2000 "William H. Duquette"]