atd

    A MoonBit port of ATD (Adaptable Type Definitions) with a MoonBit code generator

    atd
    json
    codegen
    types
    Download zip
    Author
    Version
    0.1.0
    License
    BSD-3-Clause
    Last updated
    4 days ago
    Downloads
    7

    Dependencies

    #atd.mbt — ATD for MoonBit

    A MoonBit port of ATD (Adaptable Type Definitions), a syntax for defining cross-language data types used to generate type-safe JSON serializers and deserializers. It also adds a new target language, MoonBit, with the atdmbt code generator.

    What's included:

    PackageDescription
    bobzhang/atd (src/)The ATD library: lexer, parser, AST, annotations, semantic checks, inherit expansion, monomorphization, pretty-printing, documentation format, JSON Schema export. Port of upstream atd/src.
    bobzhang/atd/formatPort of the pretty-printing engine of OCaml's Format module.
    bobzhang/atd/easy_formatPort of the easy-format library.
    bobzhang/atd/yojsonYojson-compatible JSON pretty-printer.
    bobzhang/atd/atdcatThe atdcat tool as a library.
    bobzhang/atd/mbtgenThe MoonBit code generator.
    bobzhang/atd/runtimeRuntime library used by the generated MoonBit code.
    cmd/atdcat, cmd/atdmbtCommand-line tools (wasm and native), runnable with moonx.

    #Installation

    The command-line tools run with moonx, without installation:

    moonx bobzhang/atd/cmd/atdcat foo.atd moonx bobzhang/atd/cmd/atdmbt foo.atd

    They are built on moonbitlang/async and run on the wasm (the default for moonx) and native backends. To use the library, add the module to a project:

    moon add bobzhang/atd

    From a clone of the repository:

    moon run src/cmd/atdcat -- foo.atd moon build --target native # _build/native/debug/build/cmd/{atdcat,atdmbt}/*.exe

    #atdcat

    atdcat checks, pretty-prints and transforms ATD files, exactly like the upstream tool:

    atdcat foo.atd # check and pretty-print atdcat -x foo.atd # monomorphize (expand parametrized types) atdcat -i foo.atd # expand 'inherit' statements atdcat -jsonschema root foo.atd # translate to JSON Schema atdcat -help # all options

    #atdmbt: MoonBit code generation

    atdmbt foo.atd # creates foo.mbt atdmbt -o - foo.atd # prints to stdout

    The generated file goes into a MoonBit package whose moon.pkg imports the runtime with the alias atd_runtime (and the packages of the imported ATD modules, see below):

    import { "bobzhang/atd/runtime" @atd_runtime, }

    #Example

    type point = { x : float; ~y <mbt default="1.0"> : float } type shape = [ Dot | Circle of (point * float) ]

    generates, among other things:

    pub(all) struct Point {
    x : Double
    y : Double
    } derive(Eq, Debug)

    pub fn Point::new(x~ : Double, y? : Double = 1.0) -> Point

    pub(all) enum Shape {
    Dot
    Circle(Point, Double)
    } derive(Eq, Debug)

    pub fn write_shape(x : Shape) -> Json
    pub fn read_shape(x : Json, path : @atd_runtime.Path) -> Shape raise @atd_runtime.JsonError
    pub fn shape_of_json(x : Json) -> Shape raise @atd_runtime.JsonError
    pub fn shape_of_string(s : StringView) -> Shape raise @atd_runtime.JsonError
    pub fn string_of_shape(x : Shape, indent? : Int = 0) -> String
    pub impl ToJson for Shape

    let s = string_of_shape(Circle(Point::new(x=0), 2.5))
    // ["Circle",[{"x":0,"y":1},2.5]]
    let shape = shape_of_string(s)

    For each ATD type foo:

    • Foo is a struct for records, an enum for sum types and a type alias otherwise. Records get a Foo::new constructor with labelled arguments, optional for the optional fields and the fields with a default value.
    • write_foo and read_foo are the composable JSON writer and reader; readers report errors with the path of the offending value, e.g. incompatible JSON value where type 'int' was expected: '"x"' at $.trees[0][1].
    • foo_of_json, foo_of_string and string_of_foo are conveniences.
    • ToJson is implemented for structs and enums.

    #Type mapping

    The JSON representation is the same as with atdgen, atdts and atdpy.

    ATDMoonBitJSON
    unitUnitnull
    boolBoolboolean
    intInt (range-checked)number; <json repr="string">: string
    int <mbt repr="int64">Int64 (all digits preserved)number or string
    floatDoublenumber; <json repr="int"> and <json precision="N"> (N significant digits, like atdgen) supported
    stringStringstring
    abstractJsonany
    t listArray[T]array
    (string * t) list <json repr="object">Array[(String, T)]object
    ... <mbt repr="map">Map[K, V]object or array of pairs
    t optionT?"None" or ["Some", x]
    t nullableT?null or x
    (a * b)(A, B)array; (a) is A, () is Unit
    recordpub(all) structobject
    sum typepub(all) enum"Tag" or ["Tag", x]; {"Tag": x} with <json repr="object">
    t wrapT, or the type given by <mbt t=...>same as t
    mod.t (imported)@mod.T
    'a t (parametrized)monomorphized, e.g. (string, int) entry → StringIntEntry

    Record fields:

    • ?foo : t option is foo : T?, omitted from the JSON object when None;
    • ~foo : t is foo : T with a default value used when the field is missing: the implicit default ([], None, 0, "", false, ...) or <mbt default="expression">;
    • null is treated as a missing field unless the record has <json keep_nulls>;
    • unknown fields are ignored.

    Other supported features: <json name="..."> on fields and variants, <json open_enum> (unknown tags are read into the variant with a string payload, which is written as a plain string), inherit, <doc text="..."> (turned into /// comments), inline records and sum types (lifted into named types such as InlinePoint), recursive types (except type aliases defined in terms of themselves, e.g. type t = t list), and names that clash with MoonBit keywords or builtin names (renamed, e.g. match → match_, Some → Some_). shared is not supported.

    #<mbt ...> annotations

    AnnotationPositionMeaning
    <mbt name="alias">from m <mbt name="alias"> import ...package alias of an imported module (default: the module's local name)
    <mbt name="T">from m import t <mbt name="T">MoonBit name of an imported type, if not the default
    <mbt name="n">field, variantMoonBit name of a field or constructor
    <mbt default="expr">~fielddefault value, a MoonBit expression
    <mbt repr="map">(k * v) listrepresent as Map[K, V]
    <mbt repr="int64">intrepresent as Int64
    <mbt t="T" wrap="f" unwrap="g">wrapcustom MoonBit type T, with f : (Inner) -> T and g : (T) -> Inner
    <mbt derive="Eq, Show">type definitiontraits to derive (default: Eq, Debug; empty for none)

    #Imports

    from foo import t makes foo.t refer to @foo.T, read with @foo.read_t and written with @foo.write_t. These are the names that atdmbt gives by default to the type t of foo.atd (names of builtin types such as Json get an underscore, e.g. Json_); if the names were adjusted to avoid a conflict in foo.atd, give the type name with <mbt name="..."> on the imported type. The MoonBit package containing the code generated from foo.atd must be imported in moon.pkg with the alias foo, or the alias given by <mbt name="...">.

    #Using the library

    let m = @atd.load_string(
    "type t = { x : int list }",
    inherit_fields=true,
    inherit_variants=true,
    )
    println(@atd.to_string(Module(m)))
    println(@atd.print_jsonschema(m, src_name="t.atd", root_type="t"))
    let code = @mbtgen.generate_module(m, atd_filename="t.atd")

    #Fidelity to upstream

    The port aims at byte-for-byte compatibility with the OCaml implementation (upstream commit c714a58), including pretty-printing, error messages and their locations, generated type names and JSON Schema output:

    • OCaml's Format and easy-format are ported faithfully, and all offsets are computed on UTF-8 bytes like in OCaml.
    • The hand-written parser replaces Menhir and reproduces its locations and its error messages, including Menhir's error-recovery behavior that selects messages such as Expecting '='.
    • src/atdcat/compat_*_test.mbt contains 405 tests comparing the output of atdcat (stdout, stderr and exit code) with the reference OCaml implementation, for all the ATD files of the upstream repository with various options, and for a corpus of malformed inputs (tests/errors).
    • Differential fuzzing of the parser (thousands of mutated upstream files) found no differences.

    Known, intentional differences:

    • Inputs that make upstream crash with an uncaught exception (decimal escape sequences above \255, type names with more than two components) produce proper error messages instead.
    • Strings are Unicode: invalid UTF-8 in string literals is replaced with U+FFFD.
    • atdcat -version prints the version of this port.

    #Development

    The upstream sources are used for reference and for generating the compatibility tests; clone them into .repos (ignored by git):

    git clone https://github.com/ahrefs/atd .repos/atd

    Tests:

    moon test # also with --target native|js|wasm moonx scripts/regen_fixtures.mbtx # regenerate the code of src/tests/*

    The compatibility tests and the fuzzer need a reference atdcat built from .repos/atd with dune (it needs easy-format, menhir, re, yojson and cmdliner):

    moonx scripts/gen_compat_tests.mbtx path/to/atdcat.exe && moon fmt moon build --target native moonx scripts/fuzz_atdcat.mbtx path/to/atdcat.exe 1000

    #License

    BSD-3-Clause, like the upstream ATD project; see LICENSE.md.

    Annot

    type Annot = Array[AnnotSection]

    An annotation, consisting of a sequence of sections.

    Doc

    type Doc = Array[DocBlock]

    Documentation: a list of blocks.

    Imports

    type Imports = Map[String, Import]

    Map from local module name to import.

    PredefTable

    type PredefTable = Map[TypeName, (Int, TypeDef?)]

    A table mapping type names to their arity and definition.

    Schema

    type Schema = Array[SchemaSection]

    A schema for checking the placement of annotations.

    AtdError

    pub(all) suberror AtdError {
    AtdError(String)
    } derive(Eq,
    Debug
    )

    Error raised by the functions of the ATD library. It corresponds to both Ast.Atd_error and Failure in the OCaml implementation; the payload is the complete error message.
    impl Show for AtdError

    AtdError::message

    fn AtdError::message(self : AtdError) -> String

    Return the error message.

    AnnotField

    pub(all) struct AnnotField {
    name : String
    loc : Loc
    value : String?
    } derive(Eq,
    Debug
    )

    An annotation field, i.e. a key with an optional value within an annotation, e.g. baz="123" or bar in <foo bar baz="123">.

    AnnotSection

    pub(all) struct AnnotSection {
    name : String
    loc : Loc
    fields : Array[AnnotField]
    } derive(Eq,
    Debug
    )

    A single annotation within edgy brackets, e.g. <"foo" bar baz="123" path.to.thing="abc">.

    Any

    pub(all) enum Any {
    Module(Module)
    Import(Import)
    ImportedType(ImportedType)
    TypeDef(TypeDef)
    TypeExpr(TypeExpr)
    Variant(Variant)
    Cell(Cell)
    Field(Field)
    }

    Any kind of node, used to define a visitor root.

    Cell

    pub(all) struct Cell {
    loc : Loc
    expr : TypeExpr
    annot : Array[AnnotSection]
    } derive(Eq,
    Debug
    )

    Tuple cell, with annotations placed before the type expression, as in <ocaml default="0.0">: float.

    DocBlock

    pub(all) enum DocBlock {
    Paragraph(Array[DocInline])
    Pre(Array[String])
    } derive(Eq,
    Debug
    )

    Block of documentation.

    DocInline

    pub(all) enum DocInline {
    Text(String)
    Code(String)
    } derive(Eq,
    Debug
    )

    Inline element of a paragraph.

    Field

    pub(all) enum Field {
    Field(SimpleField)
    Inherit(Loc, TypeExpr)
    } derive(Eq,
    Debug
    )

    A single record field or an inherit statement.

    FieldKind

    pub(all) enum FieldKind {
    Required
    Optional
    WithDefault
    } derive(Eq,
    Debug
    )

    Kinds of record fields.

    FieldKind::is_required

    fn FieldKind::is_required(self : FieldKind) -> Bool

    Test whether a field kind is Required.

    Import

    pub(all) struct Import {
    loc : Loc
    path : Array[String]
    annot : Array[AnnotSection]
    alias_ : String?
    name : String
    types : Array[ImportedType]
    } derive(Eq,
    Debug
    )

    Require the existence of another ATD module. The concrete syntax is from module.path [as alias] import type1, ....

    Import::new

    fn Import::new(loc~ : Loc, path~ : Array[String], annot~ : Array[AnnotSection], alias_? : String, types~ : Array[ImportedType]) -> Import

    Create an import, computing its local name.

    ImportedType

    pub(all) struct ImportedType {
    params : Array[String]
    name : String
    annot : Array[AnnotSection]
    } derive(Eq,
    Debug
    )

    A type imported by a from ... import ... statement.

    JsonAdapter

    pub(all) struct JsonAdapter {
    ocaml_adapter : OcamlAdapter?
    java_adapter : String?
    } derive(Eq,
    Debug
    )

    JSON adapters for various target languages.

    JsonFloat

    pub(all) enum JsonFloat {
    Float(Int?)
    Int
    } derive(Eq,
    Debug
    )

    JSON representation of an ATD float.

    JsonInt

    pub(all) enum JsonInt {
    Int
    String
    } derive(Eq,
    Debug
    )

    JSON representation of an ATD int.

    JsonList

    pub(all) enum JsonList {
    Array
    Object
    } derive(Eq,
    Debug
    )

    JSON representation of lists: JSON arrays, or JSON objects for lists of pairs whose first element is a string.

    JsonRecord

    pub(all) struct JsonRecord {
    json_keep_nulls : Bool
    json_record_adapter : JsonAdapter
    } derive(Eq,
    Debug
    )

    Record options.

    JsonSum

    pub(all) struct JsonSum {
    json_sum_adapter : JsonAdapter
    json_open_enum : Bool
    json_lowercase_tags : Bool
    json_sum_repr : JsonSumRepr
    } derive(Eq,
    Debug
    )

    Sum type options.

    JsonSumRepr

    pub(all) enum JsonSumRepr {
    Array
    Object
    } derive(Eq,
    Debug
    )

    Representation of the variants of a sum type with a payload: ["Cons", payload] (Array, default) or {"Cons": payload} (Object).

    JsonschemaVersion

    pub(all) enum JsonschemaVersion {
    Draft_2019_09
    Draft_2020_12
    } derive(Eq,
    Debug
    )

    Supported versions of the JSON Schema standard.

    Loc

    pub(all) struct Loc {
    start : Pos
    end_ : Pos
    } derive(Eq,
    Debug
    )

    A region in a source file.

    Loc::compare

    fn Loc::compare(a : Loc, b : Loc) -> Int

    Compare two locations so as to sort them by file path, then start position, then end position.

    Loc::new

    fn Loc::new(start : Pos, end_ : Pos) -> Loc

    Build a location from two positions.

    Module

    pub(all) struct Module {
    head : (Loc, Array[AnnotSection])
    imports : Array[Import]
    type_defs : Array[TypeDef]
    } derive(Eq,
    Debug
    )

    Contents of an ATD file.

    Module::map_all_annot

    fn Module::map_all_annot(self : Module, f : (Array[AnnotSection]) -> Array[AnnotSection]) -> Module

    Replacement of all annotations occurring in an ATD module. Note that, as in the original implementation, annotations of type definitions themselves are left untouched.

    Module::map_type_exprs

    fn Module::map_type_exprs(self : Module, m : (TypeExpr) -> TypeExpr) -> Module

    Apply TypeExpr::map_deep to all the type definitions of a module.

    Module::remove_wrap_constructs

    fn Module::remove_wrap_constructs(self : Module) -> Module

    Remove all Wrap constructs from the module.

    Module::use_only_name_variant

    fn Module::use_only_name_variant(self : Module) -> Module

    Use the generic variant Name instead of the dedicated variants List, Option, etc. (except Wrap).

    Module::use_only_specific_variants

    fn Module::use_only_specific_variants(self : Module) -> Module

    Use the dedicated variants List, Option, etc. instead of the generic variant Name.

    NodeKind

    pub(all) enum NodeKind {
    ModuleHead
    Import
    ImportedType
    TypeDef
    TypeExpr
    Variant
    Cell
    Field
    } derive(Eq,
    Debug
    )

    Kinds of nodes that carry annotations.

    OcamlAdapter

    pub(all) struct OcamlAdapter {
    normalize : String
    restore : String
    } derive(Eq,
    Debug
    )

    OCaml-specific JSON adapter.

    Pos

    pub(all) struct Pos {
    fname : String
    lnum : Int
    bol : Int
    cnum : Int
    } derive(Eq,
    Debug
    )

    A position in a source file.

    SchemaSection

    pub(all) struct SchemaSection {
    section : String
    fields : Array[(NodeKind, String)]
    } derive(
    Debug
    )

    A section of an annotation schema: the fields allowed in the section, with the kind of node where they may occur.

    SimpleField

    pub(all) struct SimpleField {
    loc : Loc
    name : String
    kind : FieldKind
    annot : Array[AnnotSection]
    expr : TypeExpr
    } derive(Eq,
    Debug
    )

    A record field that is not an inherit statement.

    TypeDef

    pub(all) struct TypeDef {
    loc : Loc
    name : TypeName
    param : Array[String]
    annot : Array[AnnotSection]
    value : TypeExpr
    orig : TypeDef?
    } derive(Eq,
    Debug
    )

    A type definition.

    TypeExpr

    A type expression.

    TypeExpr::annot

    fn TypeExpr::annot(self : TypeExpr) -> Array[AnnotSection]

    Return the annotations associated with a type expression.

    TypeExpr::extract_type_names

    fn TypeExpr::extract_type_names(self : TypeExpr, ignorable? : Array[TypeName]) -> Array[TypeName]

    Extract all the type names occurring in a type expression under Name, without duplicates, sorted.

    TypeExpr::fold

    fn[A] TypeExpr::fold(self : TypeExpr, init : A, f : (TypeExpr, A) -> A raise AtdError) -> A raise AtdError

    Iteration and accumulation over each type expression node within a given type expression, in pre-order.

    TypeExpr::is_parametrized

    fn TypeExpr::is_parametrized(self : TypeExpr) -> Bool

    Test whether a type expression contains type variables.

    TypeExpr::loc

    fn TypeExpr::loc(self : TypeExpr) -> Loc

    Extract the source location of any type expression.

    TypeExpr::map_annot

    fn TypeExpr::map_annot(self : TypeExpr, f : (Array[AnnotSection]) -> Array[AnnotSection] raise AtdError) -> TypeExpr raise AtdError

    Replace the annotations associated with a type expression (shallow).

    TypeExpr::map_deep

    fn TypeExpr::map_deep(self : TypeExpr, m : (TypeExpr) -> TypeExpr) -> TypeExpr

    Replace type expression nodes by other nodes: first the mapper is applied to a node, then the children nodes are mapped recursively.

    TypeExpr::set_loc

    fn TypeExpr::set_loc(self : TypeExpr, loc : Loc) -> TypeExpr

    Replace the location of the given expression (shallow).

    TypeInst

    pub(all) struct TypeInst {
    loc : Loc
    name : TypeName
    args : Array[TypeExpr]
    } derive(Eq,
    Debug
    )

    A dot-separated type name and its arguments.

    TypeName

    pub(all) struct TypeName {
    path : Array[String]
    } derive(Eq, Hash,
    Debug
    )

    A possibly qualified type name.

    The simple ATD type name a is represented as TN(["a"]). The composite ATD type name a.b.c is represented as TN(["a", "b", "c"]). The list of path components may not be empty. Two components indicate a type provided by an external module.
    impl Compare for TypeName
    impl Show for TypeName

    TypeName::basename

    fn TypeName::basename(self : TypeName) -> String

    Return the base name, i.e. the last component in the path.

    TypeName::is_simple

    fn TypeName::is_simple(self : TypeName, name : String) -> Bool

    Test whether the type name is the unqualified name name.

    TypeName::new

    fn TypeName::new(path : Array[String]) -> TypeName

    Build a type name from its path components.

    TypeName::simple

    fn TypeName::simple(name : String) -> TypeName

    Build an unqualified type name.

    TypeName::split

    fn TypeName::split(self : TypeName) -> (String?, String) raise AtdError

    Return the module name if any, and the base name.

    TypeName::to_string

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

    Format to a string in ATD syntax. For example, TN(["a", "b"]) gives a.b.

    UniqueNames

    pub struct UniqueNames {
    reserved_identifiers : Map[String, Unit]
    reserved_prefixes : Array[String]
    safe_prefix : String
    translations : Map[String, String]
    reverse_translations : Map[String, String]
    }

    A registry of unique names.

    UniqueNames::all

    fn UniqueNames::all(self : UniqueNames) -> Array[(String, String)]

    All the translations, sorted by source identifier.

    UniqueNames::create

    fn UniqueNames::create(self : UniqueNames, src : String) -> String

    Create a new unique source identifier based on src and register it.

    UniqueNames::new

    fn UniqueNames::new(reserved_identifiers~ : Array[String], reserved_prefixes~ : Array[String], safe_prefix~ : String) -> UniqueNames

    Create a registry.

    UniqueNames::reverse_translate

    fn UniqueNames::reverse_translate(self : UniqueNames, dst : String) -> String?

    Look up the source identifier of a translation.

    UniqueNames::translate

    fn UniqueNames::translate(self : UniqueNames, src : String, preferred_translation? : String) -> String

    Translate an identifier, registering it if needed.

    UniqueNames::translate_only

    fn UniqueNames::translate_only(self : UniqueNames, src : String) -> String?

    Look up the translation of a registered identifier.

    Variant

    pub(all) enum Variant {
    Variant(Loc, String, Array[AnnotSection], TypeExpr?)
    Inherit(Loc, TypeExpr)
    } derive(Eq,
    Debug
    )

    A single variant or an inherit statement.

    VisitorHooks

    pub(all) struct VisitorHooks {
    module_ : ((Module) -> Unit raise AtdError, Module) -> Unit raise AtdError
    import_ : ((Import) -> Unit raise AtdError, Import) -> Unit raise AtdError
    imported_type : ((ImportedType) -> Unit raise AtdError, ImportedType) -> Unit raise AtdError
    type_def : ((TypeDef) -> Unit raise AtdError, TypeDef) -> Unit raise AtdError
    type_expr : ((TypeExpr) -> Unit raise AtdError, TypeExpr) -> Unit raise AtdError
    variant : ((Variant) -> Unit raise AtdError, Variant) -> Unit raise AtdError
    cell : ((Cell) -> Unit raise AtdError, Cell) -> Unit raise AtdError
    field : ((Field) -> Unit raise AtdError, Field) -> Unit raise AtdError
    }

    Hooks for the visitor. Each hook receives a continuation that must be called to visit the children of the node.

    annot_create_id

    fn annot_create_id() -> String

    Create a unique identifier (used for shared types).

    annot_field

    fn annot_field(l : Array[AnnotSection], section~ : String, field~ : String) -> (Loc, String?)? raise AtdError

    Return the field section.field if it exists, failing if it occurs more than once.

    annot_fields

    fn annot_fields(l : Array[AnnotSection], section~ : String, field~ : String) -> Array[(Loc, String?)]

    Return all the fields named field found in the sections named section, in order.

    annot_get_field

    fn[T] annot_get_field(l : Array[AnnotSection], parse~ : (String) -> T?, default~ : T, sections~ : Array[String], field~ : String) -> T raise AtdError

    Look up the value of a field and parse it.

    annot_get_fields

    fn[T] annot_get_fields(l : Array[AnnotSection], parse~ : (String) -> T?, sections~ : Array[String], field~ : String) -> Array[T] raise AtdError

    Look up all the values of a field in the first section.

    annot_get_flag

    fn annot_get_flag(l : Array[AnnotSection], sections~ : Array[String], field~ : String) -> Bool raise AtdError

    Look up a boolean flag such as <json keep_nulls> or <json keep_nulls="true">. The default is false.

    annot_get_loc

    fn annot_get_loc(l : Array[AnnotSection], sections~ : Array[String], field~ : String) -> Loc? raise AtdError

    Return the location of the first occurrence of a field.

    annot_get_opt_field

    fn[T] annot_get_opt_field(l : Array[AnnotSection], parse~ : (String) -> T?, sections~ : Array[String], field~ : String) -> T? raise AtdError

    Look up the optional value of a field and parse it.

    annot_get_string

    fn annot_get_string(l : Array[AnnotSection], sections~ : Array[String], field~ : String) -> String? raise AtdError

    Look up a string-valued field.

    annot_has_field

    fn annot_has_field(l : Array[AnnotSection], sections~ : Array[String], field~ : String) -> Bool raise AtdError

    Test whether the field exists in one of the given sections.

    annot_has_section

    fn annot_has_section(l : Array[AnnotSection], section : String) -> Bool

    Test whether a section named section exists.

    annot_merge

    fn annot_merge(l : Array[AnnotSection]) -> Array[AnnotSection]

    Merge sections of the same name and fields of the same name, the first occurrence taking precedence.

    annot_reset_ids

    fn annot_reset_ids() -> Unit

    Reset the counter used by annot_create_id, as if the program had just started.

    annot_set_field

    fn annot_set_field(l : Array[AnnotSection], loc~ : Loc, section~ : String, field~ : String, value : String?) -> Array[AnnotSection]

    Set the value of a field, replacing the first existing occurrence if any.

    annot_validate

    fn annot_validate(schema : Array[SchemaSection], root : Any) -> Unit raise AtdError

    Check that all the annotations of the sections described by the schema are valid and properly placed.

    check_module

    fn check_module(x : Module) -> Unit raise AtdError

    Check the existence and arity of the types used in type expressions, and that inheritance is not cyclic.

    check_type_refs

    fn check_type_refs(locals : Map[String, Import], type_defs : ArrayView[TypeDef]) -> Unit raise AtdError

    Walk all type expressions and verify that every qualified type reference a.b refers to an imported module a that lists type b with the right arity.

    compare_string_lists

    fn compare_string_lists(a : ArrayView[String], b : ArrayView[String]) -> Int

    Compare string lists like OCaml's polymorphic comparison.

    compare_strings

    fn compare_strings(a : StringView, b : StringView) -> Int

    Compare strings like OCaml's String.compare, i.e. by UTF-8 bytes, which is the same as comparing code points.

    concatenate_into_lines

    fn concatenate_into_lines(words : Array[String], max_length~ : Int) -> Array[String]

    Concatenate words into lines of at most max_length bytes, if possible.

    default_format_annot

    fn default_format_annot(s : AnnotSection) ->
    T

    The default way of formatting an annotation section.

    default_jsonschema_version

    let default_jsonschema_version : JsonschemaVersion

    The latest supported version.

    doc_annot_schema

    let doc_annot_schema : Array[SchemaSection]

    All the valid annotations of the form <doc ...>.

    dummy_loc

    let dummy_loc : Loc

    Dummy value for predefined constructs that are not associated with a useful source location.

    dummy_pos

    let dummy_pos : Pos

    The dummy position, as OCaml's Lexing.dummy_pos.

    error

    fn[T] error(msg : String) -> T raise AtdError

    error(s) raises AtdError(s).

    error_at

    fn[T] error_at(loc : Loc, msg : String) -> T raise AtdError

    error_at(loc, s) raises an error whose message is the location followed by s.

    expand_inherit

    fn expand_inherit(defs : Array[TypeDef], inherit_fields? : Bool, inherit_variants? : Bool) -> Array[TypeDef] raise AtdError

    Expand the inherit statements of all the type definitions.

    expand_type_defs

    fn expand_type_defs(td_list : Array[TypeDef], prefix? : String, keep_builtins? : Bool, keep_poly? : Bool, debug? : Bool) -> Array[TypeDef] raise AtdError

    Monomorphization of type definitions.

    • prefix: prefix to use for new type names. Default is "_".
    • keep_builtins: preserve occurrences of the built-in parametrized types such as list or option.
    • keep_poly: return definitions for the parametrized types.
    • debug: keep meaningful but non ATD-compliant names for new types.

    format

    Convert any node into an Easy_format tree.

    get_construct

    fn get_construct(tbl : Map[TypeName, (Int, TypeDef?)], name : TypeName) -> (Int, TypeExpr)?

    Return the type construct that a type name stands for, if known.

    get_construct_of_expr

    fn get_construct_of_expr(tbl : Map[TypeName, (Int, TypeDef?)], x : TypeExpr) -> TypeExpr?

    Return the type construct of a type expression, looking up parameterless type names.

    get_doc

    fn get_doc(loc : Loc, an : Array[AnnotSection]) -> Array[DocBlock]? raise AtdError

    Extract and parse the documentation from an annotation.

    get_json_adapter

    fn get_json_adapter(an : Array[AnnotSection]) -> JsonAdapter raise AtdError

    JSON adapters: <json adapter.ocaml="..."> etc.

    get_json_cons

    fn get_json_cons(default : String, an : Array[AnnotSection]) -> String raise AtdError

    JSON name of a variant: <json name="...">.

    get_json_float

    fn get_json_float(an : Array[AnnotSection]) -> JsonFloat raise AtdError

    Representation of a float: <json repr="float|int">.

    get_json_fname

    fn get_json_fname(default : String, an : Array[AnnotSection]) -> String raise AtdError

    JSON name of a field: <json name="...">.

    get_json_int

    fn get_json_int(an : Array[AnnotSection]) -> JsonInt raise AtdError

    Representation of an int: <json repr="int|string">.

    get_json_keep_nulls

    fn get_json_keep_nulls(an : Array[AnnotSection]) -> Bool raise AtdError

    <json keep_nulls>

    get_json_list

    fn get_json_list(an : Array[AnnotSection]) -> JsonList raise AtdError

    Representation of a list: <json repr="array|object">.

    get_json_lowercase_tags

    fn get_json_lowercase_tags(an : Array[AnnotSection]) -> Bool raise AtdError

    <json lowercase_tags>

    get_json_open_enum

    fn get_json_open_enum(an : Array[AnnotSection]) -> Bool raise AtdError

    <json open_enum>

    get_json_precision

    fn get_json_precision(an : Array[AnnotSection]) -> Int? raise AtdError

    <json precision="N">

    get_json_record

    fn get_json_record(an : Array[AnnotSection]) -> JsonRecord raise AtdError

    All the options of a record type.

    get_json_sum

    fn get_json_sum(an : Array[AnnotSection]) -> JsonSum raise AtdError

    All the options of a sum type.

    get_json_sum_repr

    fn get_json_sum_repr(an : Array[AnnotSection]) -> JsonSumRepr raise AtdError

    Representation of sum types: <json repr="object">.

    get_original_definition

    fn get_original_definition(tbl : Map[TypeName, (Int, TypeDef?)], name : TypeName) -> (Int, TypeDef?)?

    Follow aliases of the form type t = u down to the original definition.

    html_of_doc

    fn html_of_doc(blocks : Array[DocBlock]) -> String

    Convert documentation to HTML.

    iter_annot

    fn iter_annot(any : Any, f : (NodeKind, Array[AnnotSection]) -> Unit raise AtdError) -> Unit raise AtdError

    Iterate over all the annotations of a tree, calling f with the kind of node and its annotation. This is a simplified version of OCaml's Ast.fold_annot sufficient for collecting or validating annotations.

    json_annot_schema

    let json_annot_schema : Array[SchemaSection]

    All the valid annotations of the form <json ...>.

    jsonschema_annot_schema

    let jsonschema_annot_schema : Array[SchemaSection]

    All the annotations understood by the JSON Schema translator.

    jsonschema_of_module

    fn jsonschema_of_module(module_ : Module, src_name~ : String, root_type~ : String, version? : JsonschemaVersion, xprop? : Bool) -> Json raise AtdError

    Translate an ATD module to JSON Schema.

    • src_name: name of the source, mentioned in the description.
    • root_type: name of the type that describes the root JSON value.
    • xprop: whether to allow extra properties in JSON objects.

    load_bytes

    fn load_bytes(src : BytesView, annot_schema? : Array[SchemaSection], expand? : Bool, keep_builtins? : Bool, keep_poly? : Bool, xdebug? : Bool, inherit_fields? : Bool, inherit_variants? : Bool, pos_fname? : String, pos_lnum? : Int, on_warning? : (String) -> Unit) -> Module raise AtdError

    Read ATD data from UTF-8 bytes.

    • annot_schema: check for misplaced annotations.
    • expand: perform monomorphization (atdcat -x).
    • keep_builtins: with expand, preserve the builtin parametrized types.
    • keep_poly: with expand, keep parametrized definitions (-xk).
    • xdebug: with expand, keep non-standard type names (-xd).
    • inherit_fields: expand inherit statements in records (-if).
    • inherit_variants: expand inherit statements in sums (-iv).
    • pos_fname: file name used in error messages.
    • pos_lnum: number of the first line.
    • on_warning: what to do with warnings; they are printed on stderr by default.

    load_imports

    fn load_imports(imports : ArrayView[Import]) -> Map[String, Import] raise AtdError

    Load and validate the import declarations.

    load_string

    fn load_string(s : String, annot_schema? : Array[SchemaSection], expand? : Bool, keep_builtins? : Bool, keep_poly? : Bool, xdebug? : Bool, inherit_fields? : Bool, inherit_variants? : Bool, pos_fname? : String, pos_lnum? : Int, on_warning? : (String) -> Unit) -> Module raise AtdError

    Read ATD data from a string. See load_bytes for the options.

    make_predef_table

    fn make_predef_table(user_defs : ArrayView[TypeDef]) -> Map[TypeName, (Int, TypeDef?)] raise AtdError

    Build the table of predefined and user-defined types, checking for duplicate definitions.

    ocaml_escaped

    fn ocaml_escaped(s : StringView) -> String

    OCaml's String.escaped, applied to the UTF-8 encoding of s.

    ocaml_quote

    fn ocaml_quote(s : StringView) -> String

    OCaml's Printf.sprintf "%S".

    parse_doc_text

    fn parse_doc_text(loc : Loc, s : String) -> Array[DocBlock] raise AtdError

    Parse documentation in ATD's text format.

    parse_module

    fn parse_module(src : BytesView, pos_fname? : String, pos_lnum? : Int) -> Module raise AtdError

    Parse ATD source code (UTF-8 bytes) into a module, without any semantic check.

    predef_list

    let predef_list : Array[(TypeName, Int, TypeDef?)]

    The list of predefined types: name, arity, and definition if any.
    fn print_doc_text(blocks : Array[DocBlock]) -> String

    Print documentation in ATD's text format.
    fn print_jsonschema(module_ : Module, src_name~ : String, root_type~ : String, version? : JsonschemaVersion, xprop? : Bool) -> String raise AtdError

    Translate an ATD module to JSON Schema, pretty-printed like atdcat.

    reflect_module

    fn reflect_module(name : String, x : Module) -> String

    Print the OCaml code of the AST of a module, like atdcat -ml name.

    resolve_import

    fn resolve_import(locals : Map[String, Import], loc : Loc, x : TypeName) -> ((Import, ImportedType?)?, String) raise AtdError

    Resolve a qualified or unqualified type name. Returns (Some((import, imported_type)), base_name) for qualified names, or (None, base_name) for unqualified names.

    rewrap_paragraph

    fn rewrap_paragraph(str : String, max_length~ : Int) -> Array[String]

    Rewrap a paragraph into lines of at most max_length bytes.

    split_on_blank

    fn split_on_blank(str : String) -> Array[String]

    Split a string on sequences of blanks, ignoring leading and trailing blanks.

    string_of_loc

    fn string_of_loc(loc : Loc) -> String

    Convert a location into a human-readable string such as File "foo.atd", line 123, characters 40-45.

    string_of_type_inst

    fn string_of_type_inst(name : TypeName, args : Array[TypeExpr], an : Array[AnnotSection]) -> String

    Pretty-print a type name applied to arguments, with annotations.

    to_string

    fn to_string(x : Any, format_annot? : (AnnotSection) ->
    T
    ) -> String

    Pretty-print any node in ATD syntax.

    topological_sort

    fn[T, Id : Compare + Hash + Eq] topological_sort(l : ArrayView[(T, Array[Id])], id : (T) -> Id) -> Array[(Bool, Array[T])]

    Sort the nodes topologically. Each input element comes with the list of identifiers of the nodes it points to. The result is a list of groups; a group flagged true is a cycle. Groups are sorted such that edges only go from a group to itself or to later groups.

    tsort

    fn tsort(type_defs : Array[TypeDef], all_rec? : Bool) -> Array[(Bool, Array[TypeDef])]

    Topological sort for dependency analysis: split definitions into mutually-recursive groups, ordered such that each group may only depend on type definitions of its own group or previous groups. The boolean flags indicate groups of one or more mutually recursive definitions.

    all_rec assumes all definitions are mutually dependent.

    unused_import_warnings

    fn unused_import_warnings(locals : Map[String, Import], type_defs : ArrayView[TypeDef]) -> Array[String] raise AtdError

    Collect the warnings about imported type names that are never referenced in any type expression, sorted by source position.

    version

    let version : String

    Version of the ATD library.

    visit

    fn visit(module_? : ((Module) -> Unit raise AtdError, Module) -> Unit raise AtdError, import_? : ((Import) -> Unit raise AtdError, Import) -> Unit raise AtdError, imported_type? : ((ImportedType) -> Unit raise AtdError, ImportedType) -> Unit raise AtdError, type_def? : ((TypeDef) -> Unit raise AtdError, TypeDef) -> Unit raise AtdError, type_expr? : ((TypeExpr) -> Unit raise AtdError, TypeExpr) -> Unit raise AtdError, variant? : ((Variant) -> Unit raise AtdError, Variant) -> Unit raise AtdError, cell? : ((Cell) -> Unit raise AtdError, Cell) -> Unit raise AtdError, field? : ((Field) -> Unit raise AtdError, Field) -> Unit raise AtdError) -> ((Any) -> Unit raise AtdError)

    Create a function that visits all the nodes of a tree. Each optional hook defines what to do when encountering a node of a particular kind; it is applied as hook(cont, x) and must call cont for the visitor to continue down the tree.