prettyprinter

    pretty printer for moonbit

    pretty printer
    format
    utils
    string
    Download zip
    Version
    0.4.10
    License
    Apache-2.0
    Last updated
    4 months ago
    Downloads
    51K

    #pretty printer

    A declarative-style pretty printer engine, which includes printers for built-in types such as Array, Map, and Json.

    #Usage

    Use render to pretty print any type implemented Pretty trait.

    ///|
    test {
    let record : Map[String, Array[String]] = {
    "name": ["John", "Mike"],
    "age": ["15", "18"],
    "id": ["11109121", "2000012312"],
    }
    inspect(
    record |> @prettyprinter.render(),
    content=(
    #|{
    #| "name": ["John", "Mike"],
    #| "age": ["15", "18"],
    #| "id": ["11109121", "2000012312"]
    #|}
    ),
    )
    }

    #Implement Pretty Trait

    Write declarative code to implement a printer for your type.

    ///|
    enum Tree[A] {
    Leaf(A)
    Node(Array[Tree[A]])
    }

    ///|
    impl[A : @prettyprinter.Pretty] @prettyprinter.Pretty for Tree[A] with pretty(
    tree : Tree[A],
    ) {
    match tree {
    Leaf(x) => x.pretty()
    Node(xs) =>
    @prettyprinter.group(
    @prettyprinter.text("Node(") +
    @prettyprinter.nest(
    @prettyprinter.line +
    @prettyprinter.separate(
    @prettyprinter.char(',') + @prettyprinter.line,
    xs.map(@prettyprinter.Pretty::pretty),
    ),
    ) +
    @prettyprinter.line +
    @prettyprinter.char(')'),
    )
    }
    }

    ///|
    test {
    let tree = Node([
    Leaf(100),
    Leaf(200),
    Leaf(300000000),
    Node([
    Leaf(400),
    Leaf(500),
    Node([Leaf(6000000), Leaf(7000000), Leaf(80000000), Leaf(80000001)]),
    Leaf(900),
    ]),
    Leaf(1000),
    ])
    inspect(
    @prettyprinter.render(tree, width=50),
    content=(
    #|Node(
    #| 100,
    #| 200,
    #| 300000000,
    #| Node(
    #| 400,
    #| 500,
    #| Node( 6000000, 7000000, 80000000, 80000001 ),
    #| 900
    #| ),
    #| 1000
    #|)
    ),
    )
    }

    Pretty

    pub(open) trait Pretty {
    pretty(Self) -> Document
    }

    impl Pretty for Unit
    impl Pretty for Bool
    impl Pretty for Byte
    impl Pretty for Char
    impl Pretty for Int
    impl Pretty for Int16
    impl Pretty for Int64
    impl Pretty for UInt
    impl Pretty for UInt16
    impl Pretty for UInt64
    impl Pretty for Float
    impl Pretty for Double
    impl Pretty for String
    impl Pretty for Option[A]
    impl Pretty for Result[A, B]
    impl Pretty for FixedArray[A]
    impl Pretty for Bytes
    impl Pretty for BigInt
    impl Pretty for Array[A]
    impl Pretty for Iter[A]
    impl Pretty for Iter2[A, B]
    impl Pretty for Json
    impl Pretty for Map[A, B]
    impl Pretty for Deque[A]
    impl Pretty for HashMap[A, B]
    impl Pretty for HashSet[A]
    impl Pretty for List[A]
    impl Pretty for Queue[A]
    impl Pretty for Set[A]
    impl Pretty for Tuple2[A, B]
    impl Pretty for Tuple3[A, B, C]
    impl Pretty for Tuple4[A, B, C, D]
    impl Pretty for Tuple5[A, B, C, D, E]
    impl Pretty for Tuple6[A, B, C, D, E, F]
    impl Pretty for Tuple7[A, B, C, D, E, F, G]
    impl Pretty for Tuple8[A, B, C, D, E, F, G, H]
    impl Pretty for Tuple9[A, B, C, D, E, F, G, H, I]

    Context

    pub struct Context {
    indent : Int
    next_indent : Int
    width : Int
    }

    impl Show for Context

    Context::current_remain

    fn Context::current_remain(self : Context) -> Requirement

    Context::next_remain

    fn Context::next_remain(self : Context) -> Requirement

    Document

    type Document

    impl Pretty for Document
    impl Add for Document
    impl Show for Document

    Document::requirement

    fn Document::requirement(doc : Document) -> Requirement

    Requirement

    pub(all) enum Requirement {
    Space(Int)
    Infinite
    }

    impl Add for Requirement
    impl Eq for Requirement
    impl Show for Requirement

    braces

    fn braces(doc : Document) -> Document

    wraps the given document in braces.

    inspect(braces(text("doc")), content="{doc}")

    brackets

    fn brackets(doc : Document) -> Document

    wraps the given document in brackets.

    inspect(brackets(text("doc")), content="[doc]")

    char

    fn char(c : Char) -> Document

    concat

    fn concat(l : Document, r : Document) -> Document

    Concatenate two documents. You can also use the + operator.

    ctor

    fn ctor(pkg? : String, ty? : String, ctor : String, payloads : Array[Document], labeled? : Map[String, Document]) -> Document

    Represent an enum value.

    Example

    let doc = ctor(pkg="pkg", ty="Enum", "Constr", [pretty(1), pretty(2)], labeled={ "loc": pretty(100), "name": pretty("cons"), }) inspect( doc, content=( #|@pkg.Enum::Constr(1, 2, loc=100, name="cons") ), ) let doc = ctor( pkg="pkg", ty="Enum", "LongConstructor", [ pretty(100000000), pretty(200000000), pretty(100000000), pretty(200000000), pretty(100000000), pretty(200000000), pretty(100000000), pretty(200000000), ], labeled={ "loc": pretty(100), "name": pretty("cons") }, ) inspect( doc, content=( #|@pkg.Enum::LongConstructor( #| 100000000, #| 200000000, #| 100000000, #| 200000000, #| 100000000, #| 200000000, #| 100000000, #| 200000000, #| loc=100, #| name="cons", #|) ), )

    dynamic

    fn dynamic(f : (Context) -> Document) -> Document

    empty

    let empty : Document

    Empty document. render(empty) == ""

    flow

    fn flow(doc : Array[Document]) -> Document

    print each documents in the same line if it fits, insert a line break otherwise.

    let words = [ "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog", ] let doc = words.map(fn(x){text(x) + space}) |> flow() inspect(doc, content="The quick brown fox jumps over the lazy dog ") inspect( render(doc, width=10), content=( #|The quick brown #|fox jumps over #|the lazy dog ), )

    group

    fn group(doc : Document) -> Document

    group(doc) tries to render the doc on a single line, but if the doc doesn't fit, or contains a hardline, all the switch inside the doc will be rendered with the right document.

    hardline

    let hardline : Document

    Force line break with current indentation. This document will always doesn't fit in the current line.

    Example

    render(text("hello") + nest(hardline + text("world"))) |> inspect( content=( #|hello #| world ), )

    line

    let line : Document

    Represents a line break if the line is too long, or a space if it fits.

    list

    fn list(ls : Array[Document]) -> Document

    Concatenate documents.

    mandatory_line

    let mandatory_line : Document

    Ensures at least one line at this position.
    inspect( mandatory_line + nest(line + text("abc")), content=( #| #| abc ), ) inspect( mandatory_line + nest(text("abc")), content=( #| #|abc ), )

    mandatory_space

    let mandatory_space : Document

    Inserts at least one space between concatenated text segments.
    inspect( text("f();") + mandatory_space + text("//todo"), content="f(); //todo", ) inspect( text("f();") + space + mandatory_space + text("//todo"), content="f(); //todo", ) inspect( text("f();") + hardline + mandatory_space + text("//todo"), content=( #|f(); #|//todo ), )

    nest

    fn nest(indent? : Int, doc : Document) -> Document

    Nest the document by n spaces. Any line breaks inside the nested document will be indented by n spaces.

    parens

    fn parens(doc : Document) -> Document

    wraps the given document in parentheses.

    inspect(parens(text("doc")), content="(doc)")

    pretty

    fn[A : Pretty] pretty(x : A) -> Document

    record

    fn record(pkg? : String, ty? : String, fields : Map[String, Document]) -> Document

    Represent a struct.

    Example

    let doc = record(pkg="pkg", ty="Struct", { "spos": pretty(1), "epos": pretty(2), "file": pretty("abc"), }) inspect( doc, content=( #|@pkg.Struct::{spos: 1, epos: 2, file: "abc"} ), ) let doc = record(pkg="pkg", ty="Struct", { "spos": pretty(1000000000), "epos": pretty(2000000000), "file": pretty("abc"), "spos1": pretty(1000000000), "epos1": pretty(2000000000), "file1": pretty("abc"), }) inspect( doc, content=( #|@pkg.Struct::{ #| spos: 1000000000, #| epos: 2000000000, #| file: "abc", #| spos1: 1000000000, #| epos1: 2000000000, #| file1: "abc", #|} ), )

    render

    fn[A : Pretty] render(document : A, width? : Int) -> String

    Renders a document as a pretty string with a specified width.

    Arguments

    • x - The document to render.
    • width - The maximum width of the rendered string. Defaults to 80.

    Returns

    The rendered string.

    separate

    fn separate(sep : Document, docs : Array[Document]) -> Document

    This function takes a separator document sep and an array of documents docs and returns a document where each document in the array is separated by the separator document.

    Example

    let sep = text("-") let docs = [text("Hello"), text("World"), text("!")] separate(sep, docs).pretty() |> inspect(content="Hello-World-!")

    separate_map

    fn[A] separate_map(sep : Document, elems : Array[A], f : (A) -> Document) -> Document

    This function takes a separator document, an array of values, and a mapping function, and returns a document where each value in the array is mapped to a document using the mapping function, and then separated by the separator document.

    Arguments

    • sep - The separator document to be used between each mapped value.
    • xs - The array of values to be mapped and separated.
    • f - The mapping function that takes a value from the array and returns a document.

    Returns

    The resulting document where each value is mapped and separated by the separator document.

    Example

    let doc = separate_map(text(","), [1,2,3], pretty) inspect(doc, content="1,2,3")

    softline

    let softline : Document

    Represents a line break if the line is too long, or nothing if it fits.

    space

    let space : Document

    Space character. render(space) == " "

    string

    fn string(str : String) -> Document

    print the string with double quotes.

    inspect(string("Hello, World!"), content="\"Hello, World!\"")

    surround

    fn surround(left : Document, right : Document, doc : Document) -> Document

    Surrounds the given document with the left and right documents.

    surround(l, r, doc) is equivalent to l + doc + r

    Example

    inspect(surround(char('['), char(']'), text("doc")), content="[doc]")

    switch

    fn switch(l : Document, r : Document) -> Document

    Switch between two documents based on the available space. The left document represents the flatten mode, and the right one the pretty mode. If the left document fits, it will be rendered, otherwise the right one.

    text

    fn text(s : String) -> Document

    tuple

    fn tuple(elems : Array[Document]) -> Document

    Represent a tuple.

    Example

    let doc = tuple([pretty(1), pretty('a'), pretty(true)]) inspect(doc, content="(1, a, true)") let elem = pretty("very long string") let doc = tuple(Array::make(5, elem)) inspect( doc, content=( #|( #| "very long string", #| "very long string", #| "very long string", #| "very long string", #| "very long string", #|) ), )