schema: every function a fn value can name, not only zero-argument ones - fn_type is the type a field sees with the states stripped, spelled as a field's type is (fn(A,B)->R), with params beside states; reseeded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 18:15:04 +03:00
parent 863b9712f9
commit b647964839
7 changed files with 18325 additions and 18333 deletions

View file

@ -661,8 +661,10 @@ object with `"schema_version": 1`:
"index": 0, "file": ..., "line": ..., "col": ..., "fields": [{"name", "value", "file", "line", "index": 0, "file": ..., "line": ..., "col": ..., "fields": [{"name", "value", "file", "line",
"col"}]}`), with `contributors`: which resource file or file of `def`s brought which keys in; "col"}]}`), with `contributors`: which resource file or file of `def`s brought which keys in;
- `consts` - every const: its type, its value as written, its module and doc; - `consts` - every const: its type, its value as written, its module and doc;
- `functions` - every function a `fn` value can name with no arguments (the states it takes are the - `functions` - every function a `fn` value can name, exported or not: its module, return type,
runtime's to supply): its return type, `fn_type` and signature. `params` (the arguments a caller passes), `states` (what the runtime supplies), `signature`, and
`fn_type` - the type a `fn` field sees, the states stripped, spelled as a field's type is
(`fn(NpcPerson,float)->bool`), so matching a function to a field is comparing two strings.
Each list is sorted by name (then file and line), a registry's entries are in index order, and the Each list is sorted by name (then file and line), a registry's entries are in index order, and the
paths are the ones the compiler read, so two runs over the same source write the same file. The paths are the ones the compiler read, so two runs over the same source write the same file. The

View file

@ -5,7 +5,7 @@ schema [file] [-o FILE]`) writes what the compiler resolved once the program typ
record with its fields' types, defaults, doc comments and places; every registry with its record, record with its fields' types, defaults, doc comments and places; every registry with its record,
prefix, resource file, openness and its entries in their final order after the open-registry merge prefix, resource file, openness and 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 brought (key, constant, index, file:line:col of the entry and of each field value, and which file brought
which entries in); every const; and the functions a `fn` value can name with no arguments. which entries in); every const; and every function a `fn` value can name, with the `fn_type` a field sees (its states stripped).
Deterministic, `"schema_version": 1`. Fields and registries carry editor attributes on the existing Deterministic, `"schema_version": 1`. Fields and registries carry editor attributes on the existing
`@` syntax - `@Ref(Registry)`, `@OneOf(PREFIX_)`, `@Range(lo, hi)`, `@Unit("m/s")`, `@Asset("gltf")`, `@` syntax - `@Ref(Registry)`, `@OneOf(PREFIX_)`, `@Range(lo, hi)`, `@Unit("m/s")`, `@Asset("gltf")`,
`@Color`, and `@AppendOnly` / `@ByKey` on a registry - which change nothing but go into the schema; `@Color`, and `@AppendOnly` / `@ByKey` on a registry - which change nothing but go into the schema;

View file

@ -32,6 +32,8 @@ program Attributes {
const CAMP_TOOLS: int = 2 * VD_COUNT # how many a camp can carry const CAMP_TOOLS: int = 2 * VD_COUNT # how many a camp can carry
# the tool a new camp starts with # the tool a new camp starts with
function first_tool() -> string { return Tools[0].name } function first_tool() -> string { return Tools[0].name }
# what a pack of n of a tool weighs - a fn(Tool,int)->float an editor can offer a field of that type
function pack_weight(t: Tool, n: int) -> float { return t.weight * float(n) }
entry { entry {
print(`{TL_COUNT} {Tools[TL_LANTERN].seller} {Tools[TL_LANTERN].name} {CAMP_TOOLS / 2}`) print(`{TL_COUNT} {Tools[TL_LANTERN].seller} {Tools[TL_LANTERN].name} {CAMP_TOOLS / 2}`)
} }

View file

@ -8,8 +8,8 @@
# entries in their final order - key, constant, index, where each entry and each of its # entries in their final order - key, constant, index, where each entry and each of its
# field values is written - with which file contributed which entries # field values is written - with which file contributed which entries
# consts every const: its type and its value as written # consts every const: its type and its value as written
# functions every function a `fn` value can name with no arguments (the states it takes are the # functions every function a `fn` value can name: fn_type is the type a field sees, the states
# runtime's to supply) # stripped (the runtime supplies them), written as a field's type is ("fn(A,B)->R")
# #
# Deterministic: each list sorted by name (then file and line), an entry list in index order, the # Deterministic: each list sorted by name (then file and line), an entry list in index order, the
# paths as the compiler read them, and nothing about the run. The engine's runtime is left out. # paths as the compiler read them, and nothing about the run. The engine's runtime is left out.
@ -529,13 +529,6 @@ function sc_functions(b: Buf) -> void {
let d = prog[i] let d = prog[i]
var ok = d.kind == N_FN and d.s != null and d.tps == null and sc_written(d) var ok = d.kind == N_FN and d.s != null and d.tps == null and sc_written(d)
if ok { ok = not sc_generic_fn(gens, d.s) } if ok { ok = not sc_generic_fn(gens, d.s) }
if ok {
var k = 0
while k < len(d.kids) {
if d.kids[k].kind == N_PARAM and not sc_is_state_param(d.kids[k]) { ok = false }
k += 1
}
}
if ok { push(xs, i); push(keys, sc_key(sc_name(d.s), d.file, d.line)) } if ok { push(xs, i); push(keys, sc_key(sc_name(d.s), d.file, d.line)) }
else { push(keys, "") } else { push(keys, "") }
i += 1 i += 1
@ -550,6 +543,8 @@ function sc_functions(b: Buf) -> void {
function sc_function(b: Buf, d: Node, first: bool) -> void { function sc_function(b: Buf, d: Node, first: bool) -> void {
let ret = sc_ty(d.ty) let ret = sc_ty(d.ty)
var ps: pointer = "" var ps: pointer = ""
var args: pointer = ""
var params: pointer = ""
var states: pointer = "" var states: pointer = ""
var k = 0 var k = 0
while k < len(d.kids) { while k < len(d.kids) {
@ -561,8 +556,15 @@ function sc_function(b: Buf, d: Node, first: bool) -> void {
ps = ps + p.s + ": " + mt + sc_ty(p.ty) ps = ps + p.s + ": " + mt + sc_ty(p.ty)
let bq = buf_new() let bq = buf_new()
jq_put(bq, sc_ty(p.ty)) jq_put(bq, sc_ty(p.ty))
if sc_is_state_param(p) {
if len(states) > 0 { states = states + ", " } if len(states) > 0 { states = states + ", " }
states = states + buf_str(bq) states = states + buf_str(bq)
} else {
if len(args) > 0 { args = args + "," }
args = args + sc_ty(p.ty)
if len(params) > 0 { params = params + ", " }
params = params + buf_str(bq)
}
} }
k += 1 k += 1
} }
@ -578,7 +580,10 @@ function sc_function(b: Buf, d: Node, first: bool) -> void {
buf_puts(b, ", \"returns\": ") buf_puts(b, ", \"returns\": ")
jq_put(b, ret) jq_put(b, ret)
buf_puts(b, ", \"fn_type\": ") buf_puts(b, ", \"fn_type\": ")
jq_put(b, `fn() -> {ret}`) jq_put(b, "fn(" + args + ")->" + ret)
buf_puts(b, ", \"params\": [")
buf_puts(b, params)
buf_puts(b, "]")
buf_puts(b, ", \"signature\": ") buf_puts(b, ", \"signature\": ")
jq_put(b, `function {sc_name(d.s)}({ps}) -> {ret}`) jq_put(b, `function {sc_name(d.s)}({ps}) -> {ret}`)
buf_puts(b, ", \"states\": [") buf_puts(b, ", \"states\": [")

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -1234,6 +1234,7 @@ function cmd_dev_test() -> int {
reject_case("rejected/ref_unknown", "field Tool.seller: @Ref(Vendorz) names no registry", "@Ref names a registry that exists") reject_case("rejected/ref_unknown", "field Tool.seller: @Ref(Vendorz) names no registry", "@Ref names a registry that exists")
schema_case("lang/attributes", "{\"name\": \"weight\", \"type\": \"float\", \"default\": \"1.0\", \"file\": \"examples/lang/attributes.ludic\", \"line\": 24, \"col\": 33, \"attributes\": [{\"name\": \"Range\", \"args\": [0, 20.5]}, {\"name\": \"Unit\", \"args\": [\"kg\"]}]", "--emit-schema: a field's type, default, place and attributes") schema_case("lang/attributes", "{\"name\": \"weight\", \"type\": \"float\", \"default\": \"1.0\", \"file\": \"examples/lang/attributes.ludic\", \"line\": 24, \"col\": 33, \"attributes\": [{\"name\": \"Range\", \"args\": [0, 20.5]}, {\"name\": \"Unit\", \"args\": [\"kg\"]}]", "--emit-schema: a field's type, default, place and attributes")
schema_case("lang/attributes", "{\"key\": \"lantern\", \"constant\": \"TL_LANTERN\", \"index\": 1, \"file\": \"examples/lang/data/attr_tools.lres\", \"line\": 3, \"col\": 1", "--emit-schema: a resource entry's constant, index and place") schema_case("lang/attributes", "{\"key\": \"lantern\", \"constant\": \"TL_LANTERN\", \"index\": 1, \"file\": \"examples/lang/data/attr_tools.lres\", \"line\": 3, \"col\": 1", "--emit-schema: a resource entry's constant, index and place")
schema_case("lang/attributes", "\"fn_type\": \"fn(Tool,int)->float\", \"params\": [\"Tool\", \"int\"]", "--emit-schema: a function with arguments, its fn_type spelled as a field's")
reject_case("rejected/registry_twice", "def Furnishings crate is declared twice", "a registry's key is declared once") reject_case("rejected/registry_twice", "def Furnishings crate is declared twice", "a registry's key is declared once")
reject_case("rejected/registry_field", "Furnishing has no field colour", "a def's fields are the record's") reject_case("rejected/registry_field", "Furnishing has no field colour", "a def's fields are the record's")
feat_case("modules/open_registry", "", "clock animals weather / 2 2 3", "open_registry.ludic (L8+L3: an open registry takes other modules' defs; its own entries first, then by module name)") feat_case("modules/open_registry", "", "clock animals weather / 2 2 3", "open_registry.ludic (L8+L3: an open registry takes other modules' defs; its own entries first, then by module name)")