attributes: @Clip(field) resolved as @Node is, and @OneOf takes constants as well as a prefix - one argument ending in _ is a prefix, otherwise each is a constant that must exist; reseeded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 18:34:33 +03:00
parent ebfbabdfd8
commit 34c2ac2cb9
9 changed files with 73220 additions and 71912 deletions

View file

@ -3,20 +3,23 @@
#
# property Item {
# @Ref(Vendors) seller: int = 0 # an index into that registry (its entries are offered)
# @OneOf(GR_) grade: int = 0 # one of the constants that start GR_
# @OneOf(GR_) grade: int = 0 # one of the constants that start GR_ ...
# @OneOf(WILD_MEADOW, WILD_SHORE) hab: int = 0 # ... or one of these (each must exist)
# @Range(0, 1.5) weight: float = 0.4
# @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
# @Clip(model) idle: string = "" # a clip inside that glTF
# @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. 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
# They change nothing the program does. Three are checked: @Ref must name a registry that exists,
# @Node and @Clip a field of its record that names a glTF (attrs_node.ludic), and a listing @OneOf
# constants that exist. 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 ...
@ -47,10 +50,12 @@ function at_push(g: int, name: pointer, args: pointer, first: pointer, nargs: in
var g_at_arg0: pointer = null
var g_at_argn: int = 0
var g_at_allnum: bool = true # every argument a number
var g_at_allid: bool = true # every argument a name
function at_args() -> pointer {
g_at_arg0 = null
g_at_argn = 0
g_at_allnum = true
g_at_allid = true
if not is_op("(") { return "[]" }
pi += 1
var out: pointer = "["
@ -59,6 +64,7 @@ function at_args() -> pointer {
var neg = false
if is_op("-") { neg = true; pi += 1 }
let t = toks[pi]
if t.kind != TK_ID { g_at_allid = false }
var v: pointer = null
var raw: pointer = null
if t.kind == TK_INT {
@ -98,7 +104,7 @@ function at_lead0(raw: pointer) -> pointer {
# the editor attributes a field may carry, and what each takes
function at_field_known(a: pointer) -> bool {
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)
return (a == "Node") or (a == "Clip") or at_flag(a)
}
# the attributes that take nothing: they say what a field is
function at_flag(a: pointer) -> bool {
@ -108,9 +114,10 @@ function at_flag(a: pointer) -> bool {
function at_field(g: int, a: pointer) -> void {
let args = at_args()
let n = g_at_argn
if (a == "Ref") or (a == "OneOf") or (a == "Node") {
if (a == "Ref") or (a == "Node") or (a == "Clip") {
if n != 1 or toks[pi - 2].kind != TK_ID { perr(`@{a} takes one name: @{a}(Items)`) }
}
if (a == "OneOf") and (n < 1 or not g_at_allid) { perr("@OneOf takes a prefix or constants: @OneOf(GR_) or @OneOf(GR_GOLD, GR_SILVER)") }
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")`) }
@ -145,7 +152,8 @@ function at_registry_take() -> int {
g_at_decl = new []pointer
return g
}
# once every registry is declared: each @Ref names one and each @Node a glTF field, or the build
# once every registry is declared: each @Ref names one, each @Node / @Clip a glTF field and a
# listing @OneOf constants that exist, or the build
# stops with every one that does not
function at_check_refs() -> void {
var i = 0
@ -170,7 +178,8 @@ 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]) }
if (g_at_name[j] == "Node") or (g_at_name[j] == "Clip") { at_check_node(d, f, g_at_name[j], g_at_first[j]) }
if g_at_name[j] == "OneOf" { at_check_oneof(d, f, j) }
j += 1
}
}

View file

@ -1,5 +1,5 @@
# 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
# attrs_node.ludic — @Node(model) / @Clip(model): a string naming a node or a clip 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
@ -45,8 +45,8 @@ function at_gltf_count(r: Node) -> int {
}
return n
}
function at_check_node(d: Node, f: Node, target: pointer) -> void {
let where = `field {d.s}.{f.s}: @Node({target})`
function at_check_node(d: Node, f: Node, a: pointer, target: pointer) -> void {
let where = `field {d.s}.{f.s}: @{a}({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}`)
@ -67,3 +67,41 @@ function at_check_node(d: Node, f: Node, target: pointer) -> void {
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`)
}
}
# @OneOf: one argument ending in _ is a prefix; otherwise every argument is a constant that must exist
function at_check_oneof(d: Node, f: Node, j: int) -> void {
let first = g_at_first[j]
if g_at_nargs[j] == 1 and first[len(first) - 1] == '_' { return }
let names = at_arg_names(g_at_args[j])
var i = 0
while i < len(names) {
if not at_const_exists(names[i]) {
perr_at(f.file, f.line, f.col, `field {d.s}.{f.s}: @OneOf names {names[i]}, and there is no constant {names[i]}`)
}
i += 1
}
}
# the names in an attribute's JSON arguments (["A", "B"]): each a quoted identifier
function at_arg_names(args: pointer) -> []pointer {
let out = new []pointer
var i = 0
var at = -1
while i < len(args) {
if args[i] == '"' {
if at < 0 { at = i + 1 } else {
push(out, args[at .. i])
at = -1
}
}
i += 1
}
return out
}
function at_const_exists(name: pointer) -> bool {
var i = 0
while i < len(prog) {
if prog[i].kind == N_CONST and prog[i].s != null and vis_plain(prog[i].s) == name { return true }
i += 1
}
return false
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff