Like JSP only Faster!
   
 

1 The Moto Preprocessor

Even without making use of moto's programmatic constructs, the moto preprocessor can do away with much of the repetition involved in hand coding pages of HTML. It provides functionality for text file inclusion and macro substitution.

1.1 Text File Inclusion

#include(moto page)  

Just as in the C preprocessor the include directive is used to copy the contents of another file into a moto page at the point in the page where the directive is used. This directive may be used as a replacement for server side includes to include image maps or common web page navigation. Moreover, since file inclusion occurs in the preprocessor phase, included files can contain moto source code. The path to the include is relative to the including page or, if the path begins with a /, is relative to the file system root.

1.2 Macro Substitution

The moto preprocessor also has the capability to do macro expansion within moto pages. Macros are used like moto constructs in that they also begin with a dollar sign. This document uses macros to format each of it's examples. The example above was formatted by the following code:

$BEGIN_USAGE
   #include(path to include)
$END_USAGE
 

Macros may require arguments. These arguments need not (and often should not) be quoted as they will be substituted without modification where they are used in the macro definition. Argument lists are comma delimited thus if one argument contains a comma (or a close paren ')') it must be escaped with a backslash '\'. An example of a macro that takes arguments used in the construction of this document is the macro used for specifying section headers:

$BEGIN_SECTION("Macro Substitution")  

The #readdefs preprocessor directive makes the macros contained in a text file (called a macro library) available for substitution in the preprocessing phase.

#readdefs(path to macro library file)  

The path to the macro library can be relative to the page containing the readdefs directive or fully qualified from your root directory. Macro libraries have names ending with '.lib'. The rules for constructing a library file are as follows:

  1. Lines beginning with a pound sign '#' are ignored. These are comments in the library file.
  2. Lines beginning with text (without indentation) begin a macro definition. The first word on that line is the macro name.
  3. If the macro is to take arguments, the macro name should be immediately followed by an open paren '(', then a comma delimited list of argument names, and finally a close paren ')'.
  4. Lines beginning with white space are considered part of the current macro definition thus a macro definition ends when the file ends or the next macro definition begins.
  5. Macros may be used in the definitions of other macros but self referential macro calls are prohibited.
  6. A macro argument is used within the body of a macro as if it were another macro i.e. they are called by $macro argument name

The macro library used in the creation of this document looks like:

#
# Called at the beginning of a document, this macro sets up the variables
# needed to create and store the outline of that document
#

INIT_FORMAT
  $use("codex.util")

  $declare(int levelCounter)
  $declare(int chapNum = 0)
  $declare(Stack outline = new Stack())
  $declare(Vector curLevel = new Vector())
  
  $do(outline.push(curLevel))

#
# This macro should be called at the beginning of a section or
# or subsection with the header for that section or subsection. It
# will add the section header to the outline and compute and display
# the appropriate section / subsection number. It will also insert an HTML
# anchor tag so that this section may be linked to from the outline
# (autmatically generated by the DISPLAY_OUTLINE macro)
#

BEGIN_SECTION(section)

  $do(curLevel = <Vector>outline.peek())

  $do(curLevel.add($section))
  $do(curLevel.add(new Vector()))

  <h3>
     $for(levelCounter=outline.size();levelCounter>1;levelCounter--)
        $((<Vector>(outline.peekAt(levelCounter))).size()/2 +
          (levelCounter==outline.size()?chapNum:0)).
     $endfor
     $(curLevel.size()/2 + (levelCounter==outline.size()?chapNum:0))
        <a name="$($section)">$($section)</a>
  </h3>

  $do(outline.push(curLevel.get(curLevel.size()-1)))

#
# This macro should be called at the end of a section or subsection
#

END_SECTION $do(outline.pop())

#
# This macro is used to generate the HTML for a hyperlinked outline of
# a document marked up with the preceding macros
#

GENERATE_OUTLINE(outputFile)

  $declare(int curIndex = 0)
  $declare(IntStack istck = new IntStack())
  $declare(Stack ostck = new Stack())
  $declare(StringBuffer obuffer = new StringBuffer())

  $do(ostck.push(outline.peek()))
  $do(istck.push(0))

  $while(ostck.size() > 0 )

     $do(curLevel = <Vector>ostck.peek())
     $do(curIndex = istck.peek())

     $if(curIndex == curLevel.size() )
        $do(curLevel = <Vector>ostck.pop())
        $do(curIndex = istck.pop())

        $if(istck.size()>0)
           $do(istck.push(istck.pop()+2))
        $endif

        $continue
     $endif

     $for(levelCounter=istck.size();levelCounter>0;levelCounter--)
        $do(obuffer.append(istck.peekAt(levelCounter)/2 + 1))
        $do(obuffer.append('.'))
     $endfor

     $do(obuffer.append("<a href=\"docs"))
     $do(obuffer.append(istck.peekAt(istck.size())/2 + 1))
     $do(obuffer.append(".moto#"))
     $do(obuffer.append(<String>curLevel.get(curIndex)))
     $do(obuffer.append("\" onClick=\"return targetopener(this)\">"))
     $do(obuffer.append("<nobr>"))
     $do(obuffer.append(<String>curLevel.get(curIndex)))
     $do(obuffer.append("</nobr>"))
     $do(obuffer.append("</a><br>\n"))

     $do(istck.push(0))
     $do(ostck.push(curLevel.get(curIndex+1)))

  $endwhile

  $do(putFile($outputFile,obuffer.toString()))