Parsers generated by Metal specifications store comments as annotations on the abstract syntax tree. A comment may be stored in one of two annotations, named prefix and postfix, depending on whether the comment should be pretty printed before or after the subtree to which it was attached.
The comment is stored as a tree whose associated (predefined) operator is comment_s, defined to be a list of comment lines. Each line is itself a subtree whose associated (predefined) atomic operator is named comment. Finally, the actual string of the comment is the value of this atomic operator. This implies that in order to pretty print a comment tree, we must add rules to our pretty printer to handle the comment_s and comment operators.
Ppml's pattern matching mechanism allows you to recognize a tree annotation and filter the annotation value (like an atomic value). The following pattern matches the annotation named myannot on any subtree;
*x ^myannot -> ...
The annotation value is stored in the variable ^myannot which may be used in the formatting part of the rule.
N.B. matching an annotation has the effect of rendering the annotation invisible for recursive calls, so that the pretty printer does not enter an infinite loop.
The following lines handle the comment annotations:
*x ^postfix -> [<hv> *x [<v> ^postfix ""]] ; *x ^prefix -> [<v> ^prefix *x] ;
The first line reformats the current tree (with the postfix annotation invisible), then the annotation value (tree whose operator is comment_s). The nested box has a vertical separator followed by the empty string at the end to ensure that the comment ends with a newline. The second line formats the annotation value, then reformats the current tree. These recursive calls on the same subtree are necessary because the comment annotation is independent of the subtree's arity.
Now we add two more lines to handle the annotation value: a comment subtree:
comment *x -> [<h> "%" stringpp(*x) ] ; comment_s (**x) -> [<v> (**x) ] ;
The first line filters the comment string and reinserts the % symbol that was dropped during lexing. In this line, we explicitly handle the atomic value, bypassing the default mechanism. When an atomic value is not a subtree, we must tell Ppml to stop recursion. We accomplish this by calling an external function with the value as argument. Ppml offers a number of predefined functions to handle all sorts of values. In this example, stringpp simply returns the string value of the comment tree. The formatting machine requires an external function call to return a string or any value that responds to the Le-Lisp function string.
The second line treats a comment list by superposing lines.