#moonast
The catalogue

API reference

Every public type, function, method, trait and error, one plate per package and alphabetical within each group. Import one package at a time: moonbitstack/moonast/quote, /attr, /gen, /cli.

PLATE I

quotemoonbitstack/moonast/quote

Templates: real MoonBit with $holes. A template is parsed with its holes standing as placeholders, each fill is put where its placeholder stands, and the result is printed by the official formatter.

Values

pub fn bool(Bool) -> Code #

A boolean literal.

inspect(@quote.bool(true).to_string(), content="true")
pub fn bytes(StringView) -> Code #

A byte string literal, b"...", holding text in UTF-8. Every byte outside printable ASCII is written \xNN, so the literal says the same thing in a source file of any encoding; string is the text form of the same value.

inspect(@quote.bytes("max-retries").to_string(), content="b\"max-retries\"")
inspect(
@quote.bytes("坐标").to_string(),
content="b\"\\xe5\\x9d\\x90\\xe6\\xa0\\x87\"",
)
pub fn cases(StringView) -> Template #

A template of match cases, for the hole a match has where its case list goes. Each case is pattern => body, on its own line, exactly as it is written between the braces of a match.

let cases = @quote.cases("$pattern => $value")
.set("pattern", @quote.ident("Empty"))
.set("value", @quote.int(0))
.render()
inspect(cases.to_string(), content="Empty => 0")
pub fn expr(StringView) -> Template #

A template of one expression.

let code = @quote.expr("$a + $b")
.set("a", @quote.int(1))
.set("b", @quote.int(2))
.render()
inspect(code.to_string(), content="1 + 2")
pub fn fields(StringView) -> Template #

A template of record fields, for the hole a record has where its field list goes. Each field is label: value; the same code fills the arguments of a constructor, where a labelled argument is written the same way.

let fields = @quote.fields("$label: $value")
.set("label", @quote.ident("x"))
.set("value", @quote.int(1))
.render()
inspect(fields.to_string(), content="x: 1")
pub fn ident(StringView) -> Code raise Refused #

A name: a value, a function, a field, a type or a constructor, optionally qualified as @pkg.name. Refused unless it is a well-formed identifier.

inspect(@quote.ident("answer").to_string(), content="answer")
inspect(@quote.ident("@web.App").to_string(), content="@web.App")
pub fn int(Int32) -> Code #

An integer literal.

inspect(@quote.int(42).to_string(), content="42")
pub fn items(StringView) -> Template #

A template of top-level declarations.

let code = @quote.items("fn $name() -> $T {\n  $value\n}")
.set("name", @quote.ident("answer"))
.set("T", @quote.ident("Int"))
.set("value", @quote.int(42))
.render()
inspect(code.to_string(), content="///|\nfn answer() -> Int {\n  42\n}\n")
pub fn join(Array[Code]) -> Code raise Refused #

Several codes of top-level declarations as one, in order.

let a = @quote.items("fn a() -> Unit { () }").render()
let b = @quote.items("fn b() -> Unit { () }").render()
inspect(
@quote.join([a, b]).to_string(),
content="///|\nfn a() -> Unit {\n  ()\n}\n\n///|\nfn b() -> Unit {\n  ()\n}\n",
)
pub fn string(StringView) -> Code #

A string literal holding text; quoting and escaping happen when it is printed.

inspect(@quote.string("坐标").to_string(), content="\"坐标\"")
pub fn ty(StringView) -> Template #

A template of one type.

let code = @quote.ty("Array[$T]").set("T", @quote.ident("User")).render()
inspect(code.to_string(), content="Array[User]")

Types

pub struct Code #

A piece of MoonBit: top-level declarations, an expression, a type, a name, or the text of a string literal. Codes come from rendering a template or from the literal constructors here, and fill the holes of other templates.

pub fn Code::items(Self) -> @list.List[@syntax.Impl]? #

The official syntax tree behind a code of declarations, for work this package does not cover.

let code = @quote.items("fn a() -> Unit { () }").render()
inspect(code.items() is Some(_), content="true")
pub fn Code::output(Self, &Logger) -> Unit #

Writes the value to a logger, as <code>Show</code> defines it.

pub fn Code::to_string(Self) -> String #

The value as a string, as <code>Show</code> defines it.

pub struct Template #

A piece of MoonBit with holes, waiting to be filled and rendered.

A hole is $name; write it in the case the position wants, $T where a type or constructor goes and $name where a value or a name goes. $name* takes a list and expands among statements, array elements, positional arguments, a match's cases and a record's fields. $$ is a literal dollar sign.

pub fn Template::render(Self) -> Code raise Refused #

Parse the template with its holes standing as placeholders, put each fill where its placeholder stands, and give back the result. Refused when a hole is left empty, a fill names no hole, a fill does not fit where it stands, or the template is not MoonBit.

let text = @quote.items("fn $name() -> Unit { () }").render().to_string() catch {
e => e.to_string()
}
inspect(text, content="hole $name was given nothing")
pub fn Template::set(Self, String, Code) -> Self #

Fill $hole with code.

let t = @quote.items("fn $name() -> Unit { () }").set(
"name",
@quote.ident("answer"),
)
inspect(
t.render().to_string(),
content="///|\nfn answer() -> Unit {\n  ()\n}\n",
)
pub fn Template::splice(Self, String, Array[Code]) -> Self #

Fill $hole* with codes, in order.

let code = @quote.expr("[$items*]")
.splice("items", [@quote.int(1), @quote.int(2), @quote.int(3)])
.render()
inspect(code.to_string(), content="[1, 2, 3]")

Errors

pub(all) suberror Refused { #

Why a template or a code was not accepted. Each variant is named after the thing that was wrong.

  • Template(msg~ : String, line~ : Int32, column~ : Int32) — The template is not MoonBit once its holes are in place.
  • Unfilled(hole~ : String) — A hole in the template was given nothing.
  • Unused(hole~ : String) — Something was given for a hole the template does not have.
  • Misfit(hole~ : String, expected~ : String) — A hole was given a code of the wrong kind for where it stands.
  • Name(name~ : String) — ident was given something that is not an identifier.
  • Kind(expected~ : String) — join was given a code that is not declarations.
pub fn Refused::output(Self, &Logger) -> Unit #

Writes the value to a logger, as <code>Show</code> defines it.

pub fn Refused::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub fn Refused::to_string(Self) -> String #

The value as a string, as <code>Show</code> defines it.

PLATE II

attrmoonbitstack/moonast/attr

Annotations as typed arguments. Find #lib.name(...) on a declaration and bind its payload against a spec; every refusal carries the position of the annotation.

Values

pub fn all(@list.List[@attribute.Attribute], StringView) -> Array[@attribute.Attribute] #

Every annotation called name, in source order.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
inspect(@attr.all(attrs, "app.get").length(), content="1")
pub fn find(@list.List[@attribute.Attribute], StringView) -> @attribute.Attribute? #

The first annotation called name.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
inspect(@attr.find(attrs, "app.get") is Some(_), content="true")
pub fn malformed(@list.List[@attribute.Attribute], StringView) -> Array[@attribute.Attribute] #

The annotations starting #lib. whose payload the parser could not read, in source order. A key that is a keyword does this: #app.get(type="x"). under, find and all go by the parsed name and pass over them, so a generator asks here first and reports them, rather than ignoring what was written.

let source = "#app.get(type=\"x\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
inspect(@attr.under(attrs, "app").length(), content="0")
inspect(@attr.malformed(attrs, "app").length(), content="1")
pub fn name_of(@attribute.Attribute) -> String? #

The full name of an annotation, lib.name for #lib.name(...); None when the parser could not read its payload.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
guard @attr.find(attrs, "app.get") is Some(a) else { abort("annotated") }
inspect(@attr.name_of(a).unwrap_or(""), content="app.get")
pub fn under(@list.List[@attribute.Attribute], StringView) -> Array[@attribute.Attribute] #

Every annotation whose name starts with lib., in source order: a generator for #app.get, #app.post and the rest asks once for app.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
inspect(@attr.under(attrs, "app").length(), content="1")

Types

pub struct Args { #

An annotation's arguments after Spec::bind: every required one is here and every one has the kind its spec gives.

  • name : String
  • line : Int32
  • column : Int32
  • file : String
pub fn Args::bool(Self, StringView) -> Bool? #

The boolean given for key.

pub fn Args::get(Self, StringView) -> Array[Value] #

The values given for key, in order; empty when it was not given.

pub fn Args::has(Self, StringView) -> Bool #

Whether key was given.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
guard @attr.find(attrs, "app.get") is Some(a) else { abort("annotated") }
let args = @attr.Spec::new("app.get")
.positional("path", Str)
.many("tag", Str)
.bind(a)
inspect(args.has("tag"), content="true")
inspect(args.has("page"), content="false")
pub fn Args::str(Self, StringView) -> String? #

The string given for key.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
guard @attr.find(attrs, "app.get") is Some(a) else { abort("annotated") }
let args = @attr.Spec::new("app.get")
.positional("path", Str)
.many("tag", Str)
.bind(a)
inspect(args.str("path").unwrap_or(""), content="/users")
pub fn Args::strs(Self, StringView) -> Array[String] #

Every string given for a repeated key.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
guard @attr.find(attrs, "app.get") is Some(a) else { abort("annotated") }
let args = @attr.Spec::new("app.get")
.positional("path", Str)
.many("tag", Str)
.bind(a)
inspect(args.strs("tag").join(", "), content="users")
pub(all) enum Kind { #

What a parameter accepts.

  • Str
  • Bool
  • Ident
  • Path
  • Type
pub fn Kind::equal(Self, Self) -> Bool #

Whether two values are equal, as <code>Eq</code> defines it.

pub fn Kind::not_equal(Self, Self) -> Bool #

Whether two values differ, as <code>Eq</code> defines it.

pub fn Kind::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub struct Spec #

The parameters one annotation takes. Built by chaining:

Spec::new("app.get").positional("path", Str).optional("summary", Str).many("tags", Str)
pub fn Spec::bind(Self, @attribute.Attribute, file? : String) -> Args raise Refused #

The annotation's arguments, checked against the spec.

let source = "#app.get(\"/users\", tag=\"users\")\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
guard @attr.find(attrs, "app.get") is Some(a) else { abort("annotated") }
let spec = @attr.Spec::new("app.get").positional("path", Str).many("tag", Str)
let args = spec.bind(a)
inspect(args.str("path").unwrap_or(""), content="/users")
inspect(args.strs("tag").join(", "), content="users")

Pass the file the annotation was read from and every refusal names it, so the line and column in the message can be opened:

let source = "#app.get\nfn list_users() -> Unit {}"
let impls = @parser.parse_string(source).0
guard impls is @list.More(impl_, tail=@list.Empty) else {
abort("one declaration")
}
guard impl_ is @syntax.TopFuncDef(fun_decl={ attrs, .. }, ..) else {
abort("a function")
}
guard @attr.find(attrs, "app.get") is Some(a) else { abort("annotated") }
let refused = try {
let _ = @attr.Spec::new("app.get")
.positional("path", Str)
.bind(a, file="models.mbt")
"bound"
} catch {
e => e.to_string()
}
inspect(refused, content="models.mbt:1:1: path is required")
pub fn Spec::many(Self, String, Kind, positional? : Bool) -> Self #

A parameter that may be given any number of times, tags="a", tags="b", which is how a list is written where the grammar has no arrays. With positional, the list is written as bare arguments, #serde.derive(Serialize, Deserialize), and it takes every one of them from where it starts.

pub fn Spec::new(StringView) -> Self #
pub fn Spec::optional(Self, String, Kind) -> Self #

A parameter that may be left out.

pub fn Spec::positional(Self, String, Kind, optional? : Bool) -> Self #

A parameter that may be given by position, in the order these are declared, or by name. It is required unless optional says otherwise, which is what lets #orm.table("heroes") and a bare #orm.table both stand.

pub fn Spec::required(Self, String, Kind) -> Self #

A required parameter given by name.

pub(all) enum Value { #

One argument, as its kind reads it.

  • Str(String)
  • Bool(Bool)
  • Ident(String)
  • Type(String)
pub fn Value::equal(Self, Self) -> Bool #

Whether two values are equal, as <code>Eq</code> defines it.

pub fn Value::not_equal(Self, Self) -> Bool #

Whether two values differ, as <code>Eq</code> defines it.

pub fn Value::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

Errors

pub(all) suberror Refused { #

Why an annotation's arguments were not accepted, with where the annotation stands in its file. file is empty when the caller did not say which file it read the annotation from, and then the message names the position alone.

  • Malformed(raw~ : String, line~ : Int32, column~ : Int32, file~ : String) — The payload is outside the annotation grammar: arrays, numbers, a qualified or generic type cannot be written, and a keyword cannot be a label.
  • Missing(key~ : String, line~ : Int32, column~ : Int32, file~ : String) — A required parameter was not given.
  • Unknown(key~ : String, line~ : Int32, column~ : Int32, file~ : String) — A parameter the spec does not have.
  • Mistyped(key~ : String, expected~ : String, line~ : Int32, column~ : Int32, file~ : String) — A parameter of the wrong kind.
  • Repeated(key~ : String, line~ : Int32, column~ : Int32, file~ : String) — A parameter given twice that may only be given once.
pub fn Refused::output(Self, &Logger) -> Unit #

Writes the value to a logger, as <code>Show</code> defines it.

pub fn Refused::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub fn Refused::to_string(Self) -> String #

The value as a string, as <code>Show</code> defines it.

PLATE III

genmoonbitstack/moonast/gen

Declarations read from source into plain views, the Generator trait, and render, which gives the file a generator writes. No file access here.

Values

pub fn read(String, String) -> Array[Decl] raise Refused #

The declarations of one file, in source order. Refused when the file is not MoonBit, with the first problem's position.

let source = "pub struct Point {\n  x : Int\n  y : Int\n}"
guard @gen.read("models.mbt", source) is [Struct(name~, fields~, ..)] else {
abort("a struct")
}
inspect(name, content="Point")
inspect(fields.map(f => f.name).join(", "), content="x, y")
pub fn render(&Generator, Array[(String, String)]) -> String raise #

The file a generator writes for inputs: a one-line header naming the generator and the inputs, then the code. The same inputs give the same bytes, so a check can compare them.

Types

pub(all) struct Case { #

One variant of an enum, and the annotations written on it. Its payload is Empty, Tuple or Record; an enum inside an enum's variant would be a different type altogether, so Cases never appears here.

  • name : String
  • payload : Shape
  • attrs : @list.List[@attribute.Attribute]
pub fn Case::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub(all) enum Decl { #

A top-level declaration as a generator usually needs it. Types are kept as their source text, which is what a generator splices into new code; raw is the official syntax tree for anything the view leaves out.

  • Struct(name~ : String, fields~ : Array[Field], attrs~ : @list.List[@attribute.Attribute], public~ : Bool, file~ : String, raw~ : @syntax.Impl)
  • Enum(name~ : String, constructors~ : Array[String], cases~ : Array[Case], attrs~ : @list.List[@attribute.Attribute], public~ : Bool, file~ : String, raw~ : @syntax.Impl)
  • Func(owner~ : String?, name~ : String, params~ : Array[Param], returns~ : String?, is_async~ : Bool, attrs~ : @list.List[@attribute.Attribute], public~ : Bool, file~ : String, raw~ : @syntax.Impl)
  • Other(file~ : String, attrs~ : @list.List[@attribute.Attribute], raw~ : @syntax.Impl) — Anything else, with the annotations on it: a tuple struct is here, since Struct covers the record case only, and so is an impl or a trait.
pub fn Decl::attrs(Self) -> @list.List[@attribute.Attribute] #

The annotations on a declaration, whatever the view makes of its body.

let source = "#demo.describe\npub struct Point {\n  x : Int\n}"
guard @gen.read("models.mbt", source) is [decl] else {
abort("one declaration")
}
inspect(@attr.find(decl.attrs(), "demo.describe") is Some(_), content="true")
pub fn Decl::file(Self) -> String #

The file the declaration was read from, as read was told it. A refusal a generator writes names this file, so a reader can open the line it points at.

let source = "#demo.describe\npub struct Point {\n  x : Int\n}"
guard @gen.read("src/models.mbt", source) is [decl] else {
abort("one declaration")
}
inspect(decl.file(), content="src/models.mbt")
pub fn Decl::is_public(Self) -> Bool #

Whether the declaration is pub, for every kind the view has a variant for and for the tuple structs and traits that land in Other.

pub fn Decl::name(Self) -> String #

The name a declaration introduces: a type's, a function's or a trait's. Empty for one that introduces none, such as an impl. A tuple struct and a trait have no variant of their own, so this is how a generator learns their names without reading raw itself.

let source = "pub struct Rgb(Int, Int, Int)\n\npub fn f() -> Unit { () }"
let names = @gen.read("m.mbt", source).map(decl => decl.name())
inspect(names.join(", "), content="Rgb, f")
pub fn Decl::params(Self) -> Array[String] #

The generic parameters of a type declaration, as written: ["A", "B"] for struct Pair[A, B]. Empty for a function, whose own parameters live on Func.

guard @gen.read("m.mbt", "pub struct Pair[A, B] {\n  first : A\n}") is [decl] else {
abort("a struct")
}
inspect(decl.params().join(", "), content="A, B")
pub fn Decl::raises(Self) -> String #

What a function says after its return type, as source: empty, raise, raise E or raise? E, so a wrapper can say the same. A type with arguments cannot be an error type, so the name is all there is to print. Empty for anything that is not a function.

guard @gen.read("m.mbt", "fn f() -> Unit raise @a.Bad {}") is [decl] else {
abort("a function")
}
inspect(decl.raises(), content="raise @a.Bad")
pub fn Decl::raw(Self) -> @syntax.Impl #

The official syntax tree behind the view.

pub fn Decl::shape(Self) -> Shape? #

The body of a type declaration, tuple structs included: Struct covers the record case, and this answers the other two. None for a function or a declaration that is not a type at all.

let source = "pub struct Rgb(Int, Int, Int)"
guard @gen.read("m.mbt", source) is [decl] else { abort("a declaration") }
inspect(
match decl.shape() {
Some(Tuple(arity~)) => "tuple of \{arity}"
_ => "not a tuple struct"
},
content="tuple of 3",
)
pub(all) struct Field { #

A field of a struct, with its type as written in the source.

  • name : String
  • ty : String
  • mutable : Bool
  • optional : Bool
  • attrs : @list.List[@attribute.Attribute]
pub fn Field::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub(all) struct Param { #

A function parameter: _ has no name, and an unannotated one no type.

  • name : String?
  • ty : String?
  • pass : Pass
pub fn Param::declared(Self) -> String? #

A parameter as a wrapper's signature declares it: a : Int, a~ : Int, a? : Int. A parameter with a default comes out optional, since the default itself stays with the function being wrapped. None when it has no name or no type, so there is nothing to pass along.

guard @gen.read(
"m.mbt", "fn f(a : Int, b~ : String, c~ : Bool = true) -> Unit {}",
)
is [Func(params~, ..)] else {
abort("a function")
}
inspect(
params.map(p => p.declared().unwrap_or("")).join(", "),
content="a : Int, b~ : String, c? : Bool",
)
inspect(
params.map(p => p.passed().unwrap_or("")).join(", "),
content="a, b~, c?",
)
pub fn Param::passed(Self) -> String? #

A parameter as a wrapper passes it on: a, a~, a?. None when it has no name.

pub(all) enum Pass { #

How a function parameter is passed.

  • Positional
  • Labelled
  • Optional
  • Defaulted
pub fn Pass::equal(Self, Self) -> Bool #

Whether two values are equal, as <code>Eq</code> defines it.

pub fn Pass::not_equal(Self, Self) -> Bool #

Whether two values differ, as <code>Eq</code> defines it.

pub fn Pass::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub(all) enum Shape { #

The body of a type declaration: what a struct is made of, or the cases an enum has. One type for both, because a variant carries the same three shapes a struct can have — nothing, a run of positional types, or named fields.

let source =
#|pub enum Shape {
#|  Empty
#|  Circle(Double)
#|  Rect(Double, Double)
#|  Tagged(name~ : String, size~ : Int)
#|}
guard @gen.read("m.mbt", source) is [Enum(cases~, ..)] else {
abort("an enum")
}
let shapes = cases.map(c => {
match c.payload {
Empty => "empty"
Tuple(arity~) => "tuple of \{arity}"
Record(fields~) => "record of \{fields.length()}"
_ => "cases"
}
})
inspect(
shapes.join(", "),
content="empty, tuple of 1, tuple of 2, record of 2",
)
  • Empty
  • Tuple(arity~ : Int32) — struct Rgb(Int, Int, Int), or Circle(Double): a newtype when the count is one. A generator that writes self.0 and self.1 needs the count and nothing else; one that needs the types reads them off Decl::raw.
  • Record(fields~ : Array[Field]) — struct Point { x : Int }, or Tagged(name~ : String): the fields, with their types as written in the source.
  • Cases(cases~ : Array[Case]) — enum Shape { ... }: its variants, in source order.
pub fn Shape::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

Traits

pub(open) trait Generator { #

A generator: given every declaration of its input files, the code to write. One call sees all of them, so a derive can answer per declaration and a registrar can gather every annotated function into one table.

fn generate(Self, Array[Decl]) -> @quote.Code raise #

Every declaration of the inputs, in one call: the code to write for all of them.

fn name(Self) -> String #

Short and stable: it goes in the header of every file it writes.

Errors

pub(all) suberror Refused { #

Why a generator's inputs were not read.

  • Syntax(file~ : String, line~ : Int32, column~ : Int32, msg~ : String) — An input file is not MoonBit.
pub fn Refused::output(Self, &Logger) -> Unit #

Writes the value to a logger, as <code>Show</code> defines it.

pub fn Refused::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub fn Refused::to_string(Self) -> String #

The value as a string, as <code>Show</code> defines it.

PLATE IV

climoonbitstack/moonast/cli

The command line of a generator binary: it reads the inputs, has moonast/gen render them, and writes the output or reports that it is stale.

Values

pub async fn run(&@gen.Generator, ArrayView[String]) -> Unit #

Run generator the way a dev_build rule calls it: read every input, render them, and write the output; with --check, only compare and refuse when the output is stale. args are the arguments after the program name; -C <dir> makes relative paths relative to dir.

Errors

pub(all) suberror Refused { #

Why a generator run stopped.

  • Usage(msg~ : String) — The command line is not [-C <dir>] <inputs...> -o <output> [--check].
  • Stale(output~ : String) — --check found the output out of date with its inputs.
  • Generator(msg~ : String) — The generator refused what it read, in its own words.
pub fn Refused::output(Self, &Logger) -> Unit #

Writes the value to a logger, as <code>Show</code> defines it.

pub fn Refused::to_repr(Self) -> @debug.Repr #

The value as a <code>Repr</code>, as <code>Debug</code> defines it.

pub fn Refused::to_string(Self) -> String #

The value as a string, as <code>Show</code> defines it.