Skip to content

Preprocessor Directives

Benjamin Kowarsch edited this page Jul 13, 2017 · 7 revisions

Macros

Definition

A macro shall be defined by a left-most opening tag which includes a comma delimited argument list enclosed by parentheses. The first argument shall be an identifier denoting the macro's name. It shall be mandatory. Further arguments shall be optional identifiers denoting the macro's parameters. The opening tag shall be followed by the body of the macro. The body shall contain its definition. The body shall be followed by a right-most closing tag.

macroDefinition :=
  openingTag body closingTag
  ;

openingTag :=
  '(*?MACRO(' macroIdent ( ',' macroParam )* ')*)'
  ;

alias macroIdent, macroParam = StdIdent ;

body :=
  printableCharacter*
  ;

closingTag :=
  '(*?END' _ macroIdent '*)'
  ;

Invocation

An invocation of a macro shall be denoted by the macro's identifier, followed by a comma separated argument list in parentheses, if and only if the macro's definition specifies any parameters.

macroInvocation :=
  '(*?' macroIdent ( '(' argList ')' )? '*)'
  ;

argList :=
  arg ( ',' arg )*
  ;

alias arg = <Modula-2 expression> ;

Example

Definition

(*?MACRO(TwosComplement, T, v)*)
MAX(T) - v + 1
(*?END TwosComlement*)

Invocation

subtrahend := (*?TwosComplement(CARDINAL, value)*);

Expanded Result

subtrahend := MAX(CARDINAL) - value + 1;

Functions

Definition

functionDefinition :=
  openingTag body closingTag
  ;

openingTag :=
  '(*?DEFUN(' functionIdent ( ',' functionParam )* ')*)'
  ;

alias functionIdent, functionParam = StdIdent ;

body :=
  printableCharacter*
  ;

closingTag :=
  '(*?END' _ functionIdent '*)'
  ;

Invocation

functionInvocation :=
  '(*?' functionIdent '(' argList? ')' '*)'
  ;

argList :=
  arg ( ',' arg )*
  ;

alias arg = <Modula-2 expression> ;

Example

Definition

(*?DEFUN(POW10, n)*)
(*?IF(n=0)*)1(*?ELSE*)POW10(n-1)(*?END*)
(*?END POW10*)

Invocation

(*?FORTO(i, 0, 5)*)
m[i] := (*?POW10(i)*);(*?NL*)
(*?END*)

Expanded Result

m[0] := 1;
m[1] := 10;
m[2] := 100;
m[3] := 1000;
m[4] := 10000;
m[5] := 100000;

Maps

Definition

mapDefinition :=
  openingTag body closingTag
  ;

openingTag :=
  '(*?MAP(' mapIdent ')*)'
  ;

body :=
  mapMember ( ',' mapMember )*  
  ;

alias mapIdent, mapMember = StdIdent ;

closingTag :=
  '(*?END' mapIdent '*)'
  ;

Invocation

mapInvocation :=
  mapIdent '(' indexArg ')'
  ;

alias indexArg = <Modula-2 expression> ;

Example

Definition

(*?MAP(RESWORD)*)
"ALIAS", "AND", "ARGLIST", "ARRAY", ...
(*?END RESWORD*)

Invocation

