<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">=head1 NAME

Macro - A macro pre-processor with embedded perl capability 

=head1 SYNOPSIS

    use Macro ;

    # new()

    my $Macro = new Macro ;
    while( &lt;&gt; ) {
        print $Macro-&gt;expand( $_ ) if $_ ;
    }

    # Canonical use (the filename improves error messages):
    my $Macro = new Macro ;
    while( &lt;&gt; ) {
        print $Macro-&gt;expand( $_, $ARGV ) if $_ ;
    }

    # Create a macro object and create initial macros/scripts from the file(s)
    # given:
    my $Macro = new Macro( 
                    -file =&gt; [ 'local.macro', '~/.macro/global.macro' ] 
                    ) ;


    # Create a macro object and create initial macros/scripts from the
    # definition(s) given:
    my $Macro = new Macro(
                    -macro =&gt; [
                            [ 'MAX_INT' =&gt; '32767' ],
                        ],
                    -script =&gt; [
                        [ 'DHM2S' =&gt; 
                            [ 
                                my $s = (#0*24*60*60)+(#1*60*60)+(#2*60) ;
                                "#0 days, #1 hrs, #2 mins = $s secs" 
                            ],
                        ],
                    -variable =&gt; [ '*MARKER*' =&gt; 0 ],
                    ) ;

    # We may of course use any combination of the options. 

    my $Macro = new Macro( -comment =&gt; 1 ) ; # Create the %%[] macro.


    # define()

    $Macro-&gt;define( -macro, $macroname, $macrobody ) ;

    $Macro-&gt;define( -script, $scriptname, $scriptbody ) ;

    $Macro-&gt;define( -variable, $variablename, $variablebody ) ;


    # undefine()

    $Macro-&gt;undefine( -macro, $macroname ) ;

    $Macro-&gt;undefine( -script, $scriptname ) ;

    $Macro-&gt;undefine( -variable, $variablename ) ;


    # undefine_all()

    $Macro-&gt;undefine( -macro ) ;

    $Macro-&gt;undefine( -script ) ;

    $Macro-&gt;undefine( -variable ) ;


    # list()

    @macros    = $Macro-&gt;list( -macro ) ;
    @macros    = $Macro-&gt;list( -macro, -namesonly ) ;

    @scripts   = $Macro-&gt;list( -script ) ;
    @scripts   = $Macro-&gt;list( -script, -namesonly ) ;

    @variables = $Macro-&gt;list( -variable ) ;
    @variables = $Macro-&gt;list( -variable, -namesonly ) ;


    # load_file()

    $Macro-&gt;load_file( $filename ) ;


    # expand_file()

    $Macro-&gt;expand_file( $filename ) ;
    @expanded = $Macro-&gt;expand_file( $filename ) ;

    
    # expand()

    $expanded = $Macro-&gt;expand( $unexpanded ) ;
    $expanded = $Macro-&gt;expand( $unexpanded, $filename ) ;


This bundle also includes the C&lt;macro&gt; script which allows us to expand
macros without having to use/understand C&lt;Macro.pm&gt;, although you will have to
learn the handful of macro commands available and which are documented here
and in C&lt;macro&gt;. C&lt;macro&gt; also supports an embedded perl approach (use the
C&lt;-e&gt; option or create a soft link and call it as C&lt;emacro&gt;).


=head1 DESCRIPTION

Define macros, scripts and variables in macro files or directly in text files.

Commands may appear in separate macro files which are loaded in either via the
text files they process (e.g. via the C&lt;%LOAD&gt; command), or may be embedded
directly in text files. Almost every command that can appear in a file has an
equivalent object method so that programmers may achieve the same things in
code as can be achieved by macro commands in texts; there are also additional
methods which have no command equivalents.

All the examples given here use the macro approach (since that is what this
module supports directly). However this module can be used to provide an
embedded perl approach which is what the C&lt;macro&gt; script offers; that approach
is documented in the C&lt;macro&gt; script.

=head2 Summary of Commands

These commands may appear in separate `macro' files, and/or in the body of
files. Wherever a macroname or scriptname is encountered it will be replaced
by the body of the macro or the result of the evaluation of the script using
any parameters that are given.

    %DEFINE macroname [macro body]

    %DEFINE macroname
    multi-line
    macro body
    #0, #1 are the first and second parameters if any used
    %END_DEFINE

    %UNDEFINE macroname

    %UNDEFINE_ALL   # Undefine all macros


    %DEFINE scriptname [script body]

    %DEFINE scriptname
    multi-line
    script body
    arbitrary perl
    optional parameters are in @Param, although #0, etc may be used
    any variables are in %Var, although #varname may be used
    %END_DEFINE

    %UNDEFINE scriptname

    %UNDEFINE_ALL_SCRIPT


    %DEFINE variablename [variable value]

    %UNDEFINE variablename

    %UNDEFINE_ALL_VARIABLE


    %LOAD[path/filename]    # Instantiate macros/scripts/variables in this
                            # file, but discard the text

    %INCLUDE[path/filename] # Instantiate macros/scripts/variables in this
                            # file and output the resultant text

 
    %CASE [condition]       # Provides #ifdef-type functionality
    %END_CASE

Thus, in the body of a file we may have, for example:

    %DEFINE &amp;B [Billericky Rickety Builders]
    Some arbitrary text.
    We are writing to complain to the &amp;B about the shoddy work they did.


=head2 Macro systems vs embedded systems

Macro systems read all the text, substituting anything which matches a macro
name with the macro's body (or script name with the result of the execution of
the script). This makes macro systems slower (they have to check for
macro/script names everywhere, not just in a delimited section) and more risky
(if we choose a macro/script name that normally occurs in the text we'll end
up with a mess) than embedded systems. On the other hand because they work on
the whole text not just delimited bits, macro systems can perform processing
that embedded systems can't. Macro systems are used extensively, for example
the CPP, C pre-processor, with its #DEFINE's, etc.

Essentially, embedded systems print all text until they hit an opening
delimiter. They then execute any code up until the closing delimiter. The text
that results replaces everything between and including the delimeters. They
then carry on printing text until they hit an opening delimeter and so on
until they've finished processing all the text. This module provides a macro
approach; the C&lt;macro&gt; script supplied as a wrapper for C&lt;Macro.pm&gt; can
operate both as a macro pre-processor I&lt;and&gt; as an embedded perl processor, as
we wish.

=head2 Defining Macros with C&lt;%DEFINE&gt; and C&lt;define()&gt;

In files we would write:

    %DEFINE MAC [The Mackintosh Macro]

The equivalent method call is:

    $Macro-&gt;define( -macro, 'MAC', 'The Mackintosh Macro' ) ;

We can call our macro anything, excluding white-space characters and [,
although [ is not advised. So a name like C&lt;%*&amp;!&gt; is fine - indeed names which
could not normally appear in the text are recommended to avoid having the
wrong thing substituted. We should also avoid calling macros, scripts or
variables names beginning with #. All names are case-sensitive.

Note that if we define a macro and then a script with the same name the
script will effectively replace the macro.

We can have parameters:

    %DEFINE *P [The forename is #0 and the surname is #1]

Parameters can contain square brackets since macro will grab up to the last
square bracket on the line. The only thing we can't pass are `|'s since these
are used to separate parameters. White-space between the macro name and the [
is optional.

Parameters are named #0, #1, etc. There is no limit, although we must use all
those we specify. In the example above we I&lt;must&gt; use *P[param1|param2],
e.g. *P[Jim|Hendrix]; if we don't C&lt;Macro.pm&gt; will croak. 

On the other hand we can use as many I&lt;more&gt; than we need, for example
add a third to document: *P[Jim|Hendrix|Musician] will become `The forename is
Jim and the surname is Hendrix', just as in the previous example; the third
parameter, `Musician', will simply be thrown away.

If we define a macro, script or variable and later define the same name the
later definition will replace the earlier one. This is useful for making local
macro definitions over-ride global ones, simply by loading the global ones
first.

Although macros can have plain textual names like this:

    %DEFINE MAX_INT [32767]

It is generally wise to use a prefix and/or suffix to make sure we don't
expand something unintentionally, e.g.

    %DEFINE $MAX_INT [65535]

B&lt;Macro expansion is no respector of quoted strings or anything else&gt; - 
B&lt;if the name matches the expansion will take place!&gt;

Multi-line definitions are permitted (here's an example I use with the lout
typesetting language):

    %DEFINE SCENE
    @Section
        @Title {#0}
    @Begin
    @PP
    @Include {#1}
    @End @Section
    %END_DEFINE

This allows us to write the following in our lout files:

    SCENE[ The title of the scene | scene1.lt ]

which is a lot shorter than the definition.


=head2 Defining Scripts with C&lt;%DEFINE_SCRIPT&gt; and C&lt;define()&gt;

Instead of straight textual substitution, we can have some perl executed
(after any parameters have been replaced in the perl text):

    %DEFINE_SCRIPT *ADD ["#0 + #1 = " . (#0 + #1)]

or by using the equivalent method call:

    $Macro-&gt;define( -script, '*ADD', '"#0 + #1 = " . (#0 + #1)' ) ;

These would be used as *ADD[5|11] in the text which would be output as:

    These would be used as 5 + 11 = 16 in the text...

In script definitions we can use an alternative way of passing parameters
instead of or in addition to the #0 syntax.

This is particularly useful if we want to take a variable number of parameters
since the #0 etc syntax does not provide for this. An array called C&lt;@Param&gt;
is available to our perl code that has any parameters. This allows things
like the following to be achieved:

    %DEFINE_SCRIPT ^PEOPLE
    # We don't use the name hash number params but read straight from the
    # array:
    my $a = "friends and relatives are " ;
    $a .= join ", ", @Param ;
    $a ;
    %END_DEFINE

The above would expand in the following text:

    Her ^PEOPLE[Anna|John|Zebadiah].

to
    Her friends and relatives are Anna, John, Zebadiah.

In addition to having access to the parameters either using the #0 syntax or
the C&lt;@Param&gt; array, we can also access any variables that have been defined
using C&lt;%DEFINE_VARIABLE&gt; (see later). These are accessible either using
#variablename similarly to the #0 parameter syntax, or via the C&lt;%Var&gt; hash.
Although we can change both C&lt;@Param&gt; and C&lt;%Var&gt; elements in our script,
the changes to C&lt;@Param&gt; only apply within the script whereas changes to
C&lt;%Var&gt; apply from that point on globally.

Macro names can be any length and consist of any characters (including
non-printable which is probably only useful within code), except white-space
and [, although ] is not recommended and a leading # should be avoided.

Here's a simple date-stamp in `embedded perl' style:

    %DEFINE_SCRIPT &lt;:DATESTAMP:&gt;
    {
        my( $d, $m, $y ) = (localtime( time ))[3..5] ;
        $m++ ;
        $m = "0$m" if $m &lt; 10 ;
        $d = "0$d" if $d &lt; 10 ;
        $y += 1900 ;
        "#0 on $y/$m/$d" ;
    }
    %END_DEFINE

If we wanted to add the above in code we'd have to make sure the $variables
weren't interpolated:

    $Macro-&gt;define( -script, '&lt;:DATESTAMP:&gt;', &lt;&lt;'__EOT__' ) ;
    {
        my( $d, $m, $y ) = (localtime( time ))[3..5] ;
        $m++ ;
        $m = "0$m" if $m &lt; 10 ;
        $d = "0$d" if $d &lt; 10 ;
        $y += 1900 ;
        "#0 on $y/$m/$d" ;
    }
    __EOT__
 
Here's (a somewhat contrived example of) how the above would be used:

    &lt;HTML&gt;
    &lt;HEAD&gt;&lt;TITLE&gt;Test Page&lt;/TITLE&gt;&lt;/HEAD&gt;
    &lt;BODY&gt;
    &lt;:DATESTAMP:&gt;[Last Updated]&lt;P&gt;
    This page is up-to-date and will remain valid until &lt;:DATESTAMP:&gt;[midnight]
    &lt;/BODY&gt;
    &lt;/HTML&gt;

Thus we could have a file, C&lt;test.html.m&gt; containing:

    %DEFINE_SCRIPT &lt;:DATESTAMP:&gt;
    {
        my( $d, $m, $y ) = (localtime( time ))[3..5] ;
        $m++ ;
        $m = "0$m" if $m &lt; 10 ;
        $d = "0$d" if $d &lt; 10 ;
        $y += 1900 ;
        "#0 on $y/$m/$d" ;
    }
    %END_DEFINE
    &lt;HTML&gt;
    &lt;HEAD&gt;&lt;TITLE&gt;Test Page&lt;/TITLE&gt;&lt;/HEAD&gt;
    &lt;BODY&gt;
    &lt;:DATESTAMP:&gt;[Last Updated]&lt;P&gt;
    This page is up-to-date and will remain valid until &lt;:DATESTAMP:&gt;[midnight]
    &lt;/BODY&gt;
    &lt;/HTML&gt;

which when expanded, either in code using C&lt;$Macro-&gt;expand()&gt;, or using the
simple C&lt;macro&gt; utility supplied with C&lt;Macro.pm&gt;:

    [1]% macro test.html.m &gt; test.html

C&lt;test.html&gt; will contain just this:

    &lt;HTML&gt;
    &lt;HEAD&gt;&lt;TITLE&gt;Test Page&lt;/TITLE&gt;&lt;/HEAD&gt;
    &lt;BODY&gt;
    Last Updated on 1999/08/21&lt;P&gt;
    This page is up-to-date and will remain valid until midnight on 1999/08/21
    &lt;/BODY&gt;
    &lt;/HTML&gt;

Of course in practice we wouldn't want to define everything in-line like this.
See C&lt;%LOAD&gt; later for an alternative.

=head2 Defining Variables with C&lt;%DEFINE_VARIABLE&gt; and C&lt;define()&gt;

We can also define variables:

    %DEFINE_VARIABLE &amp;*! [89.1232]

or in code:

    $Macro-&gt;define( -variable, '&amp;*!', 89.1232 ) ;

Note that there is no multi-line version of C&lt;%DEFINE_VARIABLE&gt;.

All current variables are available inside C&lt;%DEFINE_SCRIPT&gt; scripts in the C&lt;%Var&gt;
hash:

    %DEFINE_SCRIPT *TEST1
    $a = '' ;
    while( my( $k, $v ) each( %Var ) ) {
        $a .= "$key = $v\n" ;
    }
    $a ;
    %END_DEFINE

Variables may be modified within script C&lt;%DEFINE&gt;s, e.g.

    %DEFINE_VARIABLE VV[Foxtrot]
    # VV eq 'Foxtrot'
    # other text
    # Here we use the #variable synax:
    %DEFINE_SCRIPT SET_VV[#VV='Alpha']
    # VV eq 'Alpha'
    # other text
    # Here we use perl syntax:
    %DEFINE_SCRIPT UPDATE_VV[$Var{'VV'}='Tango']
    # VV eq 'Tango'

As we can see variables support the #variable syntax similarly to parameters
which support #0 etc and ara available in scripts via the C&lt;@Param&gt; array.
Note that changing parameters within a script only apply within the script;
whereas changing variables in the C&lt;%Var} hash in a script changes them from
that point on globally.

Variables are also used with C&lt;%CASE&gt; (covered later).

=head2 Loading and including files with C&lt;%LOAD&gt; and C&lt;load_file()&gt;, and C&lt;%INCLUDE&gt; and C&lt;expand_file()&gt;

Although we can define macros directly in the files that require them it is often
more useful to define them separately and include them in all those that need
them.

One way of achieving this is to load in the macros/scripts first and then
process the file(s). In code this would be achieved like this:

    $Macro-&gt;load_file( $macro_file ) ; # Loads definitions only, ignores any
                                       # other text.
    $Macro-&gt;expand_file( $file ) ;     # Expands definitions (and instantiates
                                       # any definitions that appear in the
                                       # file); output is to the current
                                       # output filehandle.
    my @expanded = $Macro-&gt;expand_file( $file ) ; # Output to array.

From the command line it would be achieved thus:

    [2]% macro -f ~/.macro/html.macros test.html.m &gt; test.html
    

One disadvantage of this approach, especially if we have lots of macro files,
is that we can easily forget which macro files are required by which text
files. One solution to this is to go back to C&lt;%DEFINE&gt;ing in the text files
themselves, but this would lose reusability. The answer to both these problems
is to use the C&lt;%LOAD&gt; command which loads the definitions from the named file at
the point it appears in the text file:

    %LOAD[~/.macro/html.macros]
    &lt;HTML&gt;
    &lt;HEAD&gt;&lt;TITLE&gt;Test Page Again&lt;/TITLE&gt;&lt;/HEAD&gt;
    &lt;BODY&gt;
    &lt;:DATESTAMP:&gt;[Last Updated]&lt;P&gt;
    This page will remain valid until &lt;:DATESTAMP:&gt;[midnight]
    &lt;/BODY&gt;
    &lt;/HTML&gt;

The above text has the same output but we don't have to remember or explicitly
load the macros. In code we can simply do this:

    my @expanded = $Macro-&gt;expand_file( $file ) ;

or from the command line:

    [3]% macro test.html.m &gt; test.html

    
At the beginning of our lout typesetting files we might put this line:

    %LOAD[local.macros]

The first line of the C&lt;local.macros&gt; file is:

    %LOAD[~/.macro/lout.macros]

So this loads both global macros then local ones (which if they have the same
name will of course over-ride).

This saves repeating the C&lt;%DEFINE&gt; definitions in all the files and makes
maintenance easier.

C&lt;%LOAD&gt; loads perl scripts and macros, but ignores any other text. Thus we can
use C&lt;%LOAD&gt;, or its method equivalent C&lt;load_file()&gt;, on I&lt;any&gt; file, and it
will only ever instantiate macros and scripts and produce no output.

If we want to include the entire contents of another file, and perform macro
expansion on that file then use C&lt;%INCLUDE&gt;, e.g.

    %INCLUDE[/path/to/file/with/scripts-and-macros-and-text]

The C&lt;%INCLUDE&gt; command will instantiate any macros and scripts it encounters
and include all other lines of text (with macro/script expansion) in the
output stream.

Macros and scripts are expanded in the following order:
1. scripts (longest named first, shortest named last)
2. macros  (longest named first, shortest named last)

=head2 Skipping text using C&lt;%CASE&gt; and C&lt;%END_CASE&gt; 

It is possible to selectively skip parts of the text.

    %CASE[0]
    All the text here will be discarded.
    No matter how much there is.
    %END_CASE

The above is useful for multi-line comments.

We can also skip selectively. Here's an if...then:

    %CASE[#OS eq 'Linux']
    Skipped if the condition is FALSE. 
    %END_CASE

The condition can be any perl fragment. We can use previously defined
variables either using the #variable syntax as shown above or using the
exported perl name, thus in this case either C&lt;#OS&gt;, or C&lt;%Var{'OS'}&gt;
whichever we prefer.

If the condition is true the text is output with macro/script expansion as
normal; if the condition is false the text is skipped.

The if...then...else structure:

    %DEFINE_VARIABLE OS[Linux]

    %CASE[$Var{'OS'} eq 'Linux']
    Linux specific stuff.
    %CASE[#OS ne 'Linux']
    Non-linux stuff - note that both references to the OS variable are
    identical in the expression (#OS is converted internally to $Var{'0S'} so
    the eval sees the same code in both cases
    %END_CASE

Although nested C&lt;%CASE&gt;s are not supported we can get the same functionality
(and indeed more versatility because we can use full perl expressions), e.g.:

    %DEFINE_VARIABLE TARGET[Linux]

    %CASE[#TARGET eq 'Win32' or #TARGET eq 'DOS']
    Win32/DOS stuff.
    %CASE[#TARGET eq 'Win32']
    Win32 only stuff.
    %CASE[#TARGET eq 'DOS']
    DOS only stuff.
    %CASE[#TARGET eq 'Win32' or #TARGET eq 'DOS']
    More Win32/DOS stuff.
    %END_CASE

Although C&lt;macro&gt; doesn't support nested C&lt;%CASE&gt;'s we can still represent
logic like this:

    if cond1 then
        if cond2
            do cond1 + cond2 stuff
        else
            do cond1 stuff
        end if
    else
        do other stuff
    end if

By `unrolling' the expression and writing something like this:

    %CASE[#cond1 and #cond2]
        do cond1 + cond2 stuff
    %CASE[#cond1 and (not #cond2)]
        do cond1 stuff
    %CASE[(not #cond1) and (not #cond2)]
        do other stuff
    %END_CASE

In other words we must fully specify the conditions for each case.

We can use any other macro/script command within C&lt;%CASE&gt; commands, e.g.
C&lt;%DEFINE&gt;s, etc., as well as have any text that will be macro/script expanded
as normal.

=head2 Undefining with C&lt;%UNDEFINE&gt; and C&lt;undefine()&gt;

Macros and scripts may be undefined in files:

    %UNDEFINE *P
    %UNDEFINE_SCRIPT &lt;:DATESTAMP:&gt;
    %UNDEFINE_VARIABLE &amp;*!

and in code:

    $Macro-&gt;undefine( -macro, '*P' ) ; 
    $Macro-&gt;undefine( -script, '&lt;:DATESTAMP:&gt;' ) ; 
    $Macro-&gt;undefine( -variable, '&amp;*!' ) ; 


All macros, scripts and variables can be undefined:

    %UNDEFINE_ALL
    %UNDEFINE_ALL_SCRIPT
    %UNDEFINE_ALL_VARIABLE

    $Macro-&gt;undefine_all( -macro ) ;
    $Macro-&gt;undefine_all( -script ) ;
    $Macro-&gt;undefine_all( -variable ) ;

One use of undefining everything is to ensure we get a clean start. We might
head up our files thus:

    %UNDEFINE_ALL
    %UNDEFINE_ALL_SCRIPT
    %UNDEFINE_ALL_VARIABLE
    %LOAD[mymacros]
    text goes here

=head2 Listing macros, scripts and variables with C&lt;list()&gt;

We can list the macros, scripts and variables in code with list:

    $Macro-&gt;list( -macro ) ;

This will print the macros currently defined to the current file handle - if
there is one. If used in an array context will provide the list one macro per
array element:

    @macros = $Macro-&gt;list( -macro ) ;

    # Just give us the macro names:
    @macros = $Macro-&gt;list( -macro, -nameonly ) ;

There are equivalents for scripts and variables:

    @scripts   = $Macro-&gt;list( -script ) ;
    @variables = $Macro-&gt;list( -variable ) ;

=head2 Commenting

Generally the text files that we process are in formats that support
commenting, e.g. HTML:

    &lt;!-- This is an HTML comment --&gt;

Sometimes however we want to put comments in our macro source files that won't
end up in the output files. One simple way of achieving this is to define a
macro whose body is empty; when its called with any number of parameters (our
comments), their text is thrown away:

    %DEFINE %%[]

which is used like this in texts:

    The comment comes %%[Here | [anything] put here will disappear]here!

The output of the above will be:

    The comment comes here!

We can add the definition in code:

    $Macro-&gt;define( -macro, '%%', '' ) ;

Or the macro can be added automatically for us when we create the Macro
object:

    my $Macro = new Macro( -comment =&gt; 1 ) ; # All other options may be used
                                             # too of course.

However the easiest way to comment is to use C&lt;%CASE&gt;:

    %CASE[0]
    This unconditionally skips text up until the end marker since the
    condition is always false.
    %END_CASE

=head1 EXAMPLES

(See DESCRIPTION.)

=head1 BUGS

Lousy error reporting for embedded perl in most cases.

=head1 CHANGES

1999/08/18  Created.

1999/08/22  Version 1.00.


=head1 AUTHOR

Mark Summerfield. I can be contacted as &lt;summer@chest.ac.uk&gt; -
please include the word 'macro' in the subject line.

=head1 COPYRIGHT

Copyright (c) Mark Summerfield 1999. All Rights Reserved.

This module may be used/distributed/modified under the LGPL. 

=cut
</pre></body></html>