Merge lang/i18nkeys (27024de) into lang/syntax: the vocabulary gains the text keys - the Key type, @TextKey (a registry's), the k"..." and kn"..." literals - and @Text's doc; docs/language pages for Key and @TextKey; ludic-dev syntax rewrote the grammars; reseeded, bootstrap-cfree fixpoint holds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
9f8aec3bc1
63 changed files with 145288 additions and 114562 deletions
|
|
@ -26,7 +26,7 @@
|
|||
"continue" "new" "spawn" "despawn" "enable" "disable" "attach" "detach" "emit" "cancel"
|
||||
"dispatch" "try" "and" "or" "not"))
|
||||
(defconst ludic--types
|
||||
'("int" "long" "float" "double" "fixed" "bool" "byte" "string" "pointer" "void" "entity"
|
||||
'("int" "long" "float" "double" "fixed" "bool" "byte" "string" "Key" "pointer" "void" "entity"
|
||||
"countdown" "words" "floats" "doubles" "fixeds" "pointers" "Vector" "IVec2" "Rect"))
|
||||
(defconst ludic--phases
|
||||
'("Start" "Input" "FixedUpdate" "Update" "LateUpdate" "Render" "Overlay"))
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@ object LudicVocabulary {
|
|||
"emit", "cancel", "dispatch", "try", "and", "or", "not"
|
||||
)
|
||||
val PRIMITIVES = setOf(
|
||||
"int", "long", "float", "double", "fixed", "bool", "byte", "string", "pointer", "void",
|
||||
"entity", "countdown", "words", "floats", "doubles", "fixeds", "pointers", "Vector",
|
||||
"int", "long", "float", "double", "fixed", "bool", "byte", "string", "Key", "pointer",
|
||||
"void", "entity", "countdown", "words", "floats", "doubles", "fixeds", "pointers", "Vector",
|
||||
"IVec2", "Rect"
|
||||
)
|
||||
val PHASES = setOf(
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
{ "name": "keyword.operator.logical.ludic", "comment": "ludic-dev syntax: operator", "match": "\\b(and|or|not)\\b" },
|
||||
{ "name": "keyword.other.clause.ludic", "comment": "ludic-dev syntax: modifier", "match": "\\b(export|internal|friend|uses|open|of|as|from|numbers|unsafe|mut|phase|query|on|cancellable|public|layer|start|shows|lasts|then|loads|system)\\b" },
|
||||
{ "name": "storage.type.ludic", "comment": "ludic-dev syntax: declaration", "match": "\\b(program|module|import|property|state|event|action|reducer|enum|model|prefab|scene|handler|ui|namespace|alias|port|bind|registry|def|component|view|prop|const|var|function|fn|extern|entry|test)\\b" },
|
||||
{ "name": "support.type.primitive.ludic", "comment": "ludic-dev syntax: types", "match": "\\b(int|long|float|double|fixed|bool|byte|string|pointer|void|entity|countdown|words|floats|doubles|fixeds|pointers|Vector|IVec2|Rect)\\b" },
|
||||
{ "name": "support.type.primitive.ludic", "comment": "ludic-dev syntax: types", "match": "\\b(int|long|float|double|fixed|bool|byte|string|Key|pointer|void|entity|countdown|words|floats|doubles|fixeds|pointers|Vector|IVec2|Rect)\\b" },
|
||||
{ "name": "constant.language.boolean.ludic", "comment": "ludic-dev syntax: constant", "match": "\\b(true|false|null)\\b" },
|
||||
{ "name": "constant.language.phase.ludic", "comment": "ludic-dev syntax: phases", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render|Overlay)\\b" }
|
||||
]
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
{ "name": "keyword.operator.logical.ludic", "comment": "ludic-dev syntax: operator", "match": "\\b(and|or|not)\\b" },
|
||||
{ "name": "keyword.other.clause.ludic", "comment": "ludic-dev syntax: modifier", "match": "\\b(export|internal|friend|uses|open|of|as|from|numbers|unsafe|mut|phase|query|on|cancellable|public|layer|start|shows|lasts|then|loads|system)\\b" },
|
||||
{ "name": "storage.type.ludic", "comment": "ludic-dev syntax: declaration", "match": "\\b(program|module|import|property|state|event|action|reducer|enum|model|prefab|scene|handler|ui|namespace|alias|port|bind|registry|def|component|view|prop|const|var|function|fn|extern|entry|test)\\b" },
|
||||
{ "name": "support.type.primitive.ludic", "comment": "ludic-dev syntax: types", "match": "\\b(int|long|float|double|fixed|bool|byte|string|pointer|void|entity|countdown|words|floats|doubles|fixeds|pointers|Vector|IVec2|Rect)\\b" },
|
||||
{ "name": "support.type.primitive.ludic", "comment": "ludic-dev syntax: types", "match": "\\b(int|long|float|double|fixed|bool|byte|string|Key|pointer|void|entity|countdown|words|floats|doubles|fixeds|pointers|Vector|IVec2|Rect)\\b" },
|
||||
{ "name": "constant.language.boolean.ludic", "comment": "ludic-dev syntax: constant", "match": "\\b(true|false|null)\\b" },
|
||||
{ "name": "constant.language.phase.ludic", "comment": "ludic-dev syntax: phases", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render|Overlay)\\b" }
|
||||
]
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
# Both leave out the ROOTS - a function that reads fn values out of a table (a dispatcher), which reaches
|
||||
# every state by definition - and count every other function without going through one, so the frame
|
||||
# and the boot count their own work; the roots are listed on a line of their own.
|
||||
# english_left: text still written in English where a key belongs (phase 26, the compiler's i18n
|
||||
# checks) - a template's words, a text attribute, a @Text row's English in the data; 0 without a
|
||||
# `lang` line in package.ludic.
|
||||
var dp_mods: []pointer = null
|
||||
var dp_pkg: []int = null
|
||||
var dp_uses: []pointer = null
|
||||
|
|
@ -71,6 +74,7 @@ var dp_rdrops: []pointer = new []pointer # 25.5e: `rdrop <kind> <file:l
|
|||
var dp_resource_drops: int = 0
|
||||
var dp_oleaks: []pointer = new []pointer # 25.5e: `oleak <Type.field> <file:line> <fn> <released>`
|
||||
var dp_owned_leaks: int = 0
|
||||
var dp_english_left: int = 0 # phase 26: a template's words or a @Text row's English, not yet keys
|
||||
var dp_wreach_n: int = 0
|
||||
var dp_wreach_at: pointer = ""
|
||||
|
||||
|
|
@ -115,6 +119,7 @@ function dp_load(path: pointer) -> bool {
|
|||
dp_fbirths = new []pointer; dp_birth_leaks = 0
|
||||
dp_rdrops = new []pointer; dp_resource_drops = 0
|
||||
dp_oleaks = new []pointer; dp_owned_leaks = 0
|
||||
dp_english_left = 0
|
||||
dp_writes = new []pointer; dp_aliases = new []pointer; dp_wowner = new []pointer; dp_wname = new []pointer; dp_wfrom = new []pointer
|
||||
let lines = split_lines(text)
|
||||
for i in 0 .. len(lines) {
|
||||
|
|
@ -146,6 +151,7 @@ function dp_load(path: pointer) -> bool {
|
|||
if len(w) == 2 and w[0] == "resource_drops" { dp_resource_drops = s_to_int(w[1]) }
|
||||
if len(w) >= 5 and w[0] == "oleak" { push(dp_oleaks, lines[i]) }
|
||||
if len(w) == 2 and w[0] == "owned_leaks" { dp_owned_leaks = s_to_int(w[1]) }
|
||||
if len(w) == 2 and w[0] == "english_left" { dp_english_left = s_to_int(w[1]) }
|
||||
if len(w) >= 5 and w[0] == "width" and w[4] == "0" and s_to_int(w[1]) > dp_wide_n {
|
||||
dp_wide_n = s_to_int(w[1])
|
||||
dp_wide_at = `{dp_fn_name(w[2])} ({w[3]})`
|
||||
|
|
@ -365,7 +371,7 @@ var dp_vals: []int = null
|
|||
var dp_cycle: []int = null
|
||||
var dp_cycle_all: []int = null # the same with the layers' own edges counted
|
||||
function dp_numbers() -> void {
|
||||
dp_names = ["modules", "dependencies", "largest_cycle", "cross_writes", "globals_written_from_outside", "widest_function", "widest_reach", "widest_write_reach", "frame_allocs", "frame_keeps", "birth_leaks", "resource_drops", "owned_leaks"]
|
||||
dp_names = ["modules", "dependencies", "largest_cycle", "cross_writes", "globals_written_from_outside", "widest_function", "widest_reach", "widest_write_reach", "frame_allocs", "frame_keeps", "birth_leaks", "resource_drops", "owned_leaks", "english_left"]
|
||||
var mods = 0
|
||||
for k in 0 .. len(dp_mods) { if dp_own(k) { mods += 1 } }
|
||||
var deps = 0
|
||||
|
|
@ -387,7 +393,7 @@ function dp_numbers() -> void {
|
|||
}
|
||||
}
|
||||
dp_reach_best()
|
||||
dp_vals = [mods, deps, len(dp_cycle), writes, len(seen), dp_wide_n, dp_reach_n, dp_wreach_n, dp_frame_allocs, dp_frame_keeps, dp_birth_leaks, dp_resource_drops, dp_owned_leaks]
|
||||
dp_vals = [mods, deps, len(dp_cycle), writes, len(seen), dp_wide_n, dp_reach_n, dp_wreach_n, dp_frame_allocs, dp_frame_keeps, dp_birth_leaks, dp_resource_drops, dp_owned_leaks, dp_english_left]
|
||||
}
|
||||
|
||||
# ---- the command --------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ function selfhost_frags() -> []pointer {
|
|||
push(f, "selfhost/frontend/component_gen.ludic")
|
||||
push(f, "selfhost/frontend/component_gen2.ludic")
|
||||
push(f, "selfhost/frontend/component_class.ludic")
|
||||
push(f, "selfhost/frontend/i18n_po.ludic")
|
||||
push(f, "selfhost/frontend/i18n.ludic")
|
||||
push(f, "selfhost/frontend/i18n_xml.ludic")
|
||||
push(f, "selfhost/backend/emit_core.ludic")
|
||||
push(f, "selfhost/backend/emit_fence.ludic")
|
||||
push(f, "selfhost/backend/emit_fence_ir.ludic")
|
||||
|
|
@ -97,6 +100,7 @@ function selfhost_frags() -> []pointer {
|
|||
push(f, "selfhost/backend/emit_schema.ludic")
|
||||
push(f, "selfhost/backend/emit_schema_ui.ludic")
|
||||
push(f, "selfhost/backend/emit_schema_hash.ludic")
|
||||
push(f, "selfhost/backend/emit_schema_lang.ludic")
|
||||
push(f, "selfhost/check/check_state.ludic")
|
||||
push(f, "selfhost/check/check_index.ludic")
|
||||
push(f, "selfhost/check/check_types.ludic")
|
||||
|
|
|
|||
|
|
@ -74,6 +74,18 @@ function permap_check_case() -> void {
|
|||
if not s_contains(capture(`cat {js}`), "the unit is spelled") or not s_contains(capture(`cat {js}`), "deg") { bad2(lbl, capture_line(`head -3 {js}`)); return }
|
||||
ok(lbl)
|
||||
}
|
||||
# phase 26: a template checks clean against en.po, its one line of words a warning, and ludic deps
|
||||
# counts it as english_left
|
||||
function i18n_check_case() -> void {
|
||||
let lbl = "--check: a component's template against en.po - clean, \"Paused\" English left (a warning), english_left 1 in ludic deps"
|
||||
let js = `{tmp_dir()}/dj_i18n.json`
|
||||
if not shq(`bin/ludicc examples/lang/i18n/i18n_ui.ludic --check --diagnostics=json > {js} 2>/dev/null`) { bad2(lbl, capture_line(`head -3 {js}`)); return }
|
||||
let got = capture(`cat {js}`)
|
||||
if not s_contains(got, "English left: a template's words") or not s_contains(got, "Paused") { bad2(lbl, capture_line(`head -3 {js}`)); return }
|
||||
let dp = capture(`bin/ludic deps examples/lang/i18n/i18n_ui.ludic 2>&1`)
|
||||
if not s_contains(dp, "english_left: 1") { bad2(lbl, `ludic deps said [{s_trim(dp)}]`); return }
|
||||
ok(lbl)
|
||||
}
|
||||
# --check --diagnostics=json: refused, with exactly `n` ERRORS on stdout (warnings beside them are not
|
||||
# counted), one of the diagnostics holding `want`
|
||||
function diag_json_case(path: pointer, n: int, want: pointer, label: pointer) -> void {
|
||||
|
|
@ -514,7 +526,7 @@ function deps_case() -> void {
|
|||
let lbl = "ludic deps: the numbers, --check against a baseline, --dot, --writes"
|
||||
let p = "examples/modules/tangle.ludic"
|
||||
let got = capture(`bin/ludic deps {p} 2>&1`)
|
||||
let want = "modules: 3\ndependencies: 2\nlargest_cycle: 2\ncross_writes: 1\nglobals_written_from_outside: 1\nwidest_function: 1\nwidest_reach: 1\nwidest_write_reach: 1\nframe_allocs: 0\nframe_keeps: 0\nbirth_leaks: 0\nresource_drops: 0\nowned_leaks: 0\nin the largest cycle: a b\nthe widest function: a_run (examples/modules/tangle/a/index.ludic:3), 1 state\nthe widest write reach: a_run (examples/modules/tangle/a/index.ludic:3), 1 state it can come to change\n"
|
||||
let want = "modules: 3\ndependencies: 2\nlargest_cycle: 2\ncross_writes: 1\nglobals_written_from_outside: 1\nwidest_function: 1\nwidest_reach: 1\nwidest_write_reach: 1\nframe_allocs: 0\nframe_keeps: 0\nbirth_leaks: 0\nresource_drops: 0\nowned_leaks: 0\nenglish_left: 0\nin the largest cycle: a b\nthe widest function: a_run (examples/modules/tangle/a/index.ludic:3), 1 state\nthe widest write reach: a_run (examples/modules/tangle/a/index.ludic:3), 1 state it can come to change\n"
|
||||
if not (s_trim(got) == s_trim(want)) { bad2(lbl, `said [{s_trim(got)}]`); return }
|
||||
let base = `{tmp_dir()}/deps-base.txt`
|
||||
if not shq(`bin/ludic deps {p} --baseline {base} > /dev/null`) { bad2(lbl, "--baseline failed"); return }
|
||||
|
|
@ -1284,6 +1296,27 @@ function cmd_dev_test() -> int {
|
|||
diag_json_case("rejected/permap_maps/permap_maps", 6, "there is no Spots row nowhere in this map", "--check reads every map: a cross-row @Ref names a row of the same map")
|
||||
diag_json_case("rejected/permap_maps/permap_maps", 6, "the key t1 is also written at", "--check reads every map: a chunked table's key is unique across the map's chunk files, both places named")
|
||||
diag_json_case("rejected/permap_maps/permap_maps", 6, "this map has no examples/rejected/permap_maps/maps/m1/blades.png", "--check reads every map: a game-wide row's @Asset(..., map) file is in each map's directory, unless optional")
|
||||
# phase 26: text keys - k"..." / kn"..." of type Key, checked against package.ludic's lang en.po
|
||||
feat_case("lang/i18n/i18n", "", "pause.resume yes no 13 12 hud.day(3) catch.count[2](trout) items.rope.name items.lamp.name steps.gather_wood.hint", "i18n.ludic (phase 26: k\"...\" and kn\"...\" of type Key, their marker byte, == on keys, a @Text Key field filled with its derived key, @TextKey's prefix)")
|
||||
i18n_check_case()
|
||||
schema_case("lang/i18n/i18n", "{\"key\": \"items.rope.name\", \"kind\": \"data\", \"kinds\": [\"data\"], \"sites\": [\"examples/lang/i18n/data/i18n_items.lres:2:1\"], \"in_source\": true, \"english\": \"Rope\"", "--emit-schema: lang.keys - a derived key, its kind, its site and en.po's English")
|
||||
schema_case("lang/i18n/i18n", "\"unused\": [\"map.close\"]", "--emit-schema: lang.unused - en.po's keys the program uses nowhere")
|
||||
schema_case("lang/i18n/i18n", "\"missing\": [\"hud.day\"], \"fuzzy\": [\"pause.close\"], \"extra\": [\"old.title\"]", "--emit-schema: lang.languages - another .po's missing, fuzzy and extra keys against en.po")
|
||||
schema_case("lang/i18n/i18n", "{\"english\": \"Close\", \"keys\": [\"map.close\", \"pause.close\"]}", "--emit-schema: lang.split - one English under several keys")
|
||||
diag_json_case("rejected/i18n_keys/key_missing", 2, "the key pause.gone is not in examples/rejected/i18n_keys/lang/en.po", "a key literal en.po does not have is refused, at its line and column")
|
||||
diag_json_case("rejected/i18n_keys/key_missing", 2, "is a plural key, and examples/rejected/i18n_keys/lang/en.po:7 gives pause.resume no msgid_plural", "kn\"...\" is a plural key: en.po's entry has a msgid_plural")
|
||||
diag_json_case("rejected/i18n_keys/key_missing", 2, "trf: hud.day is given 2 value(s), and its English in examples/rejected/i18n_keys/lang/en.po has 1 hole(s)", "trf given more values than the key's English has holes: a warning")
|
||||
diag_json_case("rejected/i18n_keys/key_missing", 2, "shop.close has no #. description, and its English", "one English split into several keys wants a #. description on each: a warning at en.po's line")
|
||||
diag_json_case("rejected/i18n_keys/key_mixup", 3, "argument 1 of show wants a string and this is a Key", "a Key is not a string")
|
||||
diag_json_case("rejected/i18n_keys/key_mixup", 3, "argument 1 of name_of wants a Key and this is a string", "a string is not a Key")
|
||||
diag_json_case("rejected/i18n_keys/key_mixup", 3, "+ on a Key", "a Key joins no text")
|
||||
diag_json_case("rejected/i18n_keys/key_hole", 2, "a Key in a template's {hole}", "a Key in a template literal's hole is refused: it would draw its marked name, untranslated")
|
||||
diag_json_case("rejected/i18n_keys/key_rules", 3, "trn counts, so its key is a plural one", "trn wants a plural key, kn\"...\"")
|
||||
diag_json_case("rejected/i18n_keys/key_rules", 3, "a plural key needs its count", "tr / trf refuse a plural key")
|
||||
diag_json_case("rejected/i18n_keys/data_missing", 1, "things.axe.name (the derived key of a @Text field the row gives no value): the key things.axe.name is not in", "a @Text Key field a row leaves out: its derived key must be in en.po")
|
||||
diag_json_case("rejected/i18n_keys/data_missing", 1, "English left: things.axe.blurb is", "a @Text string field still holding English: a warning")
|
||||
diag_json_case("rejected/i18n_keys/t_missing", 1, "t('menu.gone'): the key menu.gone is not in", "a template's t('key') is checked against en.po")
|
||||
diag_json_case("rejected/i18n_keys/t_missing", 1, "t(): hud.day is given 0 value(s)", "a template's t('key') given fewer values than the English has holes: a warning")
|
||||
feat_case("lang/defaults", "", "a 300.0 8.0 / b 120.0 8.0 / c! 300.0 8.0 / d 300.0 2.0 / x / y", "defaults.ludic (L11: default parameters, positional then named arguments, in a generic too)")
|
||||
reject_case("rejected/default_missing", "this call to f leaves out a, which has no default", "a call leaves out only what has a default")
|
||||
reject_case("rejected/default_unknown", "f has no parameter c", "a named argument names a parameter the function has")
|
||||
|
|
|
|||
|
|
@ -216,6 +216,12 @@ program LudicFmt {
|
|||
if char_is_alpha(c) {
|
||||
let st = i; while char_is_alnum(s[i]) { i += 1 }
|
||||
let w = s[st..i]
|
||||
if s[i] == '"' and ((w == "k") or (w == "kn")) { # k"pause.resume": a key literal, one token
|
||||
i += 1
|
||||
while s[i] != 0 and s[i] != '"' and s[i] != '\n' { if s[i] == '\\' and s[i + 1] != 0 { i += 2 } else { i += 1 } }
|
||||
if s[i] == '"' { i += 1 }
|
||||
push_tok(LT_STR, st, i, line); continue
|
||||
}
|
||||
var k = LT_ID
|
||||
if (w == "true") or (w == "false") or (w == "null") { k = LT_BOOL }
|
||||
else { if is_type_word(w) { k = LT_TYPE }
|
||||
|
|
|
|||
|
|
@ -236,9 +236,9 @@ program LudicLsp {
|
|||
# ludic-dev syntax: begin - generated from `ludicc --emit-syntax` (selfhost/frontend/vocab.ludic); run `ludic-dev syntax`, do not edit
|
||||
function is_type_word(w: pointer) -> bool {
|
||||
if (w == "int") or (w == "long") or (w == "float") or (w == "double") or (w == "fixed") or (w == "bool") { return true }
|
||||
if (w == "byte") or (w == "string") or (w == "pointer") or (w == "void") or (w == "entity") or (w == "countdown") { return true }
|
||||
if (w == "words") or (w == "floats") or (w == "doubles") or (w == "fixeds") or (w == "pointers") or (w == "Vector") { return true }
|
||||
if (w == "IVec2") or (w == "Rect") { return true }
|
||||
if (w == "byte") or (w == "string") or (w == "Key") or (w == "pointer") or (w == "void") or (w == "entity") { return true }
|
||||
if (w == "countdown") or (w == "words") or (w == "floats") or (w == "doubles") or (w == "fixeds") or (w == "pointers") { return true }
|
||||
if (w == "Vector") or (w == "IVec2") or (w == "Rect") { return true }
|
||||
return false
|
||||
}
|
||||
function is_phase_word(w: pointer) -> bool {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ static const char* LUDIC_KW_STMT[] = {
|
|||
"dispatch","try","and","or","not", 0
|
||||
};
|
||||
static const char* LUDIC_TYPES[] = {
|
||||
"int","long","float","double","fixed","bool","byte","string","pointer","void","entity",
|
||||
"int","long","float","double","fixed","bool","byte","string","Key","pointer","void","entity",
|
||||
"countdown","words","floats","doubles","fixeds","pointers","Vector","IVec2","Rect", 0
|
||||
};
|
||||
static const char* LUDIC_PHASES[] = {
|
||||
|
|
@ -84,10 +84,10 @@ static const char* LUDIC_PHASES[] = {
|
|||
static const char* LUDIC_ATTRIBUTES[] = {
|
||||
"Ref","OneOf","Range","Unit","Asset","Color","Node","Clip","Material","Tint","Derived","Text",
|
||||
"Multiline","Key","Computed","Sync","frame","max","owns","AppendOnly","ByKey","PerMap",
|
||||
"Chunked","export","Public","On","Queries","OnSpawn","OnDespawn","OnAttach","OnDetach",
|
||||
"OnEnable","OnDisable","OnStart","OnQuit","Owned","Server","Predicted","ToServer","ToClients",
|
||||
"System","EngineSystem","Namespace","deterministic","ClearColor","alloc_ok","creates",
|
||||
"releases","Handles","Reads","Writes", 0
|
||||
"Chunked","TextKey","export","Public","On","Queries","OnSpawn","OnDespawn","OnAttach",
|
||||
"OnDetach","OnEnable","OnDisable","OnStart","OnQuit","Owned","Server","Predicted","ToServer",
|
||||
"ToClients","System","EngineSystem","Namespace","deterministic","ClearColor","alloc_ok",
|
||||
"creates","releases","Handles","Reads","Writes", 0
|
||||
};
|
||||
/* ludic-dev syntax: end */
|
||||
static const char* LUDIC_WIDGETS[] = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue