Phase 4b: enum types name magic-int value spaces
enum Name { A, B, C } declares named int constants; Enum.Variant is a
compile-time int (index by declaration order), usable in match patterns,
comparisons, setreg. New: parse_enum + dispatch, enum_ordinal resolver
(emit_core), Enum.Variant handling (emit_expr). Scoped to a naming layer over
int (no enum-typed variables), so register/save semantics are untouched.
combat.ludic battle menus dispatch on KnightAct/MageAct instead of 0..3;
chronorift golden byte-identical. Editor vocab (ludic_syntax.h, JetBrains,
TextMate, emacs) gained enum, dropped retired edge/export/pure decl keywords;
check-vocabulary + test-tools 28/0. when/if kept by choice; bitwise stays
functions. Reseeded, fixpoint holds, test.sh 14/14.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
fd0b890acc
commit
f87964c5f7
14 changed files with 3021 additions and 2672 deletions
20
LANGUAGE.md
20
LANGUAGE.md
|
|
@ -392,6 +392,26 @@ is still accepted when a state needs a specific value.) A `machine <reg>` reads
|
||||||
value>)`. Both lower to plain branches (and `match` runs on the native LLVM
|
value>)`. Both lower to plain branches (and `match` runs on the native LLVM
|
||||||
backend too).
|
backend too).
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
`enum` names a set of related integer values so a magic-number space — a menu
|
||||||
|
selection, a mode, a machine state — reads as names instead of literals:
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
# doc-check: skip — composite: a declaration plus its uses
|
||||||
|
enum Action { Attack, Guard, Item, Flee } # Attack = 0, Guard = 1, …
|
||||||
|
|
||||||
|
match reg(R_CUR) { Action.Attack => attack() Action.Guard => guard() _ => wait() }
|
||||||
|
if reg(R_MODE) == Mode.Battle { … }
|
||||||
|
```
|
||||||
|
|
||||||
|
A variant is a **compile-time `int`** accessed as `Enum.Variant` (`Action.Guard`
|
||||||
|
is `1`), numbered from `0` by declaration order, so it works anywhere an int does
|
||||||
|
— `match` patterns, comparisons, `setreg`. Enums are a naming layer over `int`:
|
||||||
|
there is no distinct enum runtime type yet, so an enum value lives in an ordinary
|
||||||
|
`int` or register (and is saved with it). See `examples/chronorift/combat.ludic`,
|
||||||
|
whose battle menus dispatch on `KnightAct`/`MageAct` instead of `0..3`.
|
||||||
|
|
||||||
## Expressions
|
## Expressions
|
||||||
|
|
||||||
Precedence: `or → and → compar(< <= > >= == !=) → + - → * / % → unary(- not) →
|
Precedence: `or → and → compar(< <= > >= == !=) → + - → * / % → unary(- not) →
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,9 @@ Verified against the self-hosted compiler ([selfhost/parse.ludic](selfhost/parse
|
||||||
11. Operators are words (`and`/`or`/`not`), symbols (`==`/`<=`), *and* functions
|
11. Operators are words (`and`/`or`/`not`), symbols (`==`/`<=`), *and* functions
|
||||||
(`band`/`shl`) at once.
|
(`band`/`shl`) at once.
|
||||||
12. Three overlapping control families — `if`/`when`, `match`, `machine`/`become`
|
12. Three overlapping control families — `if`/`when`, `match`, `machine`/`become`
|
||||||
— and `enter` reuses `become`'s AST node ([parse.ludic:176-177](selfhost/parse.ludic)).
|
— and `enter` reuses `become`'s AST node ([parse.ludic:176-177](selfhost/parse.ludic)). *(Phase 4: `if`/`when` kept by choice; magic-int dispatch resolved)*
|
||||||
13. Typed components/structs exist, but real state lives in 64 untyped int
|
13. Typed components/structs exist, but real state lives in 64 untyped int
|
||||||
registers (`reg`/`setreg`), so `machine`/`match` dispatch on magic numbers.
|
registers (`reg`/`setreg`), so `machine`/`match` dispatch on magic numbers. *(✅ Phase 4: auto-numbered states + `enum` name the values)*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -285,13 +285,36 @@ migration (IR byte-identical / goldens identical) → reseed → docs. The migra
|
||||||
tools ([migrate_separators.c](tools/ludic-tools/migrate_separators.c),
|
tools ([migrate_separators.c](tools/ludic-tools/migrate_separators.c),
|
||||||
[migrate_records.c](tools/ludic-tools/migrate_records.c)) are the reusable spine.
|
[migrate_records.c](tools/ludic-tools/migrate_records.c)) are the reusable spine.
|
||||||
|
|
||||||
### Phase 4 — Control-flow & operator consolidation (largest)
|
### Phase 4 — Control-flow & state consolidation
|
||||||
- Remove `when`; confirm `if` covers all uses in the corpus.
|
|
||||||
- Introduce `enum` + typed `machine`/state; migrate `combat.ludic`'s R_PHASE
|
**4a — machine states auto-number ✅ DONE.** `state KnightMenu = 0 { }` →
|
||||||
machine and the register-driven `match reg(…)` sites.
|
`state KnightMenu { }`; a state's value is its declaration index (an explicit
|
||||||
- Decide the bitwise story: keep `band/shl/…` as functions (document as a
|
`= expr` still works). Removes the magic constants from state machines
|
||||||
deliberate "one spelling" choice) or promote to operators — pick one and state
|
([parse.ludic](selfhost/parse.ludic)). combat.ludic migrated; chronorift golden
|
||||||
it, don't leave it implicit.
|
byte-identical.
|
||||||
|
|
||||||
|
**4b — `enum` types ✅ DONE.** `enum Action { Attack, Guard, Item, Flee }` declares
|
||||||
|
named `int` constants; a variant is a compile-time int accessed as `Action.Guard`
|
||||||
|
(= 1), numbered by order. Parser `parse_enum` + dispatch, `enum_ordinal` resolver
|
||||||
|
in [emit_core.ludic](selfhost/emit_core.ludic), and `Enum.Variant` handling in
|
||||||
|
[emit_expr.ludic](selfhost/emit_expr.ludic). combat.ludic's battle menus now
|
||||||
|
dispatch on `KnightAct`/`MageAct` instead of `0..3`; chronorift golden
|
||||||
|
byte-identical. Editor vocab (`ludic_syntax.h`, JetBrains, TextMate, emacs) gained
|
||||||
|
`enum` and lost the retired `edge`/`export`/`pure` decl keywords; check-vocabulary
|
||||||
|
+ test-tools green. **Scoped:** enums are a naming layer over `int` (no distinct
|
||||||
|
runtime type / enum-typed variables yet) — that keeps register/save semantics
|
||||||
|
untouched, which the "enum var replaces the register" vision would have to solve.
|
||||||
|
|
||||||
|
**`when` vs `if` — kept both (decision).** `when` stays as the `if`-without-else
|
||||||
|
spelling: it is not incoherent so much as a readability signal ("no else here"),
|
||||||
|
it is documented and highlighted, and it is a pure alias with no semantic overlap
|
||||||
|
to untangle. The real target of finding #12 — dispatch on magic integers — is
|
||||||
|
addressed by 4a/4b, not by collapsing `if`/`when`.
|
||||||
|
|
||||||
|
**Bitwise operators — kept as functions (decision).** `band`/`bor`/`bxor`/`bnot`/
|
||||||
|
`shl`/`shr` stay functions, documented as the deliberate "one spelling, symbols
|
||||||
|
stay free" choice (LANGUAGE.md §Expressions already states this). Promoting them
|
||||||
|
to operators would re-introduce the symbol soup the current design avoids.
|
||||||
|
|
||||||
### Phase 5 — Single source of truth for keywords/grammar
|
### Phase 5 — Single source of truth for keywords/grammar
|
||||||
- Generate every editor plugin keyword list, `ludic_syntax.h`, and the LSP's
|
- Generate every editor plugin keyword list, `ludic_syntax.h`, and the LSP's
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@
|
||||||
# The battle is a state machine over R_PHASE. `machine` dispatches on the
|
# The battle is a state machine over R_PHASE. `machine` dispatches on the
|
||||||
# register and `become` transitions to a named state — no more `if ph == N`
|
# register and `become` transitions to a named state — no more `if ph == N`
|
||||||
# ladders. Knight turn (0,1), Mage turn (2,3), Enemy turn (4).
|
# ladders. Knight turn (0,1), Mage turn (2,3), Enemy turn (4).
|
||||||
|
|
||||||
|
# Menu selections, named instead of matched on bare integers (R_CUR holds one).
|
||||||
|
enum KnightAct { Attack, Guard, Item, Flee }
|
||||||
|
enum MageAct { Attack, Heal, Guard }
|
||||||
|
|
||||||
system Battle phase Update {
|
system Battle phase Update {
|
||||||
if reg(R_MODE) == 1 {
|
if reg(R_MODE) == 1 {
|
||||||
let k = key()
|
let k = key()
|
||||||
|
|
@ -20,20 +25,20 @@ system Battle phase Update {
|
||||||
if k == 's' { setreg(R_CUR, min(3, reg(R_CUR) + 1)) }
|
if k == 's' { setreg(R_CUR, min(3, reg(R_CUR) + 1)) }
|
||||||
if is_confirm(k) {
|
if is_confirm(k) {
|
||||||
match reg(R_CUR) {
|
match reg(R_CUR) {
|
||||||
0 => {
|
KnightAct.Attack => {
|
||||||
let d = max(1, st.atk - e.def + rng_range(0, 4))
|
let d = max(1, st.atk - e.def + rng_range(0, 4))
|
||||||
e.hp = e.hp - d
|
e.hp = e.hp - d
|
||||||
setreg(R_PDMG, d)
|
setreg(R_PDMG, d)
|
||||||
}
|
}
|
||||||
1 => { st.guard = 1; setreg(R_PDMG, 0) }
|
KnightAct.Guard => { st.guard = 1; setreg(R_PDMG, 0) }
|
||||||
2 => {
|
KnightAct.Item => {
|
||||||
if reg(R_POTION) > 0 {
|
if reg(R_POTION) > 0 {
|
||||||
setreg(R_POTION, reg(R_POTION) - 1)
|
setreg(R_POTION, reg(R_POTION) - 1)
|
||||||
st.hp = min(st.maxhp, st.hp + 24)
|
st.hp = min(st.maxhp, st.hp + 24)
|
||||||
setreg(R_PDMG, 24)
|
setreg(R_PDMG, 24)
|
||||||
} else { setreg(R_PDMG, 0) }
|
} else { setreg(R_PDMG, 0) }
|
||||||
}
|
}
|
||||||
3 => {
|
KnightAct.Flee => {
|
||||||
if rng_chance(50) { e.hp = 0; setreg(R_MODE, 0) }
|
if rng_chance(50) { e.hp = 0; setreg(R_MODE, 0) }
|
||||||
else { setreg(R_PDMG, 0) }
|
else { setreg(R_PDMG, 0) }
|
||||||
}
|
}
|
||||||
|
|
@ -71,12 +76,12 @@ system Battle phase Update {
|
||||||
if k == 'k' { setreg(R_CUR, min(2, reg(R_CUR) + 1)) }
|
if k == 'k' { setreg(R_CUR, min(2, reg(R_CUR) + 1)) }
|
||||||
if k == 'j' {
|
if k == 'j' {
|
||||||
match reg(R_CUR) {
|
match reg(R_CUR) {
|
||||||
0 => {
|
MageAct.Attack => {
|
||||||
let d = max(1, st.atk + 5 - e.def + rng_range(0, 5))
|
let d = max(1, st.atk + 5 - e.def + rng_range(0, 5))
|
||||||
e.hp = e.hp - d
|
e.hp = e.hp - d
|
||||||
setreg(R_PDMG, d)
|
setreg(R_PDMG, d)
|
||||||
}
|
}
|
||||||
1 => {
|
MageAct.Heal => {
|
||||||
if st.mp >= 4 {
|
if st.mp >= 4 {
|
||||||
st.mp = st.mp - 4
|
st.mp = st.mp - 4
|
||||||
for (t2, p2) in query [Stats, Party] {
|
for (t2, p2) in query [Stats, Party] {
|
||||||
|
|
@ -85,7 +90,7 @@ system Battle phase Update {
|
||||||
setreg(R_PDMG, 16)
|
setreg(R_PDMG, 16)
|
||||||
} else { setreg(R_PDMG, 0) }
|
} else { setreg(R_PDMG, 0) }
|
||||||
}
|
}
|
||||||
2 => { st.guard = 1; setreg(R_PDMG, 0) }
|
MageAct.Guard => { st.guard = 1; setreg(R_PDMG, 0) }
|
||||||
}
|
}
|
||||||
become MageResolve
|
become MageResolve
|
||||||
setreg(R_CUR, 0)
|
setreg(R_CUR, 0)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ const N_SYS: int = 9
|
||||||
const N_ARCH: int = 10
|
const N_ARCH: int = 10
|
||||||
const N_EXTERN: int = 11
|
const N_EXTERN: int = 11
|
||||||
const N_UI: int = 12
|
const N_UI: int = 12
|
||||||
|
const N_ENUM: int = 27 # enum Name { A, B, ... } — named int constants
|
||||||
# statements
|
# statements
|
||||||
const S_LET: int = 10
|
const S_LET: int = 10
|
||||||
const S_ASSIGN: int = 11
|
const S_ASSIGN: int = 11
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,21 @@ fn find_global(name: ptr) -> Node {
|
||||||
}
|
}
|
||||||
return ptr_null()
|
return ptr_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# `Enum.Variant` -> the variant's ordinal (its index), or -1 if `ename` names no
|
||||||
|
# enum with that variant. Enum names live in `prog` like any other declaration.
|
||||||
|
fn enum_ordinal(ename: ptr, vname: ptr) -> int {
|
||||||
|
let i = 0
|
||||||
|
while i < len(prog) {
|
||||||
|
let d = prog[i]
|
||||||
|
if d.kind == N_ENUM and streq(d.s, ename) {
|
||||||
|
let j = 0
|
||||||
|
while j < len(d.kids) { if streq(d.kids[j].s, vname) { return j }; j = j + 1 }
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return 0 - 1
|
||||||
|
}
|
||||||
fn find_fn(name: ptr) -> Node {
|
fn find_fn(name: ptr) -> Node {
|
||||||
let i = 0
|
let i = 0
|
||||||
while i < len(prog) { let d = prog[i]; if d.kind == N_FN and streq(d.s, name) { return d }; i = i + 1 }
|
while i < len(prog) { let d = prog[i]; if d.kind == N_FN and streq(d.s, name) { return d }; i = i + 1 }
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,13 @@ fn emit_expr(e: Node) -> Val {
|
||||||
if is_ui_ident(e.s) { return val(itoa(ui_index_of(e.s)), "int") }
|
if is_ui_ident(e.s) { return val(itoa(ui_index_of(e.s)), "int") }
|
||||||
perr(sconcat("unknown identifier ", e.s))
|
perr(sconcat("unknown identifier ", e.s))
|
||||||
}
|
}
|
||||||
if e.kind == E_MEMBER { let a = emit_member_addr(e); return emit_load_at(a, g_addr_ty) }
|
if e.kind == E_MEMBER {
|
||||||
|
if e.a.kind == E_ID { # `Enum.Variant` -> its ordinal, a compile-time int
|
||||||
|
let ord = enum_ordinal(e.a.s, e.s)
|
||||||
|
if ord >= 0 { return val(itoa(ord), "int") }
|
||||||
|
}
|
||||||
|
let a = emit_member_addr(e); return emit_load_at(a, g_addr_ty)
|
||||||
|
}
|
||||||
if e.kind == E_INDEX { let a = emit_index_addr(e); return emit_load_at(a, g_addr_ty) }
|
if e.kind == E_INDEX { let a = emit_index_addr(e); return emit_load_at(a, g_addr_ty) }
|
||||||
if e.kind == E_CALL { return emit_call(e) }
|
if e.kind == E_CALL { return emit_call(e) }
|
||||||
if e.kind == E_BIN { return emit_bin(e) }
|
if e.kind == E_BIN { return emit_bin(e) }
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -296,6 +296,7 @@ fn parse_one_decl() -> void {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if is_id("struct") { push(prog, parse_struct()); return }
|
if is_id("struct") { push(prog, parse_struct()); return }
|
||||||
|
if is_id("enum") { push(prog, parse_enum()); return }
|
||||||
if is_id("component") { push(prog, parse_component()); return }
|
if is_id("component") { push(prog, parse_component()); return }
|
||||||
if is_id("archetype") { push(prog, parse_archetype()); return }
|
if is_id("archetype") { push(prog, parse_archetype()); return }
|
||||||
if is_id("system") { push(prog, parse_system()); return }
|
if is_id("system") { push(prog, parse_system()); return }
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,17 @@ fn parse_spawn() -> Node {
|
||||||
eat_op("}"); return n
|
eat_op("}"); return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# enum Name { A, B, C } — named int constants; a variant's value is its index.
|
||||||
|
# Accessed as `Name.A` (a compile-time int), so it names magic-int value spaces
|
||||||
|
# (state ids, menu selections, mode registers) without a runtime cost.
|
||||||
|
fn parse_enum() -> Node {
|
||||||
|
pi = pi + 1; let n = node(N_ENUM); n.s = eat_id(); skipnl(); eat_op("{")
|
||||||
|
while true { skipnl(); if is_op("}") { break }
|
||||||
|
let v = node(E_ID); v.s = eat_id(); push(n.kids, v)
|
||||||
|
if is_op(",") { pi = pi + 1 }; skipnl() }
|
||||||
|
eat_op("}"); return n
|
||||||
|
}
|
||||||
|
|
||||||
# archetype Name { CompA, CompB } — a named entity kind (bundle of components)
|
# archetype Name { CompA, CompB } — a named entity kind (bundle of components)
|
||||||
fn parse_archetype() -> Node {
|
fn parse_archetype() -> Node {
|
||||||
pi = pi + 1; let n = node(N_ARCH); n.s = eat_id(); skipnl(); eat_op("{")
|
pi = pi + 1; let n = node(N_ARCH); n.s = eat_id(); skipnl(); eat_op("{")
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(defconst ludic--declaration-keywords
|
(defconst ludic--declaration-keywords
|
||||||
'("game" "module" "import" "component" "archetype" "ui" "scene" "layer"
|
'("game" "module" "import" "component" "struct" "archetype" "enum" "ui" "scene" "layer"
|
||||||
"const" "var" "export" "fn" "pure" "extern" "system" "edge" "start" "on"))
|
"const" "var" "fn" "extern" "system" "start" "on"))
|
||||||
|
|
||||||
(defconst ludic--clause-keywords
|
(defconst ludic--clause-keywords
|
||||||
'("phase" "query" "reads" "writes" "needs" "uses"
|
'("phase" "query" "reads" "writes" "needs" "uses"
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ object LudicTokens {
|
||||||
*/
|
*/
|
||||||
object LudicVocabulary {
|
object LudicVocabulary {
|
||||||
val DECL = setOf(
|
val DECL = setOf(
|
||||||
"game", "module", "import", "component", "struct", "archetype", "ui", "scene", "layer",
|
"game", "module", "import", "component", "struct", "archetype", "enum", "ui", "scene", "layer",
|
||||||
"const", "var", "export", "fn", "pure", "extern", "system", "edge", "start", "on"
|
"const", "var", "fn", "extern", "system", "start", "on"
|
||||||
)
|
)
|
||||||
val CLAUSE = setOf(
|
val CLAUSE = setOf(
|
||||||
"phase", "query", "reads", "writes", "needs", "uses",
|
"phase", "query", "reads", "writes", "needs", "uses",
|
||||||
|
|
|
||||||
|
|
@ -174,8 +174,8 @@
|
||||||
{ "name": "keyword.control.ludic", "match": "\\b(if|else|when|while|for|in|match|machine|become|enter|return|spawn|despawn|where|break|continue|new)\\b" },
|
{ "name": "keyword.control.ludic", "match": "\\b(if|else|when|while|for|in|match|machine|become|enter|return|spawn|despawn|where|break|continue|new)\\b" },
|
||||||
{ "name": "keyword.operator.logical.ludic", "match": "\\b(and|or|not)\\b" },
|
{ "name": "keyword.operator.logical.ludic", "match": "\\b(and|or|not)\\b" },
|
||||||
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|reads|writes|needs|uses|requires|ensures|invariant|effects|on)\\b" },
|
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|reads|writes|needs|uses|requires|ensures|invariant|effects|on)\\b" },
|
||||||
{ "name": "keyword.other.ludic", "match": "\\b(import|export|pure|extern|start|edge)\\b" },
|
{ "name": "keyword.other.ludic", "match": "\\b(import|extern|start)\\b" },
|
||||||
{ "name": "storage.type.ludic", "match": "\\b(game|module|component|archetype|ui|scene|layer|const|var|let|fn|system|state)\\b" },
|
{ "name": "storage.type.ludic", "match": "\\b(game|module|component|struct|archetype|enum|ui|scene|layer|const|var|let|fn|system|state)\\b" },
|
||||||
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
|
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
|
||||||
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false)\\b" },
|
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false)\\b" },
|
||||||
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
|
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
|
||||||
|
|
|
||||||
|
|
@ -174,8 +174,8 @@
|
||||||
{ "name": "keyword.control.ludic", "match": "\\b(if|else|when|while|for|in|match|machine|become|enter|return|spawn|despawn|where|break|continue|new)\\b" },
|
{ "name": "keyword.control.ludic", "match": "\\b(if|else|when|while|for|in|match|machine|become|enter|return|spawn|despawn|where|break|continue|new)\\b" },
|
||||||
{ "name": "keyword.operator.logical.ludic", "match": "\\b(and|or|not)\\b" },
|
{ "name": "keyword.operator.logical.ludic", "match": "\\b(and|or|not)\\b" },
|
||||||
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|reads|writes|needs|uses|requires|ensures|invariant|effects|on)\\b" },
|
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|reads|writes|needs|uses|requires|ensures|invariant|effects|on)\\b" },
|
||||||
{ "name": "keyword.other.ludic", "match": "\\b(import|export|pure|extern|start|edge)\\b" },
|
{ "name": "keyword.other.ludic", "match": "\\b(import|extern|start)\\b" },
|
||||||
{ "name": "storage.type.ludic", "match": "\\b(game|module|component|archetype|ui|scene|layer|const|var|let|fn|system|state)\\b" },
|
{ "name": "storage.type.ludic", "match": "\\b(game|module|component|struct|archetype|enum|ui|scene|layer|const|var|let|fn|system|state)\\b" },
|
||||||
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
|
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
|
||||||
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false)\\b" },
|
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false)\\b" },
|
||||||
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
|
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@ typedef struct {
|
||||||
* mirror the compiler's parser: anything parse_decl() dispatches on is a
|
* mirror the compiler's parser: anything parse_decl() dispatches on is a
|
||||||
* declaration keyword, anything stmt() dispatches on is a statement keyword. */
|
* declaration keyword, anything stmt() dispatches on is a statement keyword. */
|
||||||
static const char* LUDIC_KW_DECL[] = {
|
static const char* LUDIC_KW_DECL[] = {
|
||||||
"game","module","import","component","struct","archetype","ui","scene","layer",
|
"game","module","import","component","struct","archetype","enum","ui","scene","layer",
|
||||||
"const","var","export","fn","pure","extern","system","edge","start","on", 0
|
"const","var","fn","extern","system","start","on", 0
|
||||||
};
|
};
|
||||||
static const char* LUDIC_KW_CLAUSE[] = {
|
static const char* LUDIC_KW_CLAUSE[] = {
|
||||||
"phase","query","reads","writes","needs","uses",
|
"phase","query","reads","writes","needs","uses",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue