A Ppml rule's pattern contains textual and variable parts. A pattern matches a subtree when the textual part matches exactly and the variable parts may be instantiated (e.g., the signature of the tree's operator must match that of the pattern). The textual parts of the pattern are operator names from the associated abstract syntax specification. Variables represent descendents, atomic values, annotation names, print level, and other values.
Suppose we want to match the operator named plus which, as defined in the abstract syntax, has two descendents:
plus -> EXP EXP;
We would write the pattern:
where *x and *y are variables that will be instantiated farther down the tree as other expressions. The results of subsequent instantiations are used in the format part of the rule. For plus we choose the format:
*x "+" *y
Variables that represent lists are prefixed by two asterisks, so that the pattern corresponding to a list of expressions defined in Metal as,
exp_s-> EXP + ...;
Variables in list arity operators may partition the list in a variety of ways, so if we want to extract the first expression in our list, for example, we would break it into a head and tail as follows:
The operator exp_s still has one descendent, but since the descendent is a list, we may divide it up as we wish. The following pattern filters out the first and last elements of the list:
The following pattern, however, is not legal:
since Ppml wouldn't know how to divide the list.
You may also match lists containing a specific number elements by using variables other than list variables. Thus, the pattern:
matches the list node exp_s when it contains only two elements. Do not confuse this with a fixed arity pattern with two descendents.
Variable names are only visible within a given rule and bear no relation to abstract operator names. We strongly suggest using meaningful names (such as phyla names) as mnemonic devices.
It is possible to have more than one pattern for the same abstract syntax node. For example, we may want to treat empty lists differently from non-empty ones:
exp_s() -> ... exp_s(**exp) -> ...
When the first of the above rules doesn't match (an empty list of expressions), the second is applied.
Ppml applies rules in the order in which they are encountered. Thus, the pattern:
*anynode -> ...
which matches any node, should only appear at the end of a file, otherwise it would hide all subsequent rules. Such a rule is dangerous, even at the end of a program, because it matches everything, so you may not notice program bugs. Missing rules produce helpful warning messages during compilation, but no error since the generic pretty printer handles these nodes. In fact, the generic pretty printer allows you to develop a pretty printer incrementally since you may work on certain rules at a time, without worrying about the entire abstract syntax.