feat(lang): def REGISTRY from "file.lres" - a game fills an open registry from its own file
The entries are read and checked against the registry's record as a registry ... from file is, errors at the resource file's line, and they are defs of the module that wrote the line: the registry must be open to it, and they take that module's place in the stable order. Reseed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a09b622a1d
commit
a5f52c4581
13 changed files with 43732 additions and 43470 deletions
16
LANGUAGE.md
16
LANGUAGE.md
|
|
@ -400,6 +400,22 @@ The entries are compiled in: nothing is parsed at start-up, and a build that suc
|
||||||
every resource it uses. The path is the project's (where the build runs), else beside the file that
|
every resource it uses. The path is the project's (where the build runs), else beside the file that
|
||||||
declares the registry.
|
declares the registry.
|
||||||
|
|
||||||
|
|
||||||
|
A module that extends an open registry can bring its entries from a resource file of its own:
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
# doc-check: skip — the file it names is beside the example
|
||||||
|
import "crafting" # module crafting: export open registry Recipes of Recipe as RC
|
||||||
|
def Recipes from "recipes.lres" # the game's recipes, after crafting's own
|
||||||
|
```
|
||||||
|
|
||||||
|
`def REGISTRY from "file.lres"` reads the file as `registry ... from` does - the project's path, else
|
||||||
|
beside the file that says it - and checks every entry against the registry's record, an error
|
||||||
|
naming the resource file's own line (`bad_recipes.lres:3: error: field minutes of Recipe wants an
|
||||||
|
int and this is a string`). Its entries are defs of the module that wrote the line, so the registry
|
||||||
|
must be open (and exported) to it, and they take that module's place in the order: the declaring
|
||||||
|
module's entries, then each other module's by module name, and within a file, file order.
|
||||||
|
|
||||||
### Default parameters, and calls that name what they change
|
### Default parameters, and calls that name what they change
|
||||||
|
|
||||||
A parameter can have a default, and a call leaves out what it does not change - the last ones when
|
A parameter can have a default, and a call leaves out what it does not change - the last ones when
|
||||||
|
|
|
||||||
7
changes/def-from-resource.md
Normal file
7
changes/def-from-resource.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
bump: minor
|
||||||
|
type: feature
|
||||||
|
**`def Recipes from "recipes.lres"` - a game fills a package's open registry from its own resource
|
||||||
|
file.** The entries are checked against the registry's record as the file is read, with errors at
|
||||||
|
the resource file's line, and they are defs of the module that wrote the line: the registry must be
|
||||||
|
open to it, and they sit in the stable order (the declaring module's entries, then other modules'
|
||||||
|
by name, and file order within a file).
|
||||||
9
examples/modules/crafting/index.ludic
Normal file
9
examples/modules/crafting/index.ludic
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# crafting/index.ludic - a registry the game may add to, and the package's own first entry
|
||||||
|
module crafting
|
||||||
|
export property Recipe {
|
||||||
|
key: string = ""
|
||||||
|
name: string = ""
|
||||||
|
minutes: int = 0
|
||||||
|
}
|
||||||
|
export open registry Recipes of Recipe as RC
|
||||||
|
def Recipes rope { name: "Rope", minutes: 10 }
|
||||||
3
examples/modules/recipes.lres
Normal file
3
examples/modules/recipes.lres
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# recipes.lres - the game's own recipes, added to crafting's open registry
|
||||||
|
tea { name: "Pine tea", minutes: 5 }
|
||||||
|
splint { name: "Splint", minutes: 20 }
|
||||||
12
examples/modules/registry_extend.ludic
Normal file
12
examples/modules/registry_extend.ludic
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# registry_extend.ludic — L8 + L9: a game fills a package's open registry from its own resource
|
||||||
|
# file with `def Recipes from "recipes.lres"`. The entries are checked against Recipe as the file
|
||||||
|
# is read, and they come after the package's own, in file order.
|
||||||
|
#
|
||||||
|
# Running it prints: 3 Rope Pine tea Splint 20
|
||||||
|
import "crafting"
|
||||||
|
program RegistryExtend {
|
||||||
|
def Recipes from "recipes.lres"
|
||||||
|
entry {
|
||||||
|
print(`{RC_COUNT} {Recipes[0].name} {Recipes[RC_TEA].name} {Recipes[2].name} {Recipes[recipes_find("splint")].minutes}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
3
examples/rejected/bad_recipes.lres
Normal file
3
examples/rejected/bad_recipes.lres
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# bad_recipes.lres - the second entry gives minutes as text
|
||||||
|
tea { name: "Pine tea", minutes: 5 }
|
||||||
|
splint { name: "Splint", minutes: "twenty" }
|
||||||
8
examples/rejected/craft_kit/index.ludic
Normal file
8
examples/rejected/craft_kit/index.ludic
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# craft_kit/index.ludic - an open registry of recipes
|
||||||
|
module crafting
|
||||||
|
export property Recipe {
|
||||||
|
key: string = ""
|
||||||
|
name: string = ""
|
||||||
|
minutes: int = 0
|
||||||
|
}
|
||||||
|
export open registry Recipes of Recipe as RC
|
||||||
8
examples/rejected/registry_extend_field.ludic
Normal file
8
examples/rejected/registry_extend_field.ludic
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# L8 + L9: a resource file that extends a registry is checked against its record, at its own line
|
||||||
|
import "craft_kit"
|
||||||
|
program RegistryExtendField {
|
||||||
|
def Recipes from "bad_recipes.lres"
|
||||||
|
entry {
|
||||||
|
print(RC_COUNT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,9 +75,16 @@ function parse_registry() -> void {
|
||||||
if from != null { res_read(v, from) }
|
if from != null { res_read(v, from) }
|
||||||
}
|
}
|
||||||
# def REGISTRY key { field: value, ... }
|
# def REGISTRY key { field: value, ... }
|
||||||
|
# def REGISTRY from "file.lres" - every entry of a resource file, as defs of this file's module
|
||||||
function parse_def() -> void {
|
function parse_def() -> void {
|
||||||
pi += 1
|
pi += 1
|
||||||
let reg = eat_id()
|
let reg = eat_id()
|
||||||
|
if is_id("from") and toks[pi + 1].kind == TK_STR {
|
||||||
|
let from = toks[pi + 1].text
|
||||||
|
pi += 2
|
||||||
|
res_read_into(reg, from, `def {reg} from`)
|
||||||
|
return
|
||||||
|
}
|
||||||
let key = eat_id()
|
let key = eat_id()
|
||||||
skipnl()
|
skipnl()
|
||||||
let rec = record()
|
let rec = record()
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,14 @@ function res_path(from: pointer, declaring: pointer) -> pointer {
|
||||||
return join_path(dir_of(declaring), from)
|
return join_path(dir_of(declaring), from)
|
||||||
}
|
}
|
||||||
function res_read(v: Node, from: pointer) -> void {
|
function res_read(v: Node, from: pointer) -> void {
|
||||||
|
res_read_into(v.s, from, `registry {v.s}`)
|
||||||
|
}
|
||||||
|
# the entries of a resource file into registry `reg`, as defs of the reading file's module: a
|
||||||
|
# registry's own file, or `def REG from "file.lres"` in a module that extends an open registry
|
||||||
|
function res_read_into(reg: pointer, from: pointer, who: pointer) -> void {
|
||||||
let path = res_path(from, g_parse_file)
|
let path = res_path(from, g_parse_file)
|
||||||
let src = read_file(path)
|
let src = read_file(path)
|
||||||
if src == null { perr(`registry {v.s}: cannot read the resource file {from}`) }
|
if src == null { perr(`{who}: cannot read the resource file {from}`) }
|
||||||
let saved_toks = toks
|
let saved_toks = toks
|
||||||
let saved_pi = pi
|
let saved_pi = pi
|
||||||
let saved_file = g_parse_file
|
let saved_file = g_parse_file
|
||||||
|
|
@ -36,7 +41,7 @@ function res_read(v: Node, from: pointer) -> void {
|
||||||
let key = eat_id()
|
let key = eat_id()
|
||||||
skipnl()
|
skipnl()
|
||||||
let rec = record()
|
let rec = record()
|
||||||
push(g_df_reg, v.s)
|
push(g_df_reg, reg)
|
||||||
push(g_df_key, key)
|
push(g_df_key, key)
|
||||||
push(g_df_rec, rec)
|
push(g_df_rec, rec)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
43559
selfhost/ludicc.seed.ll
43559
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -820,6 +820,8 @@ function cmd_dev_test() -> int {
|
||||||
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)")
|
||||||
reject_case("rejected/registry_closed", "registry Tools is not open to other modules", "a def from another module into a registry that is not open is refused")
|
reject_case("rejected/registry_closed", "registry Tools is not open to other modules", "a def from another module into a registry that is not open is refused")
|
||||||
reject_case("rejected/registry_hidden", "Tools is private to module kit", "a def goes through visibility: the registry must be exported")
|
reject_case("rejected/registry_hidden", "Tools is private to module kit", "a def goes through visibility: the registry must be exported")
|
||||||
|
feat_case("modules/registry_extend", "", "3 Rope Pine tea Splint 20", "registry_extend.ludic (L8+L9: `def Recipes from \"recipes.lres\"` fills a package's open registry from the game's file)")
|
||||||
|
reject_case("rejected/registry_extend_field", "bad_recipes.lres:3: error: field minutes of Recipe wants an int", "a resource file extending a registry is checked at its own line")
|
||||||
feat_case("lang/resources", "", "2 Axe 2 Split 10 0 1", "resources.ludic (L9: a registry read from a resource file at compile time, nested records typed by the schema)")
|
feat_case("lang/resources", "", "2 Axe 2 Split 10 0 1", "resources.ludic (L9: a registry read from a resource file at compile time, nested records typed by the schema)")
|
||||||
reject_case("rejected/resource_field", "bad_tools.lres:3: error: field weight of Tool wants a float", "a resource file's entry is checked at its own line")
|
reject_case("rejected/resource_field", "bad_tools.lres:3: error: field weight of Tool wants a float", "a resource file's entry is checked at its own line")
|
||||||
reject_case("rejected/resource_missing", "cannot read the resource file data/no_such.lres", "a registry's resource file must exist")
|
reject_case("rejected/resource_missing", "cannot read the resource file data/no_such.lres", "a registry's resource file must exist")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue