Sign in

    moontoml

    moontoml — TOML 1.0.0 for MoonBit. Reads and writes the format MoonBit's own moon.mod and moon.pkg are written in, into the same `Json` tree every other reader in this family produces: tables and arrays of tables, dotted keys, the four string forms, the four date-time forms, and integers in four bases.

    toml
    config
    parser
    moonbit
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    16

    #moontoml

    TOML 1.0.0, read and written as the Json tree every other reader in this family produces. It is the format MoonBit's own moon.mod and moon.pkg are written in.

    let config = @moontoml.loads(
    #|name = "moonbitstack/moontoml"
    #|version = "0.1.0"
    #|keywords = [ "toml", "config" ]
    #|
    #|[import]
    #|"moonbitstack/moondate" = "0.1.0"
    )

    @moontoml.dumps(config) // back to TOML, tables under their own headers

    #What it reads

    Everything TOML 1.0.0 defines: bare, quoted and dotted keys; [table] and [[array of tables]] headers; the four string forms with their escapes and the line-ending backslash; integers in decimal, hexadecimal, octal and binary with digit-grouping underscores; floats with exponents and inf/nan; the four date-time types; arrays that span lines; and inline tables.

    #The two places the tree is narrower than the format

    Date-times become their normalised text. TOML has four date-time types and JSON has none. They are read through moondate, so 1979-02-30 is refused rather than kept as a string, and what lands in the tree is the RFC 3339 form — 1979-05-27T07:32:00Z. The writer writes them back bare rather than quoted, so a document round-trips as a date and not as a string. A caller that must tell a date from a string spelling one needs the values, not the tree; that is what a richer model would be for.

    Integers are a Double, but their text survives. A Json number is a double, which is exact only to 2^53. The digits a number was written as are carried alongside it when JSON spells it the same way, so 9007199254740993 writes back as itself even though arithmetic on it would not be exact. A spelling JSON does not share — +99, 0xff, 1_000, inf — carries no text and is written from the value.

    #What it refuses

    TOML's redefinition rules, which are what make a file safe to append to. A key defined twice, a header repeated, a dotted key later opened by a header, a key added to an inline table after its brace closed, [[…]] over something that is not an array of tables. Each raises Refused with a line and column.

    Also a leading zero on a multi-digit integer, an underscore not between two digits, a float missing a digit on either side of its dot, and a trailing comma in an inline table — all of which TOML 1.0.0 forbids and some parsers tolerate.

    #Configuration

    @moontoml.loads(src, flavor=@moontoml.Flavor::new(depth=64))
    @moontoml.load(bytes, bom=false)

    depth is 500, the same as this family's JSON and YAML. bom skips a leading byte-order mark; TOML says a file has none, so bom=false holds it to that.

    There is no knob for a repeated key. §1 makes redefinition an error rather than a policy, so offering to ignore it would be offering to read something that is not a TOML document.

    #What is not here

    TOML 0.4, 0.5 and the 1.1 preview, which differ in real ways — 0.4 has no dotted keys, 0.5 added the time types and hexadecimal, 1.0.0 tightened the table rules. They are planned as packages beside this one, so choosing a version is choosing a package.

    Comment preservation. A document read and written again keeps its values, not its layout: TOML can spell the same tree several ways and this writer picks one.

    #Install

    moon add moonbitstack/moontoml

    #Licence

    Apache-2.0. See LICENSE.

    Refused

    pub(all) suberror Refused {
    Unexpected(At, Char)
    Truncated(At)
    BadEscape(At)
    BadNumber(At)
    BadDate(At)
    Redefined(At, String)
    Trailing(At)
    TooDeep(At)
    BadUtf8(At)
    } derive(Eq,
    Debug
    )

    Why a document was refused.

    Redefined is the one TOML has that JSON does not: §1 says a key may be defined only once, and so may a table, so a second definition is an error rather than a value that wins. It is the rule that makes a TOML file safe to append to.
    impl Show for Refused

    Refused::at

    fn Refused::at(self : Refused) -> At

    Where the refusal happened.

    Every variant carries a position, so a caller reporting a line does not have to match all nine of them to get at it.

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::output

    fn Refused::output(self : Refused, logger : &Logger) -> Unit

    Refused::to_repr

    Refused::to_string

    fn Refused::to_string(self : Refused) -> String

    pub(all) struct At {
    at : Int
    line : Int
    column : Int
    } derive(Eq,
    Debug
    )

    Where in the source something is: the offset in code units, and the line and column a person counts from one.
    impl Show for At

    At::equal

    fn At::equal(At, At) -> Bool

    At::not_equal

    fn At::not_equal(x : At, y : At) -> Bool

    At::output

    fn At::output(self : At, logger : &Logger) -> Unit

    At::to_repr

    At::to_string

    fn At::to_string(self : At) -> String

    Flavor

    pub(all) struct Flavor {
    depth : Int
    } derive(Eq,
    Debug
    )

    How a document is read.

    The preset is [strict]. Build another with [Flavor::new], or update one in place with { ..strict, depth: 64 }.

    There is no knob for a repeated key. TOML 1.0.0 §1 makes redefinition an error, not a policy, so making it configurable would be offering to read a document the format says is not one.

    Flavor::equal

    fn Flavor::equal(Flavor, Flavor) -> Bool

    Flavor::new

    fn Flavor::new(depth? : Int) -> Flavor

    A flavor by name, every knob with the preset's value.

    Flavor::not_equal

    fn Flavor::not_equal(x : Flavor, y : Flavor) -> Bool

    Flavor::to_repr

    depth

    let depth : Int

    How deep a document may nest.

    500 is what this family uses for JSON and YAML, between Jackson's 1000 and serde_json's 128.

    dump

    fn dump(value : Json, sort? : Bool) -> Bytes

    The same, as UTF-8 bytes.

    dumps

    fn dumps(value : Json, sort? : Bool) -> String

    Write a Json tree as a TOML document.

    Tables go under their own headers and scalars stay at the level they are on, so the output reads like a configuration file someone wrote rather than one a program emitted. An array of tables is written as [[…]] when every one of its elements is a table, and inline otherwise, which is the only honest choice: TOML has no way to spell an array mixing tables with scalars across headers.

    sort orders keys by name; off, they keep the order the tree has. A document read by this module and written again carries the same values, but not necessarily the same layout — TOML can spell the same tree several ways.

    load

    fn load(src : BytesView, flavor? : Flavor, bom? : Bool) -> Json raise Refused

    The document in src, decoded as UTF-8.

    TOML 1.0.0 §1 says a file is UTF-8 and does not allow a byte-order mark; bom skips one anyway, because an editor on Windows writes it and refusing the file helps nobody. Pass bom=false to hold the format to its word.

    loads

    fn loads(src : StringView, flavor? : Flavor) -> Json raise Refused

    The document in src.

    TOML defines one document rather than a stream, so there is no plural form of this — unlike moonyaml, where --- makes both meaningful.

    strict

    let strict : Flavor

    The preset.