Tptp.Printer.Pretty (Tptp v0.1.0)

Copy Markdown View Source

Prints a CST to a chosen width, breaking lines where a formula is too long to fit.

iex> {:ok, statement, []} = Tptp.Parser.statement_from_string("fof(a,axiom,p&q).")
iex> Tptp.Printer.Pretty.to_string(statement)
"fof(a, axiom, p & q)."

iex> {:ok, statement, []} = Tptp.Parser.statement_from_string("fof(a,axiom,p&q).")
iex> Tptp.Printer.Pretty.to_string(statement, width: 12)
"fof(\n  a,\n  axiom,\n  p\n  & q\n)."

At a width of twelve columns:

fof(
  a,
  axiom,
  p
  & q
).

Contract

The token sequence is that of Tptp.Printer.Canonical: the same tokens, in the same order, with the same spellings, differing only in the white space between them. This is not preserved by construction but inherited: this module obtains the token list from the canonical printer and determines only where line breaks occur, so a change to the grammar cannot cause the two printers to diverge.

Line breaking uses Inspect.Algebra, the Wadler–Lindig algebra in the standard library, which introduces no dependency and produces an optimal rather than greedy layout: a group is flattened when its entire contents fit, so p(a, b, c) does not break its first argument before establishing that the third would not have fitted.

Structure

The token stream of a statement has balanced (, [ and {. [.], <.>, {.} and (.) are single tokens after lexing, as are quoted atoms and distinct objects, and the grammar guarantees the remainder. Bracket matching over the flat token list therefore recovers the nesting without a second traversal of the tree.

Each bracket region becomes one group with a two-space indent, and whether it breaks is determined independently, so an argument that does not fit does not separate its siblings. The internal item type represents this recovery: either a token, or an opener with its contents and its closer. The closer is nil only for unbalanced brackets, which the grammar makes unreachable and which are handled rather than raising.

Permitted break positions

  • After a comma, giving one element per line where a list breaks.

  • Before a binary connective, never after, so that a broken chain reads

    p(X)
    & q(X)
    & r(X)

    with the connective at the start of the line, which is the convention in TPTP output and which allows a long conjunction to be scanned for the operator that differs. @ is included, so a long THF application spine breaks in the same way, as is >, for a long type signature.

  • Immediately inside a bracket, which permits a region to open.

No other position. In particular no break occurs inside a quoted atom or a distinct object, each being a single token, and this module does not examine the interior of a token.

Safety of white space

Every position at which a break may occur may also carry a space, and every position at which it may not still receives the spacing prescribed by Tptp.Printer.Spacing. White space between two TPTP tokens can neither merge nor divide them: the longest-match cases that could, such as ~ adjacent to |, are those Spacing already separates.

Summary

Types

How wide a line may be.

Anything this printer knows how to write, which is anything the canonical printer does.

Functions

Write the pretty form to disk.

Print to a width, as iodata.

Print to a width, as a binary.

Types

option()

@type option() :: {:width, pos_integer()}

How wide a line may be.

:width is the column the algebra tries to keep within; it is a target and not a guarantee, because a single token longer than the width cannot be broken.

printable()

@type printable() :: Tptp.Printer.Canonical.printable()

Anything this printer knows how to write, which is anything the canonical printer does.

Functions

to_file(printable, path, options \\ [])

@spec to_file(printable(), Path.t(), [option()]) :: :ok | {:error, File.posix()}

Write the pretty form to disk.

to_iodata(printable, options \\ [])

@spec to_iodata(printable(), [option()]) :: iodata()

Print to a width, as iodata.

iex> {:ok, statement, []} = Tptp.Parser.statement_from_string("cnf(c,axiom,p|~q).")
iex> statement |> Tptp.Printer.Pretty.to_iodata() |> IO.iodata_to_binary()
"cnf(c, axiom, p | ~q)."

to_string(printable, options \\ [])

@spec to_string(printable(), [option()]) :: binary()

Print to a width, as a binary.

iex> {:ok, file, []} = Tptp.from_string("fof(a,axiom,p(X)&q).")
iex> Tptp.Printer.Pretty.to_string(file)
"fof(a, axiom, p(X) & q).\n"