macro

declaration/definition

Syntax
macro macro-name
   ( (arg argument-name delimiter) | (token argument-name delimiter?))*
is
macro-body
macro-end

Argument definitions

macro-name
A macro name is either a name token or one or two punctuation characters. A name token consists of a letter or a high-order ASCII character (ASCII 128 to 255 inclusive) followed by any combination of letters, high-order ASCII characters, digits, underscores ("_"), hyphens ("-") and periods ("."). A punctuation character is anything from the following list: ( ) { } [ ] ! @ $ % ^ & * - + = | \~ : < , > . ? /. Specifically, the backquote character ("`") and the octothorpe (#) are not permitted in macro names, macro argument names, or macro argument delimiters. Punctuation characters do not need to be separated from other macro keywords by white space in the same way names need to be. White space can be added, however, to increase readability.
argument-name
A single valid name that represents a replaceable argument for the macro.
delimiter
One or more valid names that delimit the preceding argument or token from what follows.
macro-body
One or more keywords, names, punctuation characters, quoted strings, numbers, or comments. In fact, anything which can appear in a valid OmniMark program, except the words "arg", "token", "is", and "macro-end". To include these terms, or the word "literal" itself as part of a macro, precede them with "literal".


Purpose

Defines a macro.

Macros are used for two purposes -- to define constants and to extend the language. Defining constants is simple:

  macro five is 5 macro-end
  macro mary is "Mary had a little lamb%n" macro-end
  macro tom tom is "Tom, Tom, the piper's son.%n" macro-end
  
  find mary
     local integer x initial {five}
     output "%d(x)%n"
     output tom tom

Notice that a macro name can contain spaces, as in "tom tom" above.

The sample above shows that a constant defined by a macro can be used either as a text or number replacement or as a pattern. You can also include patterns in a constant to form a new pattern. This is the simplest form of extending the language. Here the pattern for finding valid SGML names is encapsulated by a macro:

  macro sgml-name is (letter [letter | digit | "-."]*) macro-end

Note the parentheses around the pattern elements in the macro. These are not required to define the macro, but they are a useful idea because they ensure that the new pattern will be treated as a single unit when the macro is expanded. Among other benefits, this ensures that sgml-name => name will work as you expect.

Extending the language becomes a little more complex if you need to add parameters to your macros. One extension is the "delimited-string" macro which finds a string between two delimiters that you specify. It is defined:

  ; delimited-string.xom
  macro delimited-string (arg open-d, arg close-d) is
     ((open-d) any** (close-d))
  macro-end
  
  process
  submit "[we dropped freddy (the rat) off the fire escape] into the alley with military honors."
  find delimited-string("[", "]") => body
    output "Body = " || body || "END-OF-STRING%n"
  ; Output: "Body = [we dropped freddy (the rat) off the fire escape]END-OF-STRING
  ;          into the alley with military honors.']

This macro illustrates several important points about macros. First of all, the name of this macro is not actually "delimited-string", but "delimited-string(". The parentheses are not part of OmniMark's syntax for defining macros, they are actually part of the syntax of the macro being defined.

So what about the closing parenthesis? It is the delimiter of the second parameter, close-d, which is defined by the keyword arg. In the case of the delimited-string macro a delimiter is needed, since the pattern passed to it could be complex and contain spaces:

  delimited-string("[START QUOTE]", "[END QUOTE]")

If you define a macro with multiple parameters you can do it like this, using traditional parentheses and commas for delimiters:

  macro sum ( arg n1, arg n2, arg n3) is
     n1 + n2 + n3
  macro-end
  
  process
     local integer n
     set n to sum (1, 2, 3)

But you can also define a macro using any delimiters you like, including ones that contain spaces:

  macro sum > arg n1 big dog arg n2 / arg n3 ! is
     n1 + n2 + n3
  macro-end
  
  process
     local integer n
     set n to sum >1 big dog 2/ 3!

The above code works but its meaning is not very clear. There are ways to make your language extensions clearer by using appropriate words or punctuation as delimiters:

  macro sum arg n1 and arg n2 and token n3 is
     (n1 + n2 + n3)
  macro-end
  
  process
     local integer n
     set n to sum 12 and 52 and 37
This code introduces a new way to parameterize a macro -- the token keyword. When using "and" as a delimiter you obviously don't want a delimiter after the last parameter. To define a macro without a following delimiter, you can use token instead of arg.

As its name implies, a token parameter can only be a single token -- a sequence of characters with no spaces, or a quoted string. The purpose of providing both "token" and "arg" parameterization is to allow you maximum flexibility in defining your language extensions. It has no effect on what kind on parameter you pass to a macro or how that parameter is used in the body of the macro. You are not prohibited from using a delimiter with a "token" argument, but it is not required.

There is one other important difference between "arg" and "token" parameterization. Parameters declared with arg may contain names of other macros, which will be expanded in the present macro. Parameters declared with token may be macro names, but will not be treated as such. Thus, you could pass a macro that expressed a complex pattern to the "upto" macro:

  macro at-dollar is ("@" letter+ "$") macro-end
  
  find "xyz" upto ( at-dollar )
But if "upto" were defined (less usefully) as taking a token argument, this would not work:
  macro upto token pat is
     ((lookahead not (pat)) any)+
  macro-end
  
  macro at-dollar is "@" letter+ "$" macro-end
  
  find "xyz" upto at-dollar  ;error!

You can use a format item to insert a macro argument into a string using the "@" format command:

  macro discontent token season is
     "Now is the %@(season) of our discontent"
  macro-end
  
  process
      output discontent "winter"

You can use the format modifiers "u" and "l" to force the text of the argument to uppercase and lowercase respectively.

The following restrictions apply to macro use: Where two macros have names which start with the same word or words, the longer name will always be matched if possible. Thus, given:

  macro go ...
  macro sleep ...
  macro go to ...
  macro to sleep ...
  
  process
     go to sleep
     go sleep
the sequence go to sleep will invoke the "go to" macro, followed by the "sleep" macro, not "go" followed by "to sleep". The sequence go sleep will invoke the "go" macro followed by the "sleep" macro.

Macros cannot represent partial names. Consider a program that uses long integer names like chapternumber, figurenumber, and contains the following macro definitions:

  macro chp is chapter macro-end
  macro num is number macro-end

The name chpnum is not a substitute for chapternumber.

Macros cannot be redefined. Once a macro name has been defined, another macro cannot be defined with the same name in the same OmniMark program. Attention has been paid to this requirement when an include declaration is used to combine macro definitions from different sources in a single OmniMark program.