(* ##VER## version *)
(*?IF(ver=pim)*)
(*?FORIN(i, RESWORD)*)
Insert(table, RESWORD(i));(*?NL*)
(*?END FORIN*)

(*?ELIF(ver=iso)*)
table := { (*?FORIN(i, RESWORD*)RESWORD(i), (*?END*)};
(*?END IF*)

Expanded Results

if ver matches pim

(* PIM version *)
Insert(table, "ALIAS");
Insert(table, "AND");
Insert(table, "ARGLIST");
Insert(table, "ARRAY");
...

if ver matches iso

(* ISO version *)
table := { "ALIAS", "AND", "ARGLIST", "ARRAY", ... }

Builtins

IF-ELIF-ELSE

The IF-ELIF-ELSE directive shall insert source text within its body conditionally depending on a relational expression.

Source text placed between the opening tag and an optional ELIF or ELSE tag or the closing END tag will be copied into the output if the relational expression in the IF tag is true.

Source text placed between an optional ELIF tag and a following ELIF or ELSE tag or the closing END tag will be copied into the output if the relational expression in the IF tag is false, the relational expressions of any preceding ELIF tags are false, and the relational expression in the ELIF tag is true.

Source text placed between an optional ELSE tag and the closing END tag will be copied into the output if the relational expression in the IF tag is false and the relational expressions of any ELIF tags are false.

if :=
  openingTag body ( elifTag body )* ( elseTag body )? closingTag
  ;

openingTag :=
  '(*?IF(' term relOp term ')*)'
  ;

body :=
  printableCharacter*
  ;

elifTag :=
  '(*?ELIF(' term relOp term ')*)'
  ;

elseTag :=
  '(*?ELSE*)'
  ;

closingTag :=
  '(*?END' ( _ ident )? '*)'
  ;

Example

The following fragment of template source

PROCEDURE NewSet64
(*?IF(ver="16bit")*)
  ( VAR set : Set64; seg3, seg2, seg1, seg0 : CARDINAL );
(*?ELIF(ver="32bit")*)
  ( VAR set : Set64; seg1, seg0 : CARDINAL );
(*?ELIF(ver="64bit")*)
  ( VAR set : Set64; value : CARDINAL );
(*?END IF*)

will be expanded to the following output,

if ver matches 16bit

PROCEDURE NewSet64
  ( VAR set : Set64; seg3, seg2, seg1, seg0 : CARDINAL );

if ver matches 32bit

PROCEDURE NewSet64
  ( VAR set : Set64; seg1, seg0 : CARDINAL );

and if ver matches 64bit

PROCEDURE NewSet64
  ( VAR set : Set64; value : CARDINAL );

FORTO

The FORTO directive shall insert source text within its body repeatedly depending on a loop variant. The loop variant may be referenced within the body and any such reference shall be replaced with the value of the loop variant at each iteration.

forTo :=
  openingTag body closingTag
  ;

openingTag :=
  '(*?FORTO(' loopVariant ',' startValue ',' endValue ')*)'
  ;

body :=
  printableCharacter*
  ;

closingTag :=
  '(*?END' ( _ ident )? *)'
  ;

Example

The following fragment of template source

(*?FORTO(i, 0, 4)*)
m[i] := 0;(*?NL*)
(*?END*)

will be expanded to the following output

m[0] := 0;
m[1] := 0;
m[2] := 0;
m[3] := 0;
m[4] := 0;

CAT

The CAT directive shall be replaced in the output with the concatenation of its arguments.

cat :=
  '(*?CAT(' component ( ',' component )+ ')*)'
  ;

component :=
  printableCharacter*
  ;

Example 1

The following fragment of template source

(*?FORTO(i, 1, 80)*)
TYPE (*?CAT(String, i)*) = ARRAY [0..i] OF CHAR;(*?NL*)
(*?END*)

will be expanded to the following output

TYPE String1 = ARRAY [0..1] OF CHAR;
TYPE String2 = ARRAY [0..2] OF CHAR;
TYPE String3 = ARRAY [0..3] OF CHAR;
...
TYPE String80 = ARRAY [0..80] OF CHAR;

Example 2

The following fragment of template source

TYPE VariableLengthString = RECORD
  CASE length : CARDINAL OF
(*?FORTO(i, 1, 80)*)
  (*?IF(i=1)*) (*?ELSE*)|(*?END*) i : string : (*?CAT(String, i)*)(*?NL*)
(*?END FORTO*)
  END (* CASE *)
END; (* VariableLengthString *)

will be expanded to the following output

TYPE VariableLengthString = RECORD
  CASE length : CARDINAL OF
    1 : string : String1
  | 2 : string : String2
  | 3 : string : String3
  ...
  | 80 : string : String80
  END (* CASE *)
END (* VariableLengthString *)

EVAL

The EVAL directive shall be replaced in the output with the value of its argument. Its argument shall be an arithmetic expression.

eval :=
  '(*?EVAL(' expr ')*)'
  ;

alias expr = <Modula-2 expression> ;

Example

The following fragment of template source

1(*?FORTO(i, 1, 7)*), (*?EVAL(2*i+1)*)(*?END*)

will be expanded to the following output

1, 3, 5, 7, 9, 11, 13, 15