attributes: @Node(field), @Derived, @Text, @Multiline and @Key on a field, into the schema - @Node's field must be @Asset("gltf") or an @Ref to a registry whose record has exactly one, every failure reported; reseeded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 18:31:17 +03:00
parent b647964839
commit ebfbabdfd8
10 changed files with 73439 additions and 71415 deletions

View file

@ -8,10 +8,15 @@
# @Unit("m/s") speed: float = 0.0
# @Asset("gltf") model: string = "" # a file of that kind
# @Color tint: int = 0
# @Node(model) grip: string = "" # a node inside the glTF the field `model` names
# @Derived reach: float = 0.0 # worked out at boot: anything written is overwritten
# @Text @Multiline blurb: string = "" # read by the player, so translated; prose
# @Key bind: int = 0 # a key code
# }
# @AppendOnly @ByKey registry Items of Item as IT from "items.lres"
#
# They change nothing the program does. One is checked: @Ref must name a registry that exists. A
# They change nothing the program does. Two are checked: @Ref must name a registry that exists, and
# @Node a field of its record that names a glTF (attrs_node.ludic). A
# field's or a registry's attributes are a group (Node.at, g_rg_at): a run of names, each with its
# arguments as JSON values - an identifier or a string as a string, a number as written.
var g_at_start: []int = new []int # a group's first attribute ...
@ -92,19 +97,24 @@ function at_lead0(raw: pointer) -> pointer {
}
# the editor attributes a field may carry, and what each takes
function at_field_known(a: pointer) -> bool {
return (a == "Ref") or (a == "OneOf") or (a == "Range") or (a == "Unit") or (a == "Asset") or (a == "Color")
if (a == "Ref") or (a == "OneOf") or (a == "Range") or (a == "Unit") or (a == "Asset") or (a == "Color") { return true }
return (a == "Node") or at_flag(a)
}
# the attributes that take nothing: they say what a field is
function at_flag(a: pointer) -> bool {
return (a == "Color") or (a == "Derived") or (a == "Text") or (a == "Multiline") or (a == "Key")
}
# one field attribute, its '@' and name already read: its arguments, checked for shape, into group g
function at_field(g: int, a: pointer) -> void {
let args = at_args()
let n = g_at_argn
if (a == "Ref") or (a == "OneOf") {
if (a == "Ref") or (a == "OneOf") or (a == "Node") {
if n != 1 or toks[pi - 2].kind != TK_ID { perr(`@{a} takes one name: @{a}(Items)`) }
}
if (a == "Range") and n != 2 { perr("@Range takes the least and the most: @Range(0, 1.5)") }
if (a == "Range") and not g_at_allnum { perr("@Range takes two numbers: @Range(0, 1.5)") }
if ((a == "Unit") or (a == "Asset")) and (n != 1 or toks[pi - 2].kind != TK_STR) { perr(`@{a} takes one string: @{a}("m/s")`) }
if (a == "Color") and n != 0 { perr("@Color takes nothing") }
if at_flag(a) and n != 0 { perr(`@{a} takes nothing`) }
at_push(g, a, args, g_at_arg0, n)
}
# a field's group, made the first time it needs one
@ -135,7 +145,8 @@ function at_registry_take() -> int {
g_at_decl = new []pointer
return g
}
# once every registry is declared: each @Ref names one, or the build stops with every one that does not
# once every registry is declared: each @Ref names one and each @Node a glTF field, or the build
# stops with every one that does not
function at_check_refs() -> void {
var i = 0
while i < len(prog) {
@ -159,6 +170,7 @@ function at_check_field(d: Node, f: Node) -> void {
if (g_at_name[j] == "Ref") and reg_find(g_at_first[j]) < 0 {
perr_at(f.file, f.line, f.col, `field {d.s}.{f.s}: @Ref({g_at_first[j]}) names no registry - there is no registry {g_at_first[j]}`)
}
if g_at_name[j] == "Node" { at_check_node(d, f, g_at_first[j]) }
j += 1
}
}

View file

@ -0,0 +1,69 @@
# attrs_node.ludic — @Node(model): a string naming a node inside the glTF another field of the same
# record names. That field is @Asset("gltf") itself, or an @Ref(R) whose record has exactly one
# @Asset("gltf") field (the model is found through the row). Anything else is an error naming it.
# the field of record d called name, or null
function at_field_of(d: Node, name: pointer) -> Node {
var k = 0
while k < len(d.kids) {
if d.kids[k].s != null and vis_plain(d.kids[k].s) == name { return d.kids[k] }
k += 1
}
return null
}
# field f's argument to attribute a as written, or null when f does not carry it
function at_arg_of(f: Node, a: pointer) -> pointer {
if f.at < 0 { return null }
var j = g_at_start[f.at]
while j < g_at_start[f.at] + g_at_count[f.at] {
if g_at_name[j] == a { return g_at_first[j] }
j += 1
}
return null
}
function at_is_gltf(f: Node) -> bool {
let a = at_arg_of(f, "Asset")
return a != null and a == "gltf"
}
# the record called name (a property or record), or null
function at_record(name: pointer) -> Node {
let plain = vis_plain(name)
var i = 0
while i < len(prog) {
if prog[i].kind == N_COMP and prog[i].s != null and vis_plain(prog[i].s) == plain { return prog[i] }
i += 1
}
return null
}
# how many @Asset("gltf") fields record r carries
function at_gltf_count(r: Node) -> int {
var n = 0
var k = 0
while k < len(r.kids) {
if at_is_gltf(r.kids[k]) { n += 1 }
k += 1
}
return n
}
function at_check_node(d: Node, f: Node, target: pointer) -> void {
let where = `field {d.s}.{f.s}: @Node({target})`
let t = at_field_of(d, target)
if t == null {
perr_at(f.file, f.line, f.col, `{where} names no field - {d.s} has no field {target}`)
return
}
if at_is_gltf(t) { return }
let reg = at_arg_of(t, "Ref")
if reg == null {
perr_at(f.file, f.line, f.col, `{where}: {target} is neither @Asset("gltf") nor an @Ref to a registry of models`)
return
}
let r = reg_find(reg)
if r < 0 { return } # @Ref's own check says so
let rec = at_record(g_rg_type[r])
var n = 0
if rec != null { n = at_gltf_count(rec) }
if n != 1 {
perr_at(f.file, f.line, f.col, `{where}: {target} is an @Ref({reg}), and its record {g_rg_type[r]} has {n} @Asset("gltf") fields, not one`)
}
}