by Mike Parr - mikeparr@live.com
Webmacro is available for Microsoft Windows. It is written in Delphi Pascal, but also compiles under the portable Freepascal compiler, as packaged in the Lazarus IDE. I have not coded it for linux, but modifying it looks easy (Lazarus runs under Linux as well, of course). The main difference is CRLF for line-end under Windows, LF for Linux.
The source is available (around 500 lines) and the current download is a Windows exe, and the (open) source Pascal.
These notes explain the rationale behind webmacro, and also serve as a user manual.
A macro-processor is a program which performs prescribed insertions into a text file. The programmer specifies which changes are to be done by coding a 'macro definition'.
Do not confuse this kind of macro with keystroke recording. Sometimes these are called macros, but they are user-interface related, not to do with files of text.
There are many macro-processor programs. (They are easier to write than compilers, and many of programmers have had a go at them.) Which one to choose? Popular ones include M4 for Unix and GCC/linux (I think there is a Windows port), and the first one that got my attention was GPM, described in a paper
http://comjnl.oxfordjournals.org/content/8/3/225.full.pdf+html todo a link
by Christopher Strachey ( 'A General Purpose Macrogenerator') which shows the primitive power (and complexity) of macros.
The drawback of many powerful macro-processors is that their special 'unusual' characters have significance, such as * : % $ , " ' and space. Nowadays (especially if you work with html+css+javascript) these characters do crop up. You can program round this, but I thought I would roll my own, which was line-oriented rather than character-stream-oriented. Webmacro can work easily with most programming languages.
Imagine that ABC Ltd, a large company with UK and USA sections, is creating a website. Let us look at some HTML. Here, I will omit the <!DOCTYPE html> <html lang="en"><head><meta http...etc which starts most pages. This in itself is a prime candidate for being in a macro, but I want to focus on macros here, not HTML.
A product page might look like:
<html> <head> <title>ABC Ltd - Products Page</title> </head> <body> <h1>ABC Ltd</h1> <h1>Products</h1> <p> Lots of text here... etc </p> </body> </html>And a sales page might look like:
<html> <head> <title>ABC Ltd - Sales Page</title> </head> <body> <h1>ABC Ltd</h1> <h1>Sales</h1> <p> Lots of text here... etc </p> </body> </html>
As you can see, there is a lot of HTML boilerplate, and it is similar on each page. We can define a macro to create this text, which can also handle variations. Here is how.
We have 2 HTML files, for products and sales. We create a third macro-definition file, called (for example) defs.txt. It contains:
define $header <html> <head> <title>ABC Ltd - param1 Page</title> </head> <body> <h1>ABC Ltd</h1> <h1>param1</h1> enddefine
The words define, enddefine and param1 are special.
define and enddefine must be on a line by themselves, and must not be preceded or followed by spaces. (it is very unlikely that the text you are processing has this feature. In webmacro, we often use 'magic words' rather than symbols.)
Between define and enddefine we have the 'body' of the macro.
The body can contain any text, including the special strings param1 param2... up to param9. Again, an unusual word. Eventually, these param strings will be replaced by other text, when the macro is called.
$header is our choice of a macro name. It must start with $, but cannot be preceded or followed by spaces. (The name need not be letters, but a meaningful name is useful)
A definition file can contain any number of definitions, as in:
define $header ...body enddefine define $anyname ...body enddefine define $fred ...body enddefineAny text between enddefine and the next define is ignored.
Here is how products.html (our 'input file') might use (or 'call') the $header macro:
$header =Products <p> Lots of text here... etc </p> </body> </html>The macro name cannot be indented. On the lines immediately ofter a use of the macro name, we can put a series of parameters, or omit them completely - it depends on your definition. A parameter starts with =. A line not starting with = will end the series of parameters. The body of the called macro will be inserted in the output file, with the first parameter substituted for param1, the second parameter substituted for param2 etc. Lines in the input file that do not start with $ or = are just copied to the output file unaltered. The resulting output file holds the above long-winded version of products.html, i.e:
<html> <head> <title>ABC Ltd - Products Page</title> </head> <body> <h1>ABC Ltd</h1> <h1>Products</h1> <p> Lots of text here... etc </p> </body> </html>
Replacing a macro call with its body and replaced parameters is often termed 'expanding' the macro.
Moving on, let us suggest that our company has different UK and USA pages. They might re-write the definition:
define $header <html> <head> <title>ABC Ltd - param1 (param2)Page</title> </head> <body> <h1>ABC Ltd - param2 Division</h1> <h1>param1</h1> enddefineand then call the macro by:
$header =Products =UKWe might also add another definition:
define $footer </body> </html> enddefineso the input file could simply be:
$header =Products =UK <p> Lots of text... </p> $footer
We need to specify 3 files. There is a macro definition file, an input file vwith macro calls in it, and an output file. The command line is:
webmacro definition-file input-file output-fileWe run this command quite often, on a bunch of files, so it could become a script or bat file. On Windows, we might put these lines in a file (e.g. runmacro.bat):
webmacro defs.txt products.html ..\processed\products.html webmacro defs.txt sales.html ..\processed\sales.html pauseOn Windows, the pause keeps any error messages on screen. I find it convenient to use a file structure such as:
toProcess (directory/folder for input) defs.txt products.html sales.html runmacro.bat processed (directory/folder for output) products.html sales.htmlAlso I have put webmacro.exe in the toProcess folder. Replace this with the full path to your version.
There are a small number of run-time errors, which cause the program to display a message, then halt:
If you supply too few parameters for a call, the param-n strings with unsupplied parameters will not be replaced. Processing continues.
If you supply too many parameters in a call, the extra ones are not used. Processing continues.
This concludes the main features. Below are some additional. Most macro-processors have similar things.
The string nextnumber is treated specially in a body - it is replaced initially by 0
The next time you use it, it is replaced by 1 (then 2, 3 etc...)
You can use it to number sections of text. Here is a macro used to number the sections of chapter 3 of a document:
define $ch3section Section 3.nextnumber enddefineand here is how we might call it:
$ch3section Lots of text etc $ch3section Lots of text etcThe output is:
Section 3.0 Lots of text etc Section 3.1 Lots of text etcThis gives an auto-renumbering feature.
The string thisfilename is replaced by the name of the current input file, relative to its directory (e.g. mycalls.txt), with no path info.
Useful when you want to replace small items that are not worthy of a macro. For example, in a .css stylesheet, you get lines like:
background-color: #C8CBBA;The hex number might be used in several places in the file. Instead, we can put this in the definition file:
definesymbol paleBackground #C8CBBA enddefinesymbolIn the input file (the .css one) we can put:
background-color: paleBackground;Webmacro replaces every paleBackground string by the hex string. The gives us a kind of constant to be used throughout a file.
Warning about definesymbol: you could specify that 'red' gets replaced by 'FF0000'. However, watch out for replacements happening in words such as 'colored', which ends in 'red'. So, pick unusual words, or add some special symbols around the string, such as '*red*'. You might also use spaces (the replacement and original text items can have leading and trailing spaces.)
There are a variety of ways in which nesting could be imagined (e.g. putting a call in a definition, putting a definition in a call, etc. In webmacro, the most common case is covered: putting a call in a definition.
Here is an example: the header of a web page might also contain a menu. If we want to use the menu elsewhere, it might be useful as a macro. So, our $header macro definition could usefully contain a call of a $menu macro, for example:
define
$header
<html>
<head>
<title>ABC Ltd - param1 Page</title>
</head>
<body>
$menu
<h1>ABC Ltd</h1>
<h1>param1</h1>
enddefinedefine
$menu
HTML code for the menu
...etc
enddefine
If we now expand $header, we get the text of $menu as well. Parameters can be passed from $header into $menu.
Below is an example of a menu, which also uses some webmacro functions.
Sometimes, some additional capabilities can be useful - but they are not used frequently. In webmacro, there is an unusual syntax for functions. It has been made unusual so that it does not clash with the other languages you are using. A function has these forms:
functionName::string1::string2::string3Here are some examples:
doifequal::$fred::true::$doThis doifnotequal::param1::super::$anymacIf string1 or 2 is a macro name, it is replaced by the strings true or false. True if the macro has been expanded once, false if not.
If string 1 or 2 uses a param-n item or nextnumber, they are replaced before the comparison happens.
If string1 equals string2, string3 is put on a line by itself, and - if it is a macro - is expanded. If not a macro, string3 is unaltered. The function must be on one line only, so to get several lines conditionally treated, you must put, e.g.:
doifequal::$fred::true::$doThis doifequal::$fred::true::=parameter for doThisdoifnotequal has a similar - but reversed - effect.
Here, a series of pages(home.html, books.html etc) have a menu, to let the user navigate to other pages. We want the current page link to have a different colour to the rest of the pages. Here is how. Each menu item is a macro with 2 parameters. The visible text, and the html file name. The calls might be:
$menuitem =Home =index.html $menuitem =Books =books.htmThey could be in the htlm file, or we could nest them inside a $wholemenu macro. Here are the definitions:
define $menuitem <tr> $clicked =param2 <a href="param2">param1</a></td></tr> enddefine define $clicked doifequal::thisfilename::param1::<tr><td class="bg_orange" > doifnotequal::thisfilename::param1::<tr><td class="bg_dark" > enddefineI'm using a table for a set of buttons here (bad, I know, but the main point is that the $clicked macro produces different text, depending on what file calls it. Mostly, it returns the bg_dark text item, but when the current page name matches param2, it returns the orange text item. The benefit is that the exact same macros apply to each page, but tailor themselves.