Composable, type-safe AST and compiler-pass generation for MoonBit.
Dependencies
┌─────────────┐
#nanopass.language │ meta_parser │ parse annotations, resolve extends chains,
└──────┬──────┘ apply #nano.remove, resolve entries/.mbti
│ NanoLangDef[]
▼
┌─────────────┐
│ language │ expand inheritance -> a layout, then
└──────┬──────┘ generate the unified Tree + wrappers
│ GeneratedLanguageLayout
┌───────────┼───────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│unparser/gen │ │ pass │ transform/fold scaffolding,
└──────┬──────┘ └─────────────┘ smart constructors, PassM
│ from_sexp / to_sexp_doc
▼
┌─────────────┐
│unparser/sexp│ S-expression runtime: parse, doc render, form matching
└─────────────┘///|
pub type LambdaVar = String
///|
#nanopasslanguage(name="Lambda")
pub(all) enum Expr {
Int(Int)
LambdaVar(LambdaVar)
Lam(LambdaVar, Expr)
App(Expr, Expr)
}
///|
#nanopasslanguage(name="SimplyTypedLambda", extends="Lambda")
enum TypedExpr {
True
False
Int(Int)
LambdaVar(LambdaVar)
If(cond~ : TypedExpr, TypedExpr, TypedExpr)
Lam(LambdaVar, Type, TypedExpr)
App(TypedExpr, TypedExpr)
} #nanoform("(lambda ($0 : $1) $2)")
#nanoform_only
Lam(LambdaVar, Type, TypedExpr)
#nanoinline // renders as ($0 $1) — no constructor head
App(TypedExpr, TypedExpr)#nanopasslanguage(name="NoApp", extends="Lambda")
#nanoremove("App")
enum NoAppExpr {
// only Lam, Int, LambdaVar are inherited; App is dropped
}let lang = @language.Language::from_file(
name="Lambda", path="poc/lang_def.mbt", mod="YumeXi/nanocake",
)
let impls = lang.gen()
@fmt.impls_to_string(impls) // format and write to filelet lang = @language.Language::from_file(...)
// Generate from_sexp / parse_sexp / to_sexp_doc / unparse_sexp per type.
let codec_impls = @gen.gen(lang)| API | Description |
|---|---|
| find_languages(src) | Parse #nanopass.language annotations from a SourceLocRepr |
| find_languages_by_file(path) | Parse language annotations from a file path |
| normalize_language_defs(langs) | Validate and resolve entries; returns langs with resolved_entry populated |
| resolve(loc) | Convert a SourceLoc to a structured SourceLocRepr |
| resolve_mbti(repr) | Parse the .mbti interface for a package |
| NanoLangDef::diff(base, deriv) | Compute the structural diff between two language definitions |
| NanoLangDef::with_constructors(self, constrs) | Return a copy with a new constructor set (rebuilds production_defs) |
| API | Description |
|---|---|
| Language::def(name?, loc~) | Resolve languages from the callsite file |
| Language::from_file(name?, path~, mod~) | Resolve languages from an explicit file |
| Language::gen() | Generate AST definitions (Tree, wrappers, ext enums, terminals, entry stubs) |
| Language::layout() | Compute GeneratedLanguageLayout (shared by gen and codec generator) |
| Language::expand() | Expand the group into ExpandedLanguageGroup (inheritance + remove applied) |
| expand_language_group(raw) | Standalone expansion without a Language object |
| API | Description |
|---|---|
| parse(src) | Parse a StringView into a Sexp |
| match_form(sexp, pattern, field_count) | Match a sexp against a $0/$1-placeholder pattern |
| form_doc(pattern, fields) | Render field docs into a surface form |
| expect_atom / expect_list / expect_arity | Structural decode helpers |
| hook_error / decode_error / literal_failure | Structured error constructors |
| SexpDoc::render(self, width?) | Pretty-print a doc to a string |
| API | Description |
|---|---|
| gen(lang) | Generate Array[@syntax.Impl] codecs for all types in a language group |
| gen_source(lang) | Return the generated source as a String (for inspection/snapshotting) |
| Milestone | What is generated |
|---|---|
| M1 (surface) | Smart constructors + view functions for wrapper-based ASTs |
| M2 (cata) | Catamorphism cata for bottom-up folds |
| M3 (rewrite_m) | Effectful delayed-subtree rewrite with RewriteAlg |
| M4 (stub) | Diff-driven pass stub generator |
| M5 (PassM) | Effectful pipeline/trace combinators |
Composable, type-safe AST and compiler-pass generation for MoonBit.
Dependencies