feat(lang): a name is defined once, a local once per block, and a result is always returned
L1. Two vars, consts, enums, properties or events of one name in a program are an error naming both places (functions already were); the first used to win silently. A let / var of a name its own block already declared is an error (it used to shadow). A function with a result type whose body can reach its end without a return is an error, asked structurally of the body - a return, an if/else or a match with a default arm whose every branch ends - rather than handing back whatever the result slot held. The game, the lab and every package example pass all three; the lab had one harmless shadow, and the game's split screens had two real bugs of the kind. examples/rejected/ holds the four refusals, checked by the test runner's new reject_case. Seed regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c57eafcacc
commit
dc75a5a5ab
13 changed files with 46652 additions and 44931 deletions
|
|
@ -20,6 +20,17 @@ function feat_case(path: pointer, keys: pointer, exp: pointer, label: pointer) -
|
|||
if (got == exp) { ok(label) } else { bad2(label, `got [{got}] want [{exp}]`) }
|
||||
}
|
||||
|
||||
# an example the compiler must REFUSE, with `want` in the message
|
||||
function reject_case(path: pointer, want: pointer, label: pointer) -> void {
|
||||
let ll = `{tmp_dir()}/rj_{flat(path)}.ll`
|
||||
if shq(`bin/ludicc examples/{path}.ludic --emit-llvm -o {ll} 2>{tmp_dir()}/rj.err`) {
|
||||
bad2(label, "compiled, and should have been refused")
|
||||
return
|
||||
}
|
||||
let got = capture_line(`grep -i error {tmp_dir()}/rj.err | head -1`)
|
||||
if s_contains(got, want) { ok(label) } else { bad2(label, `refused, but said [{got}]`) }
|
||||
}
|
||||
|
||||
# a feature example that consumes an in-repo controller package (packages/): same
|
||||
# as feat_case, but the compiler is pointed at packages/ via LUDIC_MODULES so a
|
||||
# `import "ludic.foo/bar.ludic"` resolves, and LUDIC_HOME anchors the engine
|
||||
|
|
@ -689,6 +700,10 @@ function cmd_dev_test() -> int {
|
|||
feat_case("events/promote", "", "100 101 200 201", "promote.ludic (@Public -> model_<M>_spawn/despawn events)")
|
||||
feat_case("events/cancel", "", "0 1 92", "cancel.ludic (cancellable event + cancel + emit-as-expression)")
|
||||
feat_case("events/answer", "", "10 20 0", "answer.ludic (a listener's return ends that listener, not the dispatch)")
|
||||
reject_case("rejected/duplicate_const", "'KEY_LEFT' is defined twice", "a const defined twice is refused, naming both places")
|
||||
reject_case("rejected/duplicate_property", "'Look' is defined twice", "a property defined twice is refused")
|
||||
reject_case("rejected/redeclared_local", "'v0' is declared twice in this block", "a local declared twice in one block is refused")
|
||||
reject_case("rejected/missing_return", "can reach its end without returning", "a function that can run off its end without its result is refused")
|
||||
|
||||
# EV2 the world table: the mod reflection ABI, callable from Ludic by name.
|
||||
net_case("ecs/world_get", "50 1 7")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue