Forlan Manual


The PP Module


Synopsis

signature PP
structure PP :> PP

The module implements a simple form of pretty-printing.


Interface

val newline : string
type pp
val toString : pp -> string
val empty : pp
val block : bool * pp list -> pp
val decorate : string * pp * string -> pp
val quote : pp -> pp
val comma : pp -> pp
val colon : pp -> pp
val semicolon : pp -> pp
val fromString : string -> pp
val fromStringSplitEscape : string -> pp

Description

newline
is the operating system-dependent character sequence for separating lines in files. On UNIX-like systems, it is "\n", but on Windows it is "\r\n".

type pp
The type of pretty-printing expressions. The module can be understood as if pp were implemented as
  datatype pp = Block    of bool * pp list
              | Decorate of string * pp * string
but it's actually implemented in a way allowing more efficient processing.

toString pp
turns pp into a string where, if possible, all of the string's lines are no longer than the current printing line length (see Params.getPrintingLineLength). This translation is carried out using a recursive function that is supplied a value pp of pp to be turned into a string, together with the current indentation ind and the after size aft, i.e., the size of the string that will immediately follow the final line of the translation of pp. A recursive call is not responsible for generating the ind spaces that will preceed pp's translation in the overall translation.

Block(spaceNoBreak, pps) is turned into a string consisting of one of more lines, all but the first of which begin with ind spaces. This is done by recursively translating the elements of pps, in sequence, with appropriate indentations and after sizes. If the translations of a sequence of elements of pps—separated by blanks iff spaceNoBreak is true—fit on a single line (including the ind initial spaces, and leaving room for aft additional characters, if the final element of pps is included), then the elements' translations are combined in this way. But when an element's translation requires multiples lines, it is never combined with other translations, either before or after. The translation of the final element of pp, whether combined with other translations or not, must always be computed by taking into account that aft additional characters will immediately follow its last line.

Decorate(s1, pp, s2) is turned into the string consisting of the concatenation of:



empty
is Block(true, nil).

block
is Block.

decorate
is Decorate.

quote pp
returns Decorate("\"", pp, "\"").

comma pp
returns Decorate("", pp, ",").

colon pp
returns Decorate("", pp, ":").

semicolon pp
returns Decorate("", pp, ";").

fromString s
returns Decorate(s, empty, "").

fromStringSplitEscape s
returns
  Block(false,
        map (fn c => fromString(Char.toString c))
            (explode s))
where Char.toString escapes non-printable characters (as well as double quotes and backslashes) using SML escape sequences.

Discussion

For example, if the current printing line length is 30, running

  val pp = 
        PP.decorate
        ("(",
         PP.block
         (true,
          [PP.fromString "once", PP.fromString "upon", PP.fromString "a",
           PP.fromString "time"]),
         ")");
  val pp' =
        PP.decorate
        ("[",
         PP.block(false, [pp, pp, pp]),
         "]");
  print(PP.toString pp' ^ "\n");
results in the output
  [(once upon a
    time)
   (once upon a
    time)
   (once upon a
    time)]
And, if the current printing line length is 60, running the same code results in
  [(once upon a time)(once upon a time)(once upon a time)]

[ Top | Parent | Root | Contents | Index ]

Forlan Version 4.15
Copyright © 2022 Alley Stoughton