Pretty printing and Document
The following import statements are required for using the pretty print extension.
imports silver:langutil;
imports silver:langutil:pp;
To declare a pretty print attribute on a nonterminal, attach it using a ‘with’ clause. For example, to add a pretty print attribute to a nonterminal called Root, add ‘with pp’ to the declaration of Root.
nonterminal Root with pp;
To return the string equivalent of a attribute, use the function show with the width of the string and the pp attribute itself.
abstract production root
r::Root ::= e::Expr
{
local attribute s::String;
s = show(100,r.pp);
...
}
The examples below show the document function and the strings they represent. Ex:
pp_constructor(params) = "string equivalent"
text("foo") = "foo"
space() = " "
semi() = ";"
comma() = ","
braces(foo) = "{" ++ foo ++ "}"
parens(foo) = "(" ++ foo ++ ")"
brackets(foo) = "[" ++ foo ++ "]"
notext() = ""
In general, these should NOT be preferred over just using that punctuation directly! These are useful only in situations where it make sense and it keeps things simpler.
For example, implode(comma(), list)
is okay, but as soon as you need a space, it’s better to just write implode(text(", "), list)
instead. Whatever you do, never write implode(cat(comma(), space()), list)
because that’s just horrible.
cat(foo, bar) = foo ++ bar
concat([ text("foo"), text("bar"), text("test")]) = "foobartest"
Folds a list of Documents together using catimplode(text("|"), [text("foo"), text("bar"), text("test")]) = "foo|bar|test"
Places a string between each element of a list of strings.terminate(text("|"), [text("foo"), text("bar"), text("test")]) = "foo|bar|test|"
Similar to implode. Places a string between each element of a list of strings AND at the end.
group(inner)
creates a new indentation group forinner
. That is, every occurrence ofline()
insideinner
that isn’t itself contained by an interveninggroup
will either ALL be horizontal (spaces) or all be vertical (newlines.) Every Document begins with an initial, implicit, outer group that is automatically vertical.line()
is a potential newline point, or a space if the group it belongs to decides to be horizontal instead. Note that in addition to becoming a newline, it will also emit a number of space equal to the current indentation level.nest(depth, inner)
increases the current indentation level for allline()
s ininner
. Note that it’s for all of them,nest
does not just affect the current group or anything like that.
groupnest(depth, inner)
performs both a group and a nest operation at once.nestlines(depth, inner)
gets right an extremely common pattern that’s slightly tricky to do. This construct nestsinner
and puts a newline before and after it. The before newline is inside the nest, but the after newline is OUTSIDE the nest. This is the correct behavior for, for example, nesting statements.softbreak()
is equivalent togroup(line())
. That is, it’s a potential place for a newline, but can be taken without committing to turning anything else into newlines versus spaces.box(inner)
is equivalent to anest
but sets the indentation level to whatever the current indentation is. This makes it easier to line things up vertically at an unknown resulting indentation level.
- Do not use
group
at all until you get to, say, expressions. Creating the option to place, for example, statements horizontally is usually a very bad idea. - Use
nestlines
because it gets indentation right.
As an example, to correct indent something like the braces after an “if statement” you would write:
braces(nestlines(2, implode(line(), stmts)))
This would produce outputs like:
if(expr) { <--- this opening brace is where the above starts
stmt1; <--- this is why the first newline is *inside* the nest
stmt2; <--- the 'implode' does NOT put the newline here, nestlines does.
} <--- the last newline is *outside* the nest, and followed by }