The PPML library¶
Program notes need to be stored and later displayed in browsers. This
presents us with two problems. At the point of creation we have no way of
knowing the column width that will be used when the note is displayed.
There may even be more than one width if we want to be smart when a browser
window is resized. A second problem arises if we store a program note in
the form of a condition string + arguments and the arguments are context
sensitive. We could just save everything as a string, but then the logical
structure of the message is lost. An alternative is to store the text in
a more structured form. This is the purpose of the <ppml>
class
and its derivatives. The interface is based on Oppen’s 1980 TOPLAS paper.
A PPML document, represented as <ppml>
is a tree of PPML tokens.
The tokens available and their associated constructor functions are:
Structure is provided through instances of <ppml-block>
and
<ppml-separator-block>
as they can contain subnodes of PPML.
Constructing a PPML Document¶
Constructing a PPML document is typically done by using the various constructor functions:
define compiler-sideways method as
(class == <ppml>, o :: <&generic-function>)
=> (ppml :: <ppml>)
let sig = model-signature(o);
if (sig)
ppml-block(vector(ppml-string(o.^function-name),
ppml-break(),
as(<ppml>, sig)))
else
ppml-string(o.^function-name)
end;
end method;
Printing a PPML Document¶
Given a PPML document, the best way to print it is via ppml-print
:
ppml-print(ppml, make(<ppml-printer>, margin: 100));
The PPML module¶
PPML Tokens and Constructors¶
- <ppml> Abstract Class¶
- Superclasses:
- Discussion:
The abstract base class for all PPML tokens.
- <ppml-block> Class¶
- Superclasses:
- Init-Keywords:
break-type – An instance of
<ppml-break-type>
.constituents – An instance of
<ppml-sequence>
.offset – An instance of
<nat>
.
- Discussion:
To add structure to the output, we can package up a sequence of tokens into a block. There are a couple of attributes associated with a block. The offset indicates how much to indent subsequent lines of the block if a break is necessary. When a block is longer than a line then we have a number of options. We can display as much on each line as possible, only breaking when really necessary. Alternatively, we can break the block at each break point, e.g.:
aaa aaa bbb bbb ccc ddd ccc ddd
The choice of layout depends on whether the break-type attribute of the block is
#"consistent"
or#"inconsistent"
. The third alternative is#"fit"
. This suppresses all breaks and truncates the output if it won’t fit on a line.The size of the block is cached in the block for efficiency.
- ppml-block Function¶
- Signature:
ppml-block (constituents #key offset type) => (ppml)
- Parameters:
constituents – An instance of
<ppml-sequence>
.offset (#key) – An instance of
<nat>
.type (#key) – An instance of
<ppml-break-type>
.
- Values:
ppml – An instance of
<ppml-block>
.
- Discussion:
Construct a
<ppml-block>
.
- <ppml-break> Class¶
- Superclasses:
- Init-Keywords:
- Discussion:
A
<ppml-break>
indicates a position in the output where it is permissible to break the output if it won’t fit on a single line. If we don’t need to break the line then we output blank-space spaces. If we do need to break then we indent offset spaces relative to the current line indent.
- ppml-break Function¶
- Signature:
ppml-break (#key space offset) => (ppml)
- Parameters:
- Values:
ppml – An instance of
<ppml-break>
.
- Discussion:
Construct a
<ppml-break>
.
- <ppml-browser-aware-object> Class¶
- Superclasses:
- Init-Keywords:
object – An instance of
<object>
.
- Discussion:
The browser “knows” about some of the objects manipulated by the compiler, e.g. the various kinds of definition, and so we store these directly. Furthermore we recompute the ppml representation of the object every time token-size is called as the representation may depend on browser settings.
- ppml-browser-aware-object Function¶
- Signature:
ppml-browser-aware-object (o) => (ppml)
- Parameters:
o – An instance of
<object>
.
- Values:
ppml – An instance of
<ppml-browser-aware-object>
.
- Discussion:
Construct a
<ppml-browser-aware-object>
.
- <ppml-separator-block> Class¶
- Superclasses:
- Init-Keywords:
separator – An instance of
<ppml-sequence>
.
- Discussion:
When constructing blocks representing collections it is wasteful to explicitly store the separators between elements. The
<ppml-separator-block>
class captures this common case.The default value for the separator is
vector(ppml-string(","), ppml-break(space: 1))
.
- ppml-separator-block Function¶
- Signature:
ppml-separator-block (constituents #key separator offset type left-bracket right-bracket) => (ppml)
- Parameters:
constituents – An instance of
<ppml-sequence>
.separator (#key) – An instance of
<ppml-sequence>
.offset (#key) – An instance of
<nat>
.type (#key) – An instance of
<ppml-break-type>
. The default value is#"inconsistent"
.left-bracket (#key) – An instance of
false-or(<ppml>)
.right-bracket (#key) – An instance of
false-or(<ppml>)
.
- Values:
ppml – An instance of
<ppml>
.
- Discussion:
Construct a
<ppml-separator-block>
.
- <ppml-string> Class¶
- Superclasses:
- Init-Keywords:
string – An instance of
<byte-string>
.
- Discussion:
The simplest ppml token is just a string.
- ppml-string Function¶
- Signature:
ppml-string (str) => (ppml)
- Parameters:
str – An instance of
<byte-string>
.
- Values:
ppml – An instance of
<ppml-string>
.
- Discussion:
Construct a
<ppml-string>
.
- <ppml-suspension> Class¶
- Superclasses:
- Init-Keywords:
cache-token? – An instance of
<boolean>
. The default value is#t
.pair – Either an instance of
<ppml>
or a<pair>
of<function>
and its arguments.
- Discussion:
Sometimes it is more space efficient to delay the construction of the
<ppml>
equivalent of an object until we need to print it. The<ppml-suspension>
class supports this. It contains either a<ppml>
token, or a pair of a function and its arguments. When we need a token and encounter the pair we apply the function to its arguments. This should return an instance of<ppml>
. Optionally we can overwrite the pair by the result.
- ppml-suspension Function¶
- Signature:
ppml-suspension (fun #rest args) => (ppml)
- Parameters:
fun – An instance of
<function>
.args (#rest) – An instance of
<object>
.
- Values:
ppml – An instance of
<ppml-suspension>
.
- Discussion:
Construct a
<ppml-suspension>
.
Conversion to PPML¶
- as(class == <ppml>, <object>) Method¶
- as(class == <ppml>, <byte-string>) Method¶
- Parameters:
class – The class
<ppml>
.object – An instance of
<byte-string>
.
- Values:
ppml – An instance of
<ppml>
.
- Discussion:
Returns the quoted string value as PPML.
- as(class == <ppml>, <symbol>) Method¶
- as(class == <ppml>, <collection>) Method¶
- Parameters:
class – The class
<ppml>
.object – An instance of
<collection>
.
- Values:
ppml – An instance of
<ppml>
.
- Discussion:
Returns PPML representing the collection as a comma-separated list surrounded by
#(...)
.
- as(class == <ppml>, <explicit-key-collection>) Method¶
- Parameters:
class – The class
<ppml>
.object – An instance of
<explicit-key-collection>
.
- Values:
ppml – An instance of
<ppml>
.
- Discussion:
Returns PPML representing the collection.
- as(class == <ppml>, <vector>) Method¶
Printing / Formatting¶
- format-to-ppml Generic function¶
- Signature:
format-to-ppml (string #rest args) => (ppml)
- Parameters:
string – An instance of
<byte-string>
.args (#rest) – An instance of
<object>
.
- Values:
ppml – An instance of
<ppml>
.
- Discussion:
We insert breaks at the places where arguments are inserted in the format string. This will hopefully give us reasonable output, but not always as good as we could do by hand. We separate out the processing of the format string so that we can share the constant components of the resulting ppml-block if the same format expression is used multiple times.
- ppml-format-string Generic function¶
- Signature:
ppml-format-string (string) => (f)
- Parameters:
string – An instance of
<byte-string>
.
- Values:
f – An instance of
<function>
.
- Discussion:
Used by
format-to-ppml
.
- ppml-print Generic function¶
- Signature:
ppml-print (t pp) => ()
- Parameters:
t – An instance of
<ppml>
.pp – An instance of
<ppml-printer>
.
- Discussion:
This is the best way to display PPML.
- ppml-print-one-line Generic function¶
- Signature:
ppml-print-one-line (t pp) => ()
- Parameters:
t – An instance of
<ppml>
.pp – An instance of
<ppml-printer>
.
- Discussion:
This prints in the same way as
ppml-print
, but will limit the output to a single line. It does this by internally using a line-break-type of#"fit"
.
- <ppml-printer> Class¶
- Superclasses:
- Init-Keywords:
margin – An instance of
<nat>
.newline-function – An instance of
<function>
, taking no arguments. The default value writes a newline to*standard-output*
.output-function – An instance of
<function>
, taking a single<string>
argument. The default value writes to*standard-output*
.terse-depth – An instance of
<integer>
. The default value is100
.
- Discussion:
When outputting ppml we need to keep track of the space left on the current line and the current margin. We store these values in a
<ppml-printer>
object, along with the functions used to display text and line breaks.The terse-depth is used to limit recursion amongst
<ppml-block>
instances. Once printing has recursed through terse-depth blocks, it will change the break-type to#"fit"
to abbreviate things.
Type Aliases and Constants¶
- <nat> Type¶
- Equivalent:
limited(<integer>, min: 0)
- <ppml-break-type> Type¶
- Equivalent:
one-of(#"consistent", #"inconsistent", #"fit")
- <ppml-sequence> Type¶
- Equivalent:
- Discussion:
Note
This should be
limited(<sequence>, of: <ppml>)
.
- $line-break Constant¶
- Value:
ppml-break(space: 999)
- Discussion:
A way to force a line break by making a break with a space larger than the column width.