Pretty-print tables are used to specify how language constructs have to be pretty-printed and are used in combination with GenericPrettyPrinter front-ends. Pretty-print tables use Box as language to specify formatting of language constructs. A pretty-print table contains mappings from constructor names of SDF language productions to Box expressions.
For example, for the SDF production
f ( A ) -> B {cons("f")}
A pretty-print entry looks like:
f -- H [ "f" "(" _1 ")" ]
Pretty-print tables are ordered such that pretty-print rules occuring first take preceedence over overlapping pretty-print rules defined later. The syntax of pretty-print tables is available in the online Sdf.Trash.GrammarBase.
To be able to specify formattings for all nested constructs that are allowed in SDF productions, so called selectors are used in pretty-print tables to refer to specific parts of an SDF production and to define a formatting for them. For example, the SDF prodcution
f ( A? ) -> B {cons("f")}
contains a nested symbol A?. To specify a formatting for this production, two pretty-print entries can be used:
f -- H [ "f" "(" _1 ")" ] f.1:opt -- H [_1]
A selector consists of a constructor name followed by a list of number+type tuples. A number selects a particular subtree of an SDF construct, the type denotes the type of the selected construct (sequence, optional, separator list etc.). This rather verbose selector mechanism allows unambiguous selection of subtrees of SDF productions. Its verbosity (by specifying both the number of a subtree and its type), makes pretty-print tables easier to understand and, more importantly, it enables pretty-printing of AST's, because with type information, a concrete term can be correctly recontructed from an AST. Pretty-print tables can thus be used for both formatting of parse-trees and AST's.
Below we summarize which selector types are available:
Below we list a simple SDF module with productions containing all above constructs:
module Symbols exports context-free syntax A? -> S {cons("ex1")} A+ -> S {cons("ex2")} A* -> S {cons("ex3")} {S1 S2}+ -> S {cons("ex4")} {S1 S2}* -> S {cons("ex5")} "one" | "two" | S? -> S {cons("ex6")} ("one" "two" S? ) -> S {cons("ex7")}
The following pretty-print table shows which pretty-print rules can be defined for this syntax:
[ ex1 -- _1, ex1.1:opt -- _1, ex2 -- _1, ex2.1:iter -- _1, ex3 -- _1, ex3.1:iter-star -- _1, ex4 -- _1, ex4.1:iter-sep -- _1 _2, ex5 -- _1, ex5.1:iter-star-sep -- _1 _2, ex6 -- _1, ex6.1:alt -- KW["one"] KW["two"] _1, ex6.1:alt.1:opt -- _1, ex7 -- _1, ex7.1:seq -- KW["one"] KW["two"] _1, ex7.1:seq.1:opt -- _1 ]
The pretty-print rule 'ex6.1:alt' is a special case. It contains three Box expressions, one for each alternative. It is used to specify a formatting for the non-nested literals "one" and "two". During pretty-printing one of the three BOX expressions is selected, depending on alternative contained the term to format.
Pretty-print tables can be generated from SDF syntax definitions (see ppgen?). Generated pretty-print rules can easiliy be customized by overruling them in additional pretty-print tables. The tool pptable-diff notifies inconsistensies in pretty-print tables after the syntax definition has changed and can be used to brin inconsistent table up-to-date.