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:
parent
f8d811aa97
commit
42deb76c28
27 changed files with 1220 additions and 23 deletions
|
|
@ -11,14 +11,21 @@ function parse_component() -> Node {
|
|||
var is_sync = false # @Sync — this field replicates (NETWORKING N2)
|
||||
var is_frame = false # @frame — a fn stored here runs every frame (25.2)
|
||||
var cap = 0 # @max(n) — this list never holds more than n (25.5a)
|
||||
if is_op("@") {
|
||||
var fat = -1 # its attributes, for the schema (attrs.ludic)
|
||||
while is_op("@") { # one or more, on its line or the lines above
|
||||
pi += 1
|
||||
let ann = eat_id()
|
||||
if ann == "Computed" { is_computed = true } else if ann == "Sync" { is_sync = true } else if ann == "frame" { is_frame = true }
|
||||
if (ann == "Computed") or (ann == "Sync") or (ann == "frame") {
|
||||
if ann == "Computed" { is_computed = true } else if ann == "Sync" { is_sync = true } else { is_frame = true }
|
||||
if fat < 0 { fat = at_group() }
|
||||
at_push(fat, ann, "[]", null, 0)
|
||||
}
|
||||
else if ann == "owns" { # @owns(PhysShape) — this field holds a handle its record owns (25.5e)
|
||||
eat_op("(")
|
||||
g_own_pending = eat_id()
|
||||
eat_op(")")
|
||||
if fat < 0 { fat = at_group() }
|
||||
at_push(fat, ann, `["{g_own_pending}"]`, g_own_pending, 1)
|
||||
}
|
||||
else if ann == "max" {
|
||||
eat_op("(")
|
||||
|
|
@ -26,14 +33,30 @@ function parse_component() -> Node {
|
|||
if ce.kind != E_INT or ce.ival <= 0 { perr("@max takes the most the list may hold: @max(64)") }
|
||||
cap = ce.ival
|
||||
eat_op(")")
|
||||
if fat < 0 { fat = at_group() }
|
||||
at_push(fat, ann, `[{itoa(cap)}]`, itoa(cap), 1)
|
||||
}
|
||||
else if at_field_known(ann) { # @Ref(Items), @Range(0, 1), @Unit("m/s"), ...
|
||||
if fat < 0 { fat = at_group() }
|
||||
at_field(fat, ann)
|
||||
}
|
||||
else if is_op("(") { # anything else: read past, as a declaration's is
|
||||
var d = 0
|
||||
while true { if is_op("(") { d += 1 }; if is_op(")") { d -= 1 }; pi += 1; if d == 0 { break } }
|
||||
}
|
||||
skipnl()
|
||||
}
|
||||
let f = node(N_FIELD); f.s = eat_id(); eat_op(":"); f.ty = ptype()
|
||||
f.at = fat
|
||||
if is_frame { push(g_frame_fields, `{n.s}.{f.s}`) }
|
||||
if cap > 0 { push(g_cap_keys, `{n.s}.{f.s}`); push(g_cap_vals, cap) }
|
||||
if g_own_pending != null { push(g_own_type, n.s); push(g_own_field, f.s); push(g_own_kind, g_own_pending); g_own_pending = null }
|
||||
if is_op("=") { pi += 1; f.a = expr() }
|
||||
if is_op("=") {
|
||||
pi += 1
|
||||
let v0 = pi
|
||||
f.a = expr()
|
||||
f.sp0 = toks[v0].off; f.sp1 = toks[pi - 1].oend # the schema's text of the default
|
||||
}
|
||||
if is_sync { f.ival = 1 } # mark the field replicable (read by emit_net)
|
||||
if is_computed { register_computed(n.s, f.s, f.ty, f.a) } # derived: no storage
|
||||
else { push(n.kids, f) }
|
||||
|
|
@ -52,7 +75,12 @@ function parse_event() -> Node {
|
|||
n.s = eat_id(); skipnl(); eat_op("{")
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
let f = node(N_FIELD); f.s = eat_id(); eat_op(":"); f.ty = ptype()
|
||||
if is_op("=") { pi += 1; f.a = expr() }
|
||||
if is_op("=") {
|
||||
pi += 1
|
||||
let v0 = pi
|
||||
f.a = expr()
|
||||
f.sp0 = toks[v0].off; f.sp1 = toks[pi - 1].oend
|
||||
}
|
||||
push(n.kids, f)
|
||||
if is_op(",") { pi += 1 } }
|
||||
eat_op("}"); return n
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue