New box operators Z and I
The Z operator
The
Z
operator is similar to theV
operator, with the difference illustrated by the following figure:
V: …aaa
bbb
bbb
c…Z: …aaa
bbb
bbb
c…
where...
is (part of) a sibling box.As seen in the example, the difference between
V
andZ
is that the wholeV
box is aligned to the last character of the box preceding it, while theZ
box is aligned to the left margin, except for the first line.The I operator
So what defines the left margin? That is where the second new operator enters the playing field: the
I
operator.A variant of the
I
operator is also present in “CWI BOX”.
In that BOX dialect, theI
operator is (one of a few) context-sensitive operators: only in a vertical context it indents its children. Regardless of the context, it is equivalent to an H box.
This gives rise to some peculiarities: for example, anI is=2
box with two children"hello"
and"world"
in a vertical context results in the string" hello world"
, as both children are prefixed with two spaces, and then horizontally composed.The
I
operator I propose behaves a bit different. First, it is not context-sensitive: in any context,I is=N b
is equivalent to `H hs=N [S(""), b], modulo side effects. Second, it only takes a single child box, not a list of child boxes, so there is no need to decide whether its children should be composed horizontally or vertically (or line wrapped, etc.): that choice is delegated to the child box.So what makes the
I
operator slightly different from indentation usingH hs=N [S(""), ...]
orV is=N [...]
is its single side effect: theI
operator records the left margin as its x position plus the value of itsis
option. This left margin is then used by any nestedZ
operators.Rationale
These
Z
andI
operators are exactly the right abstraction for expressing constructs such as compound statement in control flow statements in languages with curly braces. For example, in
if (cond) |{
…
}|
the part between the pipe symbols (|
) could be a compound statement, pretty printed using aZ
box. TheZ
box allows the opening bracket to be placed on the same line as the if. This code style is not possible to generate with BOX as is, without complicated transformations that lift the opening bracket from theV
/Z
box of the compound statement to theH
box of the if statement.The example
else |if (cond) |{
…
}||
suggests the need for theI
operator. While the preceding example could possibly be implemented by aligning theZ
box contents to the parents’ left margin, this second example would require theZ
box contents to be aligned to the grandparents’ left margin. A separateI
operator that makes indentation explicit solves this, as both the condition of the nested if statement and the opening bracket haven’t been shifted to the right through indentation, but through other character data.This addition to BOX allows all possible brace styles to be generated. (those are: 1 - GNU style; 2 - braces on separate line, aligned with the “if” keyword; 3 - opening brace on same line as “if” keyword, else on new line; 4 - same as 3, but “else” is put on the same line as the closing brace)
Combined with a pattern matching pretty printer with BOX as back end this addition to BOX allows all possible brace styles in combination with all possible styles for putting a non-compound statement in a control flow statement (i.e., same line or next line).
Implementation
I would integrate the following two strategies with libstratego-gpp:
signature constructors
Z : S-OPTIONS * BOX-LIST -> BOX
I : S-OPTIONS * BOX -> BOXstrategies
//I is=N b' is equivalent to
H hs=N [S(""), b]’
// NOTE: this I box is not context sensitive!
// NOTE: this I box takes a single box as argument, not a list of boxes!
abox2text-I(rec : t * t -> t | xpos, width) =
?I(sopt, bs); {|Indent: <add> (xpos, <gpp-is-length> sopt) => left-xpos ; rules( GppIndent := left-xpos ) ; <fetch-elem(?SOpt(IS(), <id>)) <+ !"0"> sopt => value ; <rec(|xpos)> H([SOpt(HS(), value)], [S(""), bs]) |}
abox2text-Z(rec : t * t -> t | xpos, width) =
?Z(sopt, bs); GppIndent => indent ; <add> (indent, <gpp-is-length> sopt) => left-xpos ; <filter(gpp-is-real-vbox)> bs ; if ?[] then !([], xpos) else gpp-do-vbox-children(rec | xpos, left-xpos) ; gpp-format-vbox(|sopt, indent) end
I haven’t made any grammar changes for PP yet, but I could add those if that is desired.
So, any comments?
Can I commit those changes to libstratego-gpp? :-)
Submitted by Tobi Vollebregt on 19 December 2011 at 20:28
Issue Log
For consistency an
HZ
operator might be useful too, though I didn’t implement this. (HZ
would be toHV
whatZ
is toV
.)
Got a complete patch now, including HZ operator, changes to PP syntax, etc.
So, okay to commit?
Yes, sounds like good stuff Tobi.
Log in to post comments