Nic Volanschi – Language Migration Expert at Metaware – Presents safe clone based refactoring solution at the IWSC convention in Zurich, Switzerland

Presented at the 2012 IWSC convention in Zurich, Switzerland, Metapp is a general-purpose text preprocessor featuring a minimalistic macro language. Its primary intended purpose is to add macros to legacy programming languages such as Cobol or JCL, but it can be used in a wider spectrum of text processing applications.


 

Metapp is implemented in Perl and provides access to a significant part of Perl within the macro language. When used in conjunction with a programming language, macros are a powerful mechanism for code factorization. The metapp implementation of macros is considerably more powerful both than Cobol's COPY REPLACING and C's preprocessor (CPP) directives.

Downloads

Metapp consists of only one Perl file: metappThere is a research paper describing Metapp and one application of it to program refactoring:

Nic Volanschi | Language Migration Expert at Metaware presents Safe Clone-Based Refactoring through Stereotype Identification and Iso-Generationat the IWSC'12 Convention in Zurich, Switzerland | 6th International Workshop On Software Clones, 2012.  More about metapp...
 

Syntax

The syntax of a source file to be preprocessed is very simple:

  • each line starting with a '#' (after any number of spaces) is considered to be a line in the macro language interpreted by metapp (also called a preprocessor line)
  • any other line (also called a text line) is reproduced by metapp in the output, after:
    • substituting any Perl interpolated variable ($name or ${name}) with its current value
    • if option -a is given, substituting any Perl interpolated expression (@{[perl-expr]}) with its computed value However, see option -p to inhibit substitutions on some text lines. Metapp lines (starting with '#') can be of the following types:
  • ## text
    • This line is simply skipped by metapp. It can serve to place comments that will not appear in the preprocessed file. Note that there is no space between the two '#' (although xwiki makes it seem such!)
  • # perl-command
    • Evaluate the given Perl command. Note the mandatory space after the '#'. The most common use of this command is for assigning a macro-variable.
    • Note that variables assigned this way are global. This means that a variable assigned in some file can be retrieved both in files included by the current file and in files including the current file.
  • #copy macro-name( [arg, ...] )
    • This line is substituted by the result of preprocessing the file macro-name, searched in the directories $MACRODIR, after passing the arguments, if any. See option -x for specifying a file extension to be added automatically to stub and macro names. The file stub-name obeys to the same syntax conventions as source-file. Arguments can be used by the macro using the command #bind below.
  • #copy stub-name
    • This line is substituted by the result of preprocessing the file stub-name, searched in the directories $STUBDIR. See option -x for specifying a file extension to be added automatically to stub and macro names. The file stub-name obeys to the same syntax conventions as source-file. Thus, a stub is just a simplified form of macro that is not parameterized (i.e. cannot take arguments), and lives in a distinct namespace (i.e. is searched in a distinct list of directories).
  • #bind $arg1[=defval1], ...
    • Bind the Perl formal arguments $arg1 ... to the actual arguments passed to the current macro. If an actual argument is absent or undefined (i.e. has the Perl value undef), the corresponding formal argument is bound to the optionally specified default value. If both the actual argument and the default value are missing, the formal is undefined. The #bind statement is usually the first statement in a macro taking arguments. In a macro without arguments or in a stub, this statement is useless.
    • Formal arguments are dynamically scoped, i.e. they exist until exiting the current file; in particular, these variable are also visible in files included by the current one (unless the variables are redefined). If some variable with the same name existed before the current statement, its old definition is shaded until exiting the current file.
  • #let $var=expr
    • Define a local variable $var and assign to it the result of evaluating the Perl expression expr.
    • Local variables are dynamically scoped (see #bind).
  • #if condition ... #else ... #fi
    • The condition is evaluated as a Perl expression. If the result is true (in Perl), the lines between the #if line and the #else line are preprocessed and the lines between the #else line and the #fi line are skipped. If the result is false, the former lines are skipped and the latter lines are preprocessed.
  • #while condition ... #end
    • The condition is evaluated as a Perl expression. If the result is true, the lines between the #while line and the #end line are preprocessed, after which the #while line is preprocessed again in the same way. If the result is false, the lines between the #while line end the #end line, as well as the #end line itself, are skipped.
  • #exit
    • This line does not generate any output. It only ends the preprocessing of the current (macro or stub) file, and continues preprocessing right after the calling #copy line, if any.
  • #log message
    • The message is evaluated as a Perl interpolated string and the result is output on stderr. Any line can be continued on the following line by:
  • appending a '\' at the end of the line (which may followed only by some trailing spaces)
  • starting the following line with '#...' (which may be preceded by any number of spaces).

 

Several #if and #while constructs can be (properly) nested.

Invocation

Synopsis:
  • metapp [options] source-file
Arguments:
  • source-file: the source file to be pre-processed
Options:
  • -i Initfile: a Perl file to be executed before the source-file is preprocessed.
    • default: no initialization file
    • This file can serve to perform various initializations. It is executed with the Perl "require" command, which means that the last command in the file has to return a value equivalent to "true" (e.g. 1).
  • -M Macrodirs: a list of directories containing macro files
    • default: "." (the current directory)
    • Directories in the list are separated by a colon (":"). When a macro is called using #copy macro(...), this list of directories is searched, in order, until a macro of the given name is found.
  • -S Stubdirs: a list of directories containing stub files
    • default: "." (the current directory)
    • Directories in the list are separated by a colon (":"). When a stub is called using #copy stub, this list of directories is searched, in order, until a stub of the given name is found.
  • -m Markerprefix: prefix for marker lines
    • default: no marker lines at all
    • If this option is present, the preprocessor introduces a marker line each time an included macro or stub starts or ends. The marker lines start with the given prefix and further contain the name of the file. This option is useful to clearly delimit text fragments coming from the expansion of a macro or stub.
  • -p Passregex: pass unchanged text lines matching the regex
    • default: process all lines
    • Text lines matching this regex are not substituted for embedded variables or arrays, but rather output as is. This may be used for instance to inhibit the substitution of macro variables within comments.
  • -a: interpolate Array variables
    • default: don't interpolate arrays
    • When this option is set, a macro variable x valued as a Perl array can be substituted using the @x notation. Also, Perl expressions can be evaluated and substituted with their result using the @{[expr]} syntax.    © Metaware