schema: ludicc --emit-schema / ludic schema, --check --diagnostics=json, and editor attributes

--emit-schema FILE writes the compiler's resolved view once the program type-checks: every
record (fields, types, defaults as written, docs, places, attributes), every registry with its
entries in their final order after the open-registry merge (key, constant, index, file:line:col
of the entry and of each field value, and which file contributed which keys), every const, and
the zero-argument functions a fn value can name. Deterministic, schema_version 1; the runtime is
left out. `ludic schema [file] [-o FILE]` wraps it.

--check --diagnostics=json prints every error as one JSON array on stdout: the checker's and the
module rules' all, a parse or lowering error as the last. Tokens and nodes now carry a column.

Fields take several @attributes; @Ref(Registry), @OneOf(PREFIX_), @Range(lo, hi), @Unit("..."),
@Asset("..."), @Color on a field and @AppendOnly / @ByKey on a registry change nothing but go into
the schema, and @Ref naming no registry is an error (every one reported). Fixtures:
examples/lang/attributes.ludic, examples/rejected/ref_unknown.ludic, cases in ludic-dev test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 18:06:54 +03:00
parent f8d811aa97
commit 42deb76c28
27 changed files with 1220 additions and 23 deletions

View file

@ -0,0 +1,38 @@
# attributes.ludic — editor metadata on the @-syntax: which registry a field indexes, which constants
# it takes, its range, its unit, the kind of file it names, that it is a colour, and a registry's own
# rules (append only, saved by key). None of them changes what the program does: `ludicc
# --emit-schema` carries them to an editor with every record, registry entry and const. One is
# checked - @Ref must name a registry that exists.
#
# Running it prints: 2 1 Lantern 2
program Attributes {
# a shop in the valley
property Vendor {
key: string = ""
name: string = "" # what the sign over the counter says
}
@AppendOnly @ByKey
registry Vendors of Vendor as VD
def Vendors store { name: "General store" }
def Vendors marina { name: "Marina" }
# a tool a camp keeps
property Tool {
key: string = ""
name: string = ""
@Ref(Vendors) seller: int = 0 # who sells it
@Range(0, 20.5) @Unit("kg") weight: float = 1.0
@Asset("gltf")
model: string = "" # its model, under assets/kit
@Color tint: int = 16777215
@OneOf(VD_) stocked_at: int = VD_STORE
}
@AppendOnly
registry Tools of Tool as TL from "data/attr_tools.lres"
const CAMP_TOOLS: int = 2 * VD_COUNT # how many a camp can carry
# the tool a new camp starts with
function first_tool() -> string { return Tools[0].name }
entry {
print(`{TL_COUNT} {Tools[TL_LANTERN].seller} {Tools[TL_LANTERN].name} {CAMP_TOOLS / 2}`)
}
}

View file

@ -0,0 +1,8 @@
# attr_tools.lres - the tools attributes.ludic's registry reads when it is compiled
axe { name: "Axe", seller: VD_STORE, weight: 1.5, model: "kit/axe.gltf" }
lantern {
name: "Lantern"
seller: VD_MARINA
weight: 0.75
stocked_at: VD_MARINA
}

View file

@ -0,0 +1,10 @@
# attributes: @Ref names a registry - there is no registry Vendorz, nor Shops (both are reported)
program R {
property Vendor { key: string = "" }
registry Vendors of Vendor
property Tool {
@Ref(Vendorz) seller: int = 0
@Ref(Shops) shop: int = 0
}
entry { print(VENDORS_COUNT) }
}