diff --git a/tools/atlas.ludic b/tools/atlas.ludic index e12791c9..20006f40 100644 --- a/tools/atlas.ludic +++ b/tools/atlas.ludic @@ -1,56 +1,41 @@ -# ============================================================================ # atlas.ludic — render a Kenney tile pack as one index-labeled sheet, so you can # see which sprite id is which before referring to it in a game. # -# ludicc tools/atlas.ludic --headless -o build/atlas -# ./build/atlas && sips -s format png out.ppm --out atlas.png +# bin/ludicc --headless tools/atlas.ludic -o build/atlas +# ./build/atlas < /dev/null && sips -s format png build/out.ppm --out atlas.png # -# Replaces the old tools/atlas.c, which used the C runtime that no longer -# exists. It is the same program, in the language it is a tool for. -# ============================================================================ -game Atlas { - const DIR: str = "assets/kenney/tiny-dungeon/Tiles" - const N: int = 132 - const COLS: int = 12 +# A tool written in the language it is a tool for: one Start handler draws every +# tile with its number underneath and quits, and the headless build leaves the +# frame in build/out.ppm. +program Atlas { + const DIR: string = "assets/kenney/tiny-dungeon/Tiles" + const TILE_COUNT: int = 132 # tile_0000.png … tile_0131.png + const COLUMNS: int = 12 const CELL: int = 24 + const SPRITE_INSET_X: int = 4 # centre a 16px sprite in a 24px cell + const SPRITE_INSET_Y: int = 2 + const LABEL_INSET_X: int = 1 + const LABEL_INSET_Y: int = 18 + const BACKDROP: int = 0x101018 + const LABEL_COLOR: int = 0x8090b0 - system Sheet phase Start { - clear(0x101018) - for i in 0 .. N { - let col = i % COLS - let row = i / COLS - let x = col * CELL - let y = row * CELL - # tile_0000.png … tile_0131.png — the id is the file's number - let id = load_png(tile_path(i)) - if id >= 0 { - draw_sprite(id, x + 4, y + 2) - } - text_int(x + 1, y + 18, i, 0x8090b0, 1) + # "/tile_0007.png" — the id is the file's zero-padded number + function tile_path(i: int) -> string { + var digits = Text.from_int(i) + while Text.length(digits) < 4 { digits = "0" + digits } + return `{DIR}/tile_{digits}.png` + } + + handler Sheet phase Start { + Screen.clear(BACKDROP) + for i in 0 .. TILE_COUNT { + let x = (i % COLUMNS) * CELL + let y = (i / COLUMNS) * CELL + let id = Assets.image(tile_path(i)) + if id >= 0 { Screen.sprite(id, x + SPRITE_INSET_X, y + SPRITE_INSET_Y) } + Screen.draw_number(x + LABEL_INSET_X, y + LABEL_INSET_Y, i, LABEL_COLOR, 1) } - present() + Screen.show() quit() } - - # build "/tile_%04d.png" without a formatter: the runtime's string - # helpers write into a scratch buffer we own - function tile_path(i: int) -> string { - let buf = atlas_buf() - let n = rt_put_str(buf, 0, DIR) - n = rt_put_str(buf, n, "/tile_") - if i < 1000 { poke8(buf, n, 48) n = n + 1 } - if i < 100 { poke8(buf, n, 48) n = n + 1 } - if i < 10 { poke8(buf, n, 48) n = n + 1 } - n = rt_put_int(buf, n, i) - n = rt_put_str(buf, n, ".png") - poke8(buf, n, 0) - return buf - } - - var atlas_scratch: pointer = ptr_null() - - function atlas_buf() -> pointer { - if ptr_is_null(atlas_scratch) { atlas_scratch = mem_alloc(512) } - return atlas_scratch - } } diff --git a/tools/test-grammar.js b/tools/test-grammar.js index ad37af66..865f1dc5 100644 --- a/tools/test-grammar.js +++ b/tools/test-grammar.js @@ -10,8 +10,9 @@ * * node tools/test-grammar.js * - * Needs vscode-textmate and vscode-oniguruma; skips cleanly when they are not - * installed, so it never blocks the suite on a machine without npm. + * Needs vscode-textmate and vscode-oniguruma (from the VS Code extension's + * node_modules, or a directory named by $LUDIC_NODE_MODULES); exits 2 when they + * are not installed so the caller can report a skip rather than a pass. * ==========================================================================*/ const fs = require('fs'); const path = require('path'); @@ -22,12 +23,12 @@ const SHARED = path.join(ROOT, 'tools', 'editors', 'shared'); let vsctm, oniguruma; try { const local = path.join(ROOT, 'tools', 'editors', 'vscode', 'node_modules'); - const paths = [local, '/tmp/tmgrammar/node_modules', ...module.paths]; + const paths = [local, ...(process.env.LUDIC_NODE_MODULES ? [process.env.LUDIC_NODE_MODULES] : []), ...module.paths]; vsctm = require(require.resolve('vscode-textmate', { paths })); oniguruma = require(require.resolve('vscode-oniguruma', { paths })); } catch (e) { console.log(' skip TextMate grammar (npm i vscode-textmate vscode-oniguruma to run)'); - process.exit(0); + process.exit(2); } let passed = 0; @@ -38,7 +39,7 @@ function bad(name, detail) { failed++; console.log(` FAIL ${name}\n ${d async function main() { const wasmPath = require.resolve('vscode-oniguruma/release/onig.wasm', { - paths: [path.join(ROOT, 'tools', 'editors', 'vscode', 'node_modules'), '/tmp/tmgrammar/node_modules'] + paths: [path.join(ROOT, 'tools', 'editors', 'vscode', 'node_modules'), ...(process.env.LUDIC_NODE_MODULES ? [process.env.LUDIC_NODE_MODULES] : []), ...module.paths] }); await oniguruma.loadWASM(fs.readFileSync(wasmPath).buffer); @@ -96,27 +97,31 @@ async function main() { } expect('comment', '# a note', '# a note', 'comment.line'); - expect('string', 'const S: str = "hi"', '"hi"', 'string.quoted.double'); - expect('hex literal', 'clear(0x0e0e16)', '0x0e0e16', 'constant.numeric.hex'); + expect('string', 'const S: string = "hi"', '"hi"', 'string.quoted.double'); + expect('interpolated string', 'let m = `hp {hp}`', '`hp {hp}`', 'string.interpolated'); + expect('hex literal', 'Screen.clear(0x0e0e16)', '0x0e0e16', 'constant.numeric.hex'); expect('fixed literal', 'let f = 1.5', '1.5', 'constant.numeric.fixed'); - expect('declaration keyword', 'component Pos { x: int = 0 }', 'component', 'storage.type.component'); - expect('component name', 'component Pos { x: int = 0 }', 'Pos', 'entity.name.type.component'); - expect('primitive type', 'component Pos { x: int = 0 }', 'int', 'support.type.primitive'); - expect('system name', 'system Move phase Update {', 'Move', 'entity.name.function.system'); - expect('phase name', 'system Move phase Update {', 'Update', 'constant.language.phase'); - expect('clause keyword', ' reads [Vel]', 'reads', 'keyword.other.clause'); - expect('annotation', 'system Move @deterministic', '@deterministic', 'entity.name.function.decorator'); - expect('builtin call', ' clear(0x000000)', 'clear', 'support.function.builtin'); - expect('intrinsic call', ' mem_alloc(64)', 'mem_alloc', 'support.function.intrinsic'); + expect('declaration keyword', 'property Pos { x: int = 0 }', 'property', 'storage.type.component'); + expect('property name', 'property Pos { x: int = 0 }', 'Pos', 'entity.name.type.component'); + expect('primitive type', 'property Pos { x: int = 0 }', 'int', 'support.type.primitive'); + expect('model keyword', 'model Hero { Pos, Stats }', 'model', 'storage.type.archetype'); + expect('model name', 'model Hero { Pos, Stats }', 'Hero', 'entity.name.type.archetype'); + expect('handler name', 'handler Move phase Update {', 'Move', 'entity.name.function.system'); + expect('phase name', 'handler Move phase Update {', 'Update', 'constant.language.phase'); + expect('clause keyword', 'handler Move phase Update {', 'phase', 'keyword.other.clause'); + expect('annotation', '@Queries(Pos, Vel) handler Move phase Update {', '@Queries', 'entity.name.function.decorator'); + expect('builtin call', ' print(1)', 'print', 'support.function.builtin'); expect('control keyword', ' if a == 1 { }', 'if', 'keyword.control'); + expect('become keyword', ' become Playing', 'become', 'keyword.control'); expect('member access', ' p.x = 1', 'x', 'variable.other.property'); expect('query tag filter', 'for (p) in query [Pos, {Player}] {', 'Player', 'entity.name.type.tag'); - expect('query component', 'for (p) in query [Pos, {Player}] {', 'Pos', 'entity.name.type.component'); - expect('widget type', ' button id=Go text="go"', 'button', 'support.class.widget'); - expect('widget prop', ' button id=Go text="go"', 'id', 'variable.parameter.widget'); + expect('query property', 'for (p) in query [Pos, {Player}] {', 'Pos', 'entity.name.type.component'); + expect('widget type', ' button id: Go text: "go"', 'button', 'support.class.widget'); expect('user function call', ' my_helper(1)', 'my_helper', 'entity.name.function'); - expect('archetype in spawn', ' spawn Hero {', 'Hero', 'entity.name.type.archetype'); - expect('scene in enter', ' enter Battle', 'Battle', 'entity.name.type.scene'); + expect('model in spawn', ' spawn Hero {', 'Hero', 'entity.name.type.archetype'); + expect('scene declaration', 'scene Battle {', 'Battle', 'entity.name.type'); + expect('prefab declaration', 'prefab Orc {', 'Orc', 'entity.name.type'); + expect('event declaration', 'event Hurt { amount: int = 0 }', 'Hurt', 'entity.name.type'); // A whole file must tokenise without the engine bailing out. const sample = fs.readFileSync(path.join(ROOT, 'examples', 'games', 'menu.ludic'), 'utf8'); @@ -140,7 +145,7 @@ async function main() { // Drive the injection directly: feed it the fence, then the body, and check // the body is handed to source.ludic. - const lines = ['```ludic', 'component Pos { x: int = 0 }', '```']; + const lines = ['```ludic', 'property Pos { x: int = 0 }', '```']; let mdState = vsctm.INITIAL; const bodyScopes = []; lines.forEach((line, i) => { diff --git a/tools/x/build.ludic b/tools/x/build.ludic index da4953d2..444aa7fe 100644 --- a/tools/x/build.ludic +++ b/tools/x/build.ludic @@ -56,8 +56,8 @@ function cmd_app() -> int { if (a == "--headless") { mode = 2 } else if (a == "--windowed") { mode = 1 } else if (a == "--save-temps") { save = true } - else if (a[0] != 45) { src = a } # 45 = '-', so skip other flags - ai = ai + 1 + else if (a[0] != '-') { src = a } # 45 = '-', so skip other flags + ai += 1 } if (src == null) { err("usage: x app [--headless] [--save-temps]\n"); return 1 } let name = capture_line(`basename {src} .ludic`) diff --git a/tools/x/checks.ludic b/tools/x/checks.ludic index a776234a..28f1e7ce 100644 --- a/tools/x/checks.ludic +++ b/tools/x/checks.ludic @@ -12,7 +12,7 @@ # ---- small string helpers --------------------------------------------------- # length of a NUL-terminated buffer -function slen(s: pointer) -> int { var n = 0; while s[n] != 0 { n = n + 1 }; return n } +function slen(s: pointer) -> int { var n = 0; while s[n] != 0 { n += 1 }; return n } # a fresh NUL-terminated copy of s[start .. end) (end exclusive) function sslice(s: pointer, start: int, end: int) -> pointer { @@ -20,7 +20,7 @@ function sslice(s: pointer, start: int, end: int) -> pointer { let n = end - start let b = bytes(n + 1) var i = 0 - while i < n { b[i] = s[start + i]; i = i + 1 } + while i < n { b[i] = s[start + i]; i += 1 } b[n] = 0 return b } @@ -33,11 +33,11 @@ function s_index(hay: pointer, needle: pointer, from: int) -> int { var i = from while i + nn <= hn { var j = 0 - while j < nn and hay[i + j] == needle[j] { j = j + 1 } + while j < nn and hay[i + j] == needle[j] { j += 1 } if j == nn { return i } - i = i + 1 + i += 1 } - return 0 - 1 + return -1 } function s_contains(hay: pointer, needle: pointer) -> bool { return s_index(hay, needle, 0) >= 0 } @@ -46,7 +46,7 @@ function s_contains(hay: pointer, needle: pointer) -> bool { return s_index(hay, function s_starts_at(hay: pointer, at: int, needle: pointer) -> bool { let nn = slen(needle) var i = 0 - while i < nn { if hay[at + i] != needle[i] { return false }; i = i + 1 } + while i < nn { if hay[at + i] != needle[i] { return false }; i += 1 } return true } @@ -54,17 +54,17 @@ function s_starts_at(hay: pointer, at: int, needle: pointer) -> bool { function s_starts(s: pointer, pre: pointer) -> bool { let pn = slen(pre) var i = 0 - while i < pn { if s[i] != pre[i] { return false }; i = i + 1 } + while i < pn { if s[i] != pre[i] { return false }; i += 1 } return true } # is byte c an ASCII space/tab? -function is_ws(c: int) -> bool { return c == 32 or c == 9 } +function is_ws(c: int) -> bool { return c == ' ' or c == '\t' } # the substring from `start` up to the next '\n' (or end) function line_at(s: pointer, start: int) -> pointer { var e = start - while s[e] != 0 and s[e] != 10 { e = e + 1 } + while s[e] != 0 and s[e] != '\n' { e += 1 } return sslice(s, start, e) } @@ -72,9 +72,9 @@ function line_at(s: pointer, start: int) -> pointer { function s_trim(s: pointer) -> pointer { let n = slen(s) var a = 0 - while a < n and (is_ws(s[a]) or s[a] == 10 or s[a] == 13) { a = a + 1 } + while a < n and (is_ws(s[a]) or s[a] == '\n' or s[a] == '\r') { a += 1 } var b = n - while b > a and (is_ws(s[b - 1]) or s[b - 1] == 10 or s[b - 1] == 13) { b = b - 1 } + while b > a and (is_ws(s[b - 1]) or s[b - 1] == '\n' or s[b - 1] == '\r') { b -= 1 } return sslice(s, a, b) } @@ -101,11 +101,11 @@ function classify_fence(body: pointer) -> pointer { while i < n { let ln = line_at(body, i) let t = s_trim(ln) - if slen(t) > 0 and t[0] != 35 { # non-empty, not a '#' comment + if slen(t) > 0 and t[0] != '#' { # non-empty, not a '#' comment # head = first token, split on '(' or whitespace var e = 0 let tn = slen(t) - while e < tn and t[e] != 40 and not is_ws(t[e]) { e = e + 1 } + while e < tn and t[e] != '(' and not is_ws(t[e]) { e += 1 } let head = sslice(t, 0, e) if head == "program" { return "whole" } if is_decl_head(head) { return "decls" } @@ -118,8 +118,8 @@ function classify_fence(body: pointer) -> pointer { # does the wrapped source at `path` parse? (ludicc --fmt = lex+parse gate) function fence_parses(src: pointer) -> bool { - write_file("/tmp/x_docchk.ludic", src) - return shq("bin/ludicc /tmp/x_docchk.ludic --fmt > /dev/null 2>&1") + write_file(`{tmp_dir()}/docchk.ludic`, src) + return shq(`bin/ludicc {tmp_dir()}/docchk.ludic --fmt > /dev/null 2>&1`) } # try the candidate framings for a fence; true if any parses @@ -149,21 +149,21 @@ function check_docs_file(path: pointer) -> void { let open = s_index(t, "```ludic\n", i) if open < 0 { break } # require the fence to sit at a line start - if open != 0 and t[open - 1] != 10 { i = open + 1; continue } + if open != 0 and t[open - 1] != '\n' { i = open + 1; continue } let bstart = open + 9 # past "```ludic\n" let close = s_index(t, "\n```", bstart) if close < 0 { break } let body = sslice(t, bstart, close + 1) # include trailing newline let line = 1 + count_nl(t, open) i = close + 4 - if s_contains(body, "# doc-check: skip") { DC_SKIP = DC_SKIP + 1; continue } + if s_contains(body, "# doc-check: skip") { DC_SKIP += 1; continue } let expect_err = s_contains(body, "# doc-check: expect-error") let kind = classify_fence(body) let parsed = fence_body_parses(body, kind) if parsed == (not expect_err) { - DC_OK = DC_OK + 1 + DC_OK += 1 } else { - DC_BAD = DC_BAD + 1 + DC_BAD += 1 if expect_err { DC_FAILS = DC_FAILS + " " + path + ":" + string(line) + " expected to be rejected\n" } else { DC_FAILS = DC_FAILS + " " + path + ":" + string(line) + " expected to parse\n" } } @@ -173,7 +173,7 @@ function check_docs_file(path: pointer) -> void { # number of '\n' in s[0 .. upto) function count_nl(s: pointer, upto: int) -> int { var n = 0; var i = 0 - while i < upto { if s[i] == 10 { n = n + 1 }; i = i + 1 } + while i < upto { if s[i] == '\n' { n += 1 }; i += 1 } return n } @@ -205,12 +205,12 @@ function cmd_check_docs() -> int { # asserts they agree. Replaces the former tools/docgen/check-impl.py. # a double-quote byte as a string (the lexer has no \" escape we rely on) -function dq() -> pointer { let b = bytes(2); b[0] = 34; b[1] = 0; return b } +function dq() -> pointer { let b = bytes(2); b[0] = '"'; b[1] = 0; return b } # set-style string list function set_has(l: []pointer, s: pointer) -> bool { var i = 0 - while i < len(l) { if l[i] == s { return true }; i = i + 1 } + while i < len(l) { if l[i] == s { return true }; i += 1 } return false } function set_add(l: []pointer, s: pointer) -> void { if not set_has(l, s) { push(l, s) } } @@ -218,7 +218,7 @@ function set_add(l: []pointer, s: pointer) -> void { if not set_has(l, s) { push # the body of `function (` up to the next top-level `function ` (or EOF) function fn_body(src: pointer, name: pointer) -> pointer { let needle = "function " + name + "(" - var p = 0 - 1 + var p = -1 if s_starts(src, needle) { p = 0 } else { let q = s_index(src, "\n" + needle, 0) @@ -239,7 +239,7 @@ function collect_after(src: pointer, marker: pointer) -> []pointer { let p = s_index(src, marker, i) if p < 0 { break } var e = p + mn - while src[e] != 0 and src[e] != 34 { e = e + 1 } + while src[e] != 0 and src[e] != '"' { e += 1 } push(out, sslice(src, p + mn, e)) i = e + 1 } @@ -256,20 +256,20 @@ function table_set(h: pointer, name: pointer) -> []pointer { if b < 0 or e < 0 { return out } var i = b while i < e { - if h[i] == 34 { + if h[i] == '"' { var j = i + 1 - while j < e and h[j] != 34 { j = j + 1 } + while j < e and h[j] != '"' { j += 1 } set_add(out, sslice(h, i + 1, j)) i = j + 1 - } else { i = i + 1 } + } else { i += 1 } } return out } # concatenate every selfhost/*.ludic into one buffer (via cat, then read) function read_all_selfhost() -> pointer { - run("find selfhost -name '*.ludic' | sort | xargs cat > /tmp/x_allsh.txt 2>/dev/null") - let s = read_file("/tmp/x_allsh.txt") + run(`find selfhost -name '*.ludic' | sort | xargs cat > {tmp_dir()}/allsh.txt 2>/dev/null`) + let s = read_file(`{tmp_dir()}/allsh.txt`) if s == null { return "" } return s } @@ -285,7 +285,7 @@ function collect_impl_pairs(allsrc: pointer, pairs: []pointer) -> void { let p = s_index(body, mns, i) if p < 0 { break } var ne = p + mnslen - while body[ne] != 0 and body[ne] != 34 { ne = ne + 1 } + while body[ne] != 0 and body[ne] != '"' { ne += 1 } let nsname = sslice(body, p + mnslen, ne) let nxt = s_index(body, mns, ne) var chunkEnd = slen(body) @@ -296,13 +296,13 @@ function collect_impl_pairs(allsrc: pointer, pairs: []pointer) -> void { let dp = s_index(chunk, "is_", 0) if dp >= 0 { var pe = dp - while chunk[pe] != 0 and chunk[pe] != 40 { pe = pe + 1 } + while chunk[pe] != 0 and chunk[pe] != '(' { pe += 1 } let predname = sslice(chunk, dp, pe) methods = collect_after(fn_body(allsrc, predname), mmeth) } } var k = 0 - while k < len(methods) { set_add(pairs, nsname + "." + methods[k]); k = k + 1 } + while k < len(methods) { set_add(pairs, nsname + "." + methods[k]); k += 1 } if nxt < 0 { break } i = nxt } @@ -340,9 +340,9 @@ function collect_docs(tokens: []pointer, docpairs: []pointer) -> void { var a = 0 let vn = slen(val) while a < vn { - while a < vn and val[a] == 32 { a = a + 1 } + while a < vn and val[a] == ' ' { a += 1 } var e = a - while e < vn and val[e] != 32 { e = e + 1 } + while e < vn and val[e] != ' ' { e += 1 } if e > a { set_add(tokens, sslice(val, a, e)) } a = e } @@ -359,7 +359,7 @@ function collect_docs(tokens: []pointer, docpairs: []pointer) -> void { var CI_PROB: pointer = "" var CI_NPROB: int = 0 -function ci_problem(msg: pointer) -> void { CI_PROB = CI_PROB + " - " + msg + "\n"; CI_NPROB = CI_NPROB + 1 } +function ci_problem(msg: pointer) -> void { CI_PROB = CI_PROB + " - " + msg + "\n"; CI_NPROB += 1 } # usage: x check-impl function cmd_check_impl() -> int { @@ -377,13 +377,13 @@ function cmd_check_impl() -> int { let pair = impl[i] if not set_has(docpairs, pair) { ci_problem("undocumented " + pair + " — add a docs/language page (tokens: " + pair + ")") } else { if not set_has(tokens, pair) { ci_problem(pair + " documented but its page lacks that tokens entry") } } - i = i + 1 + i += 1 } # 2) every documented ns-method corresponds to real dispatch i = 0 while i < len(docpairs) { if not set_has(impl, docpairs[i]) { ci_problem("stale doc: " + docpairs[i] + " is not dispatched by the compiler") } - i = i + 1 + i += 1 } # 3) every implemented keyword / type / phase is documented let h = read_file("tools/ludic-tools/ludic_syntax.h") @@ -410,14 +410,14 @@ function cmd_check_impl() -> int { # add every element of `src` not in `deny` to `dst` (set semantics) function add_all_except(dst: []pointer, src: []pointer, deny: []pointer) -> void { var i = 0 - while i < len(src) { if not set_has(deny, src[i]) { set_add(dst, src[i]) }; i = i + 1 } + while i < len(src) { if not set_has(deny, src[i]) { set_add(dst, src[i]) }; i += 1 } } # every name must appear in the token set, else a problem function check_all_documented(names: []pointer, tokens: []pointer, label: pointer) -> void { var i = 0 while i < len(names) { if not set_has(tokens, names[i]) { ci_problem("undocumented " + label + ": " + names[i] + " (no docs/language page lists it in tokens)") } - i = i + 1 + i += 1 } } @@ -435,7 +435,7 @@ function all_lower(s: pointer) -> bool { let n = slen(s) if n == 0 { return false } var i = 0 - while i < n { if s[i] < 97 or s[i] > 122 { return false }; i = i + 1 } + while i < n { if s[i] < 'a' or s[i] > 'z' { return false }; i += 1 } return true } @@ -446,7 +446,7 @@ function split_pipe(s: pointer) -> []pointer { var a = 0 while a <= n { var e = a - while e < n and s[e] != 124 { e = e + 1 } + while e < n and s[e] != '|' { e += 1 } set_add(out, sslice(s, a, e)) a = e + 1 } @@ -463,16 +463,16 @@ function c_table_names(h: pointer, name: pointer) -> []pointer { if b < 0 or e < 0 { return out } var i = b + 1 while i < e { - if h[i] == 123 { # '{' opens a row + if h[i] == '{' { # '{' opens a row var j = i + 1 - while j < e and (h[j] == 32 or h[j] == 9 or h[j] == 10) { j = j + 1 } - if h[j] == 34 { + while j < e and (h[j] == ' ' or h[j] == '\t' or h[j] == '\n') { j += 1 } + if h[j] == '"' { var k = j + 1 - while k < e and h[k] != 34 { k = k + 1 } + while k < e and h[k] != '"' { k += 1 } set_add(out, sslice(h, j + 1, k)) } } - i = i + 1 + i += 1 } return out } @@ -488,22 +488,22 @@ function kotlin_set(text: pointer, name: pointer) -> []pointer { let region_start = i let tn = slen(text) while i < tn and depth > 0 { - if text[i] == 40 { depth = depth + 1 } - if text[i] == 41 { depth = depth - 1 } + if text[i] == '(' { depth += 1 } + if text[i] == ')' { depth -= 1 } if depth == 0 { break } - i = i + 1 + i += 1 } # collect quoted strings in [region_start, i), skipping //-comments var k = region_start while k < i { - if text[k] == 47 and text[k + 1] == 47 { # '//' - while k < i and text[k] != 10 { k = k + 1 } - } else { if text[k] == 34 { + if text[k] == '/' and text[k + 1] == '/' { # '//' + while k < i and text[k] != '\n' { k += 1 } + } else { if text[k] == '"' { var e = k + 1 - while e < i and text[e] != 34 { e = e + 1 } + while e < i and text[e] != '"' { e += 1 } set_add(out, sslice(text, k + 1, e)) k = e + 1 - } else { k = k + 1 } } + } else { k += 1 } } } return out } @@ -517,13 +517,13 @@ function grammar_alt(root: JVal, node: pointer, marker: pointer) -> []pointer { while i < len(pats.kids) { let m = j_get(pats.kids[i], "match") if m.t == JV_STR and s_contains(m.s, marker) { - let op = s_index(m.s, bx3(92, 98, 40), 0) # \b( + let op = s_index(m.s, bx3('\\', 'b', '('), 0) # \b( if op >= 0 { - let cl = s_index(m.s, bx3(41, 92, 98), op) # )\b + let cl = s_index(m.s, bx3(')', '\\', 'b'), op) # )\b if cl >= 0 { return split_pipe(sslice(m.s, op + 3, cl)) } } } - i = i + 1 + i += 1 } return out } @@ -543,28 +543,28 @@ function add_parser_kw(out: []pointer, path: pointer) -> void { } function add_lower(out: []pointer, src: []pointer) -> void { var i = 0 - while i < len(src) { if all_lower(src[i]) { set_add(out, src[i]) }; i = i + 1 } + while i < len(src) { if all_lower(src[i]) { set_add(out, src[i]) }; i += 1 } } var CV_PROB: pointer = "" var CV_N: int = 0 -function cv_problem(msg: pointer) -> void { CV_PROB = CV_PROB + " - " + msg + "\n"; CV_N = CV_N + 1 } +function cv_problem(msg: pointer) -> void { CV_PROB = CV_PROB + " - " + msg + "\n"; CV_N += 1 } # report names in `ref` missing from `other`, and names in `other` not in `ref` function cmp_sets(label: pointer, ref: []pointer, other: []pointer, other_label: pointer) -> void { var i = 0 - while i < len(ref) { if not set_has(other, ref[i]) { cv_problem(other_label + " is missing " + label + ": " + ref[i]) }; i = i + 1 } + while i < len(ref) { if not set_has(other, ref[i]) { cv_problem(other_label + " is missing " + label + ": " + ref[i]) }; i += 1 } i = 0 - while i < len(other) { if not set_has(ref, other[i]) { cv_problem(other_label + " has unknown " + label + ": " + other[i]) }; i = i + 1 } + while i < len(other) { if not set_has(ref, other[i]) { cv_problem(other_label + " has unknown " + label + ": " + other[i]) }; i += 1 } } # union of two sets function set_union(a: []pointer, b: []pointer) -> []pointer { let out = new []pointer var i = 0 - while i < len(a) { set_add(out, a[i]); i = i + 1 } + while i < len(a) { set_add(out, a[i]); i += 1 } i = 0 - while i < len(b) { set_add(out, b[i]); i = i + 1 } + while i < len(b) { set_add(out, b[i]); i += 1 } return out } @@ -614,7 +614,7 @@ function cmd_check_vocab() -> int { cmp_unparsed("clause keywords", h_clause, pkw, h_reserved) # reserved words the parser now accepts should be promoted var i = 0 - while i < len(h_reserved) { if set_has(pkw, h_reserved[i]) { cv_problem("LUDIC_KW_RESERVED lists a keyword the parser now accepts — promote it: " + h_reserved[i]) }; i = i + 1 } + while i < len(h_reserved) { if set_has(pkw, h_reserved[i]) { cv_problem("LUDIC_KW_RESERVED lists a keyword the parser now accepts — promote it: " + h_reserved[i]) }; i += 1 } } if CV_N > 0 { @@ -632,7 +632,7 @@ function cmp_unparsed(label: pointer, kws: []pointer, pkw: []pointer, reserved: if not set_has(pkw, kws[i]) { if not set_has(reserved, kws[i]) { cv_problem("ludic_syntax.h lists a " + label + " the selfhost parser never dispatches on: " + kws[i]) } } - i = i + 1 + i += 1 } } @@ -648,14 +648,14 @@ function xml_valid(text: pointer) -> bool { var sp = 0 var i = 0 while i < n { - if text[i] != 60 { i = i + 1; continue } # seek '<' - if text[i + 1] == 63 { # + if text[i] != '<' { i += 1; continue } # seek '<' + if text[i + 1] == '?' { # let e = s_index(text, "?>", i) if e < 0 { return false } i = e + 2; continue } - if text[i + 1] == 33 { # + if text[i + 1] == '!' { # let e = s_index(text, "-->", i) if e < 0 { return false } i = e + 3; continue @@ -669,33 +669,33 @@ function xml_valid(text: pointer) -> bool { if e < 0 { return false } i = e + 1; continue } - if text[i + 1] == 47 { # closing + if text[i + 1] == '/' { # closing var j = i + 2 - while j < n and text[j] != 62 and text[j] != 32 and text[j] != 9 and text[j] != 10 { j = j + 1 } + while j < n and text[j] != '>' and text[j] != ' ' and text[j] != '\t' and text[j] != '\n' { j += 1 } let name = sslice(text, i + 2, j) let e = s_index(text, ">", j) if e < 0 { return false } if sp == 0 { return false } if stack[sp - 1] != name { return false } - sp = sp - 1 + sp -= 1 i = e + 1; continue } # opening tag: read the name var j = i + 1 - while j < n and text[j] != 62 and text[j] != 32 and text[j] != 9 and text[j] != 10 and text[j] != 47 { j = j + 1 } + while j < n and text[j] != '>' and text[j] != ' ' and text[j] != '\t' and text[j] != '\n' and text[j] != '/' { j += 1 } let name = sslice(text, i + 1, j) if slen(name) == 0 { return false } # advance to '>', skipping quoted attribute values var k = j - while k < n and text[k] != 62 { - if text[k] == 34 { k = k + 1; while k < n and text[k] != 34 { k = k + 1 } } - else { if text[k] == 39 { k = k + 1; while k < n and text[k] != 39 { k = k + 1 } } } - k = k + 1 + while k < n and text[k] != '>' { + if text[k] == '"' { k += 1; while k < n and text[k] != '"' { k += 1 } } + else { if text[k] == '\'' { k += 1; while k < n and text[k] != '\'' { k += 1 } } } + k += 1 } if k >= n { return false } - if text[k - 1] != 47 { # not self-closing -> push + if text[k - 1] != '/' { # not self-closing -> push if sp < len(stack) { stack[sp] = name } else { push(stack, name) } - sp = sp + 1 + sp += 1 } i = k + 1 } diff --git a/tools/x/docgen.ludic b/tools/x/docgen.ludic index f53b2063..55bcc166 100644 --- a/tools/x/docgen.ludic +++ b/tools/x/docgen.ludic @@ -26,10 +26,10 @@ function sb_new() -> Sb { function sb_grow(s: Sb, need: int) -> void { if s.len + need + 1 <= s.cap { return } var nc = s.cap * 2 - while nc < s.len + need + 1 { nc = nc * 2 } + while nc < s.len + need + 1 { nc *= 2 } let nd = bytes(nc) var i = 0 - while i < s.len { nd[i] = s.data[i]; i = i + 1 } + while i < s.len { nd[i] = s.data[i]; i += 1 } s.data = nd s.cap = nc } @@ -37,12 +37,12 @@ function sb_puts(s: Sb, t: pointer) -> void { let n = slen(t) sb_grow(s, n) var i = 0 - while i < n { s.data[s.len] = t[i]; s.len = s.len + 1; i = i + 1 } + while i < n { s.data[s.len] = t[i]; s.len += 1; i += 1 } } function sb_putc(s: Sb, c: int) -> void { sb_grow(s, 1) s.data[s.len] = c - s.len = s.len + 1 + s.len += 1 } function sb_str(s: Sb) -> pointer { s.data[s.len] = 0 @@ -59,13 +59,13 @@ function esc_q(s: pointer, quote: bool) -> pointer { var i = 0 while i < n { let c = s[i] - if c == 38 { sb_puts(b, "&") } - else if c == 60 { sb_puts(b, "<") } - else if c == 62 { sb_puts(b, ">") } - else if quote and c == 34 { sb_puts(b, """) } - else if quote and c == 39 { sb_puts(b, "'") } + if c == '&' { sb_puts(b, "&") } + else if c == '<' { sb_puts(b, "<") } + else if c == '>' { sb_puts(b, ">") } + else if quote and c == '"' { sb_puts(b, """) } + else if quote and c == '\'' { sb_puts(b, "'") } else { sb_putc(b, c) } - i = i + 1 + i += 1 } return sb_str(b) } @@ -75,16 +75,16 @@ function hescattr(s: pointer) -> pointer { return esc_q(s, true) } # ---- generic byte/character predicates -------------------------------------- # Python \s = [ \t\n\r\f\v] function is_space_all(c: int) -> bool { - return c == 32 or c == 9 or c == 10 or c == 13 or c == 12 or c == 11 + return c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == 12 or c == 11 } # Python \w = [A-Za-z0-9_] function is_word(c: int) -> bool { - return (c >= 48 and c <= 57) or (c >= 65 and c <= 90) or (c >= 97 and c <= 122) or c == 95 + return (c >= '0' and c <= '9') or (c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z') or c == '_' } # strip only leading '\n' (Python str.lstrip("\n")) function lstrip_nl(s: pointer) -> pointer { var i = 0 - while s[i] == 10 { i = i + 1 } + while s[i] == '\n' { i += 1 } return sslice(s, i, slen(s)) } # lowercase ASCII A-Z @@ -94,9 +94,9 @@ function lower_ascii(s: pointer) -> pointer { var i = 0 while i < n { var c = s[i] - if c >= 65 and c <= 90 { c = c + 32 } + if c >= 'A' and c <= 'Z' { c += 32 } b[i] = c - i = i + 1 + i += 1 } b[n] = 0 return b @@ -109,14 +109,14 @@ function title_case(s: pointer) -> pointer { var prev_alpha = false while i < n { var c = s[i] - let al = (c >= 65 and c <= 90) or (c >= 97 and c <= 122) + let al = (c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z') if al { - if prev_alpha { if c >= 65 and c <= 90 { c = c + 32 } } - else { if c >= 97 and c <= 122 { c = c - 32 } } + if prev_alpha { if c >= 'A' and c <= 'Z' { c += 32 } } + else { if c >= 'a' and c <= 'z' { c -= 32 } } } b[i] = c prev_alpha = al - i = i + 1 + i += 1 } b[n] = 0 return b @@ -128,7 +128,7 @@ function str_cmp(a: pointer, b: pointer) -> int { let ca = a[i] & 255 let cb = b[i] & 255 if ca != cb { return ca - cb } - i = i + 1 + i += 1 } return (a[i] & 255) - (b[i] & 255) } @@ -146,9 +146,9 @@ function split_ws(s: pointer) -> []pointer { let n = slen(s) var i = 0 while i < n { - while i < n and is_space_all(s[i]) { i = i + 1 } + while i < n and is_space_all(s[i]) { i += 1 } var e = i - while e < n and not is_space_all(s[e]) { e = e + 1 } + while e < n and not is_space_all(s[e]) { e += 1 } if e > i { push(out, sslice(s, i, e)) } i = e } @@ -160,9 +160,9 @@ function strs_sort(a: []pointer) -> void { while i < len(a) { let cur = a[i] var j = i - 1 - while j >= 0 and str_gt(a[j], cur) { a[j + 1] = a[j]; j = j - 1 } + while j >= 0 and str_gt(a[j], cur) { a[j + 1] = a[j]; j -= 1 } a[j + 1] = cur - i = i + 1 + i += 1 } } @@ -173,22 +173,22 @@ function codeify(s: pointer) -> pointer { let b = sb_new() var i = 0 while i < n { - if s[i] == 96 { + if s[i] == '`' { var j = i + 1 - while j < n and s[j] != 96 { j = j + 1 } + while j < n and s[j] != '`' { j += 1 } if j < n and j > i + 1 { sb_puts(b, "") var k = i + 1 - while k < j { sb_putc(b, s[k]); k = k + 1 } + while k < j { sb_putc(b, s[k]); k += 1 } sb_puts(b, "") i = j + 1 } else { - sb_putc(b, 96) - i = i + 1 + sb_putc(b, '`') + i += 1 } } else { sb_putc(b, s[i]) - i = i + 1 + i += 1 } } return sb_str(b) @@ -204,7 +204,7 @@ function jva() -> JVal { return jval_new(JV_ARR) } # token collides across kinds (e.g. `exit`/`string`/`fixed`) in sym["tips"]. function jvput(o: JVal, k: pointer, v: JVal) -> void { var i = 0 - while i < len(o.keys) { if o.keys[i] == k { o.kids[i] = v; return }; i = i + 1 } + while i < len(o.keys) { if o.keys[i] == k { o.kids[i] = v; return }; i += 1 } push(o.keys, k) push(o.kids, v) } @@ -215,7 +215,7 @@ function hex_lower(n: int) -> int { return 87 + n } function jemit_u(b: Sb, cp: int) -> void { - sb_putc(b, 92); sb_putc(b, 117) # \u + sb_putc(b, '\\'); sb_putc(b, 'u') # \u sb_putc(b, hex_lower((cp >> 12) & 15)) sb_putc(b, hex_lower((cp >> 8) & 15)) sb_putc(b, hex_lower((cp >> 4) & 15)) @@ -228,20 +228,20 @@ function json_escape_into(b: Sb, s: pointer, ensure_ascii: bool) -> void { var i = 0 while i < n { let c = s[i] & 255 - if c == 34 { sb_putc(b, 92); sb_putc(b, 34); i = i + 1 } - else if c == 92 { sb_putc(b, 92); sb_putc(b, 92); i = i + 1 } - else if c == 8 { sb_putc(b, 92); sb_putc(b, 98); i = i + 1 } - else if c == 9 { sb_putc(b, 92); sb_putc(b, 116); i = i + 1 } - else if c == 10 { sb_putc(b, 92); sb_putc(b, 110); i = i + 1 } - else if c == 12 { sb_putc(b, 92); sb_putc(b, 102); i = i + 1 } - else if c == 13 { sb_putc(b, 92); sb_putc(b, 114); i = i + 1 } - else if c < 32 { - sb_putc(b, 92); sb_putc(b, 117); sb_putc(b, 48); sb_putc(b, 48) + if c == '"' { sb_putc(b, '\\'); sb_putc(b, '"'); i += 1 } + else if c == '\\' { sb_putc(b, '\\'); sb_putc(b, '\\'); i += 1 } + else if c == 8 { sb_putc(b, '\\'); sb_putc(b, 'b'); i += 1 } + else if c == '\t' { sb_putc(b, '\\'); sb_putc(b, 't'); i += 1 } + else if c == '\n' { sb_putc(b, '\\'); sb_putc(b, 'n'); i += 1 } + else if c == 12 { sb_putc(b, '\\'); sb_putc(b, 'f'); i += 1 } + else if c == '\r' { sb_putc(b, '\\'); sb_putc(b, 'r'); i += 1 } + else if c < ' ' { + sb_putc(b, '\\'); sb_putc(b, 'u'); sb_putc(b, '0'); sb_putc(b, '0') sb_putc(b, hex_lower((c >> 4) & 15)); sb_putc(b, hex_lower(c & 15)) - i = i + 1 + i += 1 } - else if c < 128 { sb_putc(b, c); i = i + 1 } - else if not ensure_ascii { sb_putc(b, c); i = i + 1 } + else if c < 128 { sb_putc(b, c); i += 1 } + else if not ensure_ascii { sb_putc(b, c); i += 1 } else { var cp = 0 var nb = 1 @@ -249,8 +249,8 @@ function json_escape_into(b: Sb, s: pointer, ensure_ascii: bool) -> void { else if c < 240 { cp = c - 224; nb = 3 } else { cp = c - 240; nb = 4 } var k = 1 - while k < nb and i + k < n { cp = cp * 64 + ((s[i + k] & 255) - 128); k = k + 1 } - i = i + nb + while k < nb and i + k < n { cp = cp * 64 + ((s[i + k] & 255) - 128); k += 1 } + i += nb if cp <= 65535 { jemit_u(b, cp) } else { let c2 = cp - 65536 @@ -262,7 +262,7 @@ function json_escape_into(b: Sb, s: pointer, ensure_ascii: bool) -> void { } function json_indent(b: Sb, depth: int) -> void { var i = 0 - while i < depth { sb_putc(b, 32); sb_putc(b, 32); i = i + 1 } + while i < depth { sb_putc(b, ' '); sb_putc(b, ' '); i += 1 } } # pretty=true reproduces json.dumps(indent=2); pretty=false the compact default # (item sep ", ", key sep ": "). ensure_ascii toggles \uXXXX for non-ASCII. @@ -273,36 +273,36 @@ function jwrite(b: Sb, v: JVal, pretty: bool, depth: int, ea: bool) -> void { return } if v.t == JV_NUM { sb_puts(b, string(v.num)); return } - if v.t == JV_STR { sb_putc(b, 34); json_escape_into(b, v.s, ea); sb_putc(b, 34); return } + if v.t == JV_STR { sb_putc(b, '"'); json_escape_into(b, v.s, ea); sb_putc(b, '"'); return } if v.t == JV_ARR { let m = len(v.kids) if m == 0 { sb_puts(b, "[]"); return } - sb_putc(b, 91) + sb_putc(b, '[') var i = 0 while i < m { - if pretty { sb_putc(b, 10); json_indent(b, depth + 1) } + if pretty { sb_putc(b, '\n'); json_indent(b, depth + 1) } jwrite(b, v.kids[i], pretty, depth + 1, ea) - if i < m - 1 { sb_putc(b, 44); if not pretty { sb_putc(b, 32) } } - i = i + 1 + if i < m - 1 { sb_putc(b, ','); if not pretty { sb_putc(b, ' ') } } + i += 1 } - if pretty { sb_putc(b, 10); json_indent(b, depth) } - sb_putc(b, 93) + if pretty { sb_putc(b, '\n'); json_indent(b, depth) } + sb_putc(b, ']') return } let m = len(v.keys) if m == 0 { sb_puts(b, "{}"); return } - sb_putc(b, 123) + sb_putc(b, '{') var i = 0 while i < m { - if pretty { sb_putc(b, 10); json_indent(b, depth + 1) } - sb_putc(b, 34); json_escape_into(b, v.keys[i], ea); sb_putc(b, 34) - sb_putc(b, 58); sb_putc(b, 32) + if pretty { sb_putc(b, '\n'); json_indent(b, depth + 1) } + sb_putc(b, '"'); json_escape_into(b, v.keys[i], ea); sb_putc(b, '"') + sb_putc(b, ':'); sb_putc(b, ' ') jwrite(b, v.kids[i], pretty, depth + 1, ea) - if i < m - 1 { sb_putc(b, 44); if not pretty { sb_putc(b, 32) } } - i = i + 1 + if i < m - 1 { sb_putc(b, ','); if not pretty { sb_putc(b, ' ') } } + i += 1 } - if pretty { sb_putc(b, 10); json_indent(b, depth) } - sb_putc(b, 125) + if pretty { sb_putc(b, '\n'); json_indent(b, depth) } + sb_putc(b, '}') } function json_dumps(v: JVal, pretty: bool, ea: bool) -> pointer { let b = sb_new() @@ -571,11 +571,10 @@ function palette_emit_ludic(path: pointer) -> bool { var i = 0 while i < len(pal_names) { sb_puts(b, ` if (name == "{pal_names[i]}") {{ return 0x{pal_hexes[i]} }}\n`) - i = i + 1 + i += 1 } sb_puts(b, " return -1\n") sb_puts(b, "}\n") - sb_puts(b, "\n") return write_file(path, sb_str(b)) } @@ -609,19 +608,23 @@ function palette_emit_json(path: pointer) -> bool { sb_puts(b, ` "hex": "{pal_hexes[ci]}"\n`) sb_puts(b, " }") } - ci = ci + 1 + ci += 1 } sb_puts(b, "\n ]\n") sb_puts(b, " }") } - gi = gi + 1 + gi += 1 } sb_puts(b, "\n ]\n") sb_puts(b, "}") return write_file(path, sb_str(b)) } +# `x docs-palette` rewrites the two tracked outputs; `x docs-palette --check` +# regenerates into scratch files and compares them to the tracked ones instead, +# so the drift guard judges the working tree, not git HEAD. function cmd_docs_palette() -> int { + let check = arg_count() > 2 and arg(2) == "--check" palette_init() # guard: duplicate names let seen = new []pointer @@ -629,10 +632,17 @@ function cmd_docs_palette() -> int { while i < len(pal_names) { if set_has(seen, pal_names[i]) { err(`DUPLICATE NAME: {pal_names[i]}\n`); return 1 } push(seen, pal_names[i]) - i = i + 1 + i += 1 + } + var ludic_out = "selfhost/backend/stdlib/emit_color.ludic" + var json_out = "docs/language/colors/palette.json" + if check { ludic_out = tmp_path("pal_emit_color.ludic"); json_out = tmp_path("pal_palette.json") } + if not palette_emit_ludic(ludic_out) { err("cannot write emit_color.ludic\n"); return 1 } + if not palette_emit_json(json_out) { err("cannot write palette.json\n"); return 1 } + if check { + if not shq(`cmp -s {ludic_out} selfhost/backend/stdlib/emit_color.ludic`) { err("emit_color.ludic drifted from the palette table (run: x docs-palette)\n"); return 1 } + if not shq(`cmp -s {json_out} docs/language/colors/palette.json`) { err("palette.json drifted from the palette table (run: x docs-palette)\n"); return 1 } } - if not palette_emit_ludic("selfhost/backend/stdlib/emit_color.ludic") { err("cannot write emit_color.ludic\n"); return 1 } - if not palette_emit_json("docs/language/colors/palette.json") { err("cannot write palette.json\n"); return 1 } print(`OK {string(len(pal_names))} colors`) return 0 } diff --git a/tools/x/docgen_check.ludic b/tools/x/docgen_check.ludic index 46096a59..913a613f 100644 --- a/tools/x/docgen_check.ludic +++ b/tools/x/docgen_check.ludic @@ -11,7 +11,7 @@ function bucket_get(bs: []Bucket, k1: pointer, k2: pointer) -> Bucket { var i = 0 while i < len(bs) { if bs[i].k1 == k1 and bs[i].k2 == k2 { return bs[i] } - i = i + 1 + i += 1 } let b = new Bucket b.k1 = k1 @@ -23,7 +23,7 @@ function bucket_get(bs: []Bucket, k1: pointer, k2: pointer) -> Bucket { function bucket_add(bs: []Bucket, k1: pointer, k2: pointer, v: pointer) -> void { let b = bucket_get(bs, k1, k2) var i = 0 - while i < len(b.vals) { if b.vals[i] == v { return }; i = i + 1 } + while i < len(b.vals) { if b.vals[i] == v { return }; i += 1 } push(b.vals, v) } # ", ".join(sorted(vals)) @@ -34,7 +34,7 @@ function join_sorted(vals: []pointer) -> pointer { while i < len(vals) { if i > 0 { sb_puts(b, ", ") } sb_puts(b, vals[i]) - i = i + 1 + i += 1 } return sb_str(b) } @@ -70,7 +70,7 @@ function cp_len(s: pointer) -> int { let n = slen(s) var c = 0 var i = 0 - while i < n { if (s[i] & 192) != 128 { c = c + 1 }; i = i + 1 } + while i < n { if (s[i] & 192) != 128 { c += 1 }; i += 1 } return c } function replace_all(hay: pointer, needle: pointer, repl: pointer) -> pointer { @@ -80,8 +80,8 @@ function replace_all(hay: pointer, needle: pointer, repl: pointer) -> pointer { let n = slen(hay) var i = 0 while i < n { - if s_starts_at(hay, i, needle) { sb_puts(b, repl); i = i + nl } - else { sb_putc(b, hay[i]); i = i + 1 } + if s_starts_at(hay, i, needle) { sb_puts(b, repl); i += nl } + else { sb_putc(b, hay[i]); i += 1 } } return sb_str(b) } @@ -110,14 +110,14 @@ function cmd_docs_check() -> int { var ci = 0 while ci < len(cats) { let cat = cats[ci] - ci = ci + 1 + ci += 1 let cdir = "docs/language/" + cat if not is_dir(cdir) { continue } let files = list_sorted(cdir) var fi = 0 while fi < len(files) { let fn = files[fi] - fi = fi + 1 + fi += 1 if not s_ends(fn, ".md") { continue } if fn == "_section.md" { continue } let d = parse_front(cdir + "/" + fn) @@ -143,12 +143,12 @@ function cmd_docs_check() -> int { while k < len(inv.keys) { let cat = inv.keys[k] let ids = inv.kids[k] - k = k + 1 + k += 1 if ids.t != JV_ARR { continue } var m = 0 while m < len(ids.kids) { let sid = ids.kids[m].s - m = m + 1 + m += 1 if meta_get(id2src, sid, "") == "" { push(problems, `no source file for inventory symbol: {sid} ({cat})`) } else if not file_exists(`{site}/{sid}.html`) { push(problems, `no generated page for symbol: {sid}.html`) } } @@ -160,11 +160,11 @@ function cmd_docs_check() -> int { let tok2ids = new []Bucket # k1=kind, k2=tok -> ids let ns2dirs = new []Bucket # k1="", k2=ns -> dirs let id2dirs = new []Bucket # k1="", k2=section id -> dirs - let title2dirs = new []Bucket # k1="", k2=lower(title) -> dirs + let title2dirs = new []Bucket # k1="", k2=lower(title) -> dirs ci = 0 while ci < len(cats) { let cat = cats[ci] - ci = ci + 1 + ci += 1 let cdir = "docs/language/" + cat if not is_dir(cdir) { continue } let secp = cdir + "/_section.md" @@ -177,7 +177,7 @@ function cmd_docs_check() -> int { var fi = 0 while fi < len(files) { let fn = files[fi] - fi = fi + 1 + fi += 1 if not s_ends(fn, ".md") { continue } if fn == "_section.md" { continue } let d = parse_front(cdir + "/" + fn) @@ -185,7 +185,7 @@ function cmd_docs_check() -> int { let id = meta_get(d.m, "id", "") let toks = split_ws(meta_get(d.m, "tokens", "")) var ti = 0 - while ti < len(toks) { bucket_add(tok2ids, kind, toks[ti], id); ti = ti + 1 } + while ti < len(toks) { bucket_add(tok2ids, kind, toks[ti], id); ti += 1 } let ns = meta_get(d.m, "ns", "") if slen(ns) > 0 { bucket_add(ns2dirs, "", ns, cat) } } @@ -193,25 +193,25 @@ function cmd_docs_check() -> int { var bi = 0 while bi < len(tok2ids) { let b = tok2ids[bi] - bi = bi + 1 + bi += 1 if len(b.vals) > 1 { push(problems, `token '{b.k2}' ({b.k1}) documented on multiple pages: {join_sorted(b.vals)}`) } } bi = 0 while bi < len(ns2dirs) { let b = ns2dirs[bi] - bi = bi + 1 + bi += 1 if len(b.vals) > 1 { push(problems, `namespace '{b.k2}' documented from multiple dirs: {join_sorted(b.vals)}`) } } bi = 0 while bi < len(id2dirs) { let b = id2dirs[bi] - bi = bi + 1 + bi += 1 if len(b.vals) > 1 { push(problems, `section id '{b.k2}' declared by multiple dirs: {join_sorted(b.vals)}`) } } bi = 0 while bi < len(title2dirs) { let b = title2dirs[bi] - bi = bi + 1 + bi += 1 if slen(b.k2) > 0 and len(b.vals) > 1 { push(problems, `section title '{b.k2}' shared by multiple dirs: {join_sorted(b.vals)}`) } } @@ -223,7 +223,7 @@ function cmd_docs_check() -> int { while i < len(thin) and i < 12 { if i > 0 { sb_puts(b, ", ") } sb_puts(b, thin[i]) - i = i + 1 + i += 1 } var tail = "" if len(thin) > 12 { tail = " …" } @@ -247,18 +247,18 @@ function cmd_docs_check() -> int { var i = 0 while i < len(targets) { let tgt = targets[i] - i = i + 1 + i += 1 if not file_exists(`{site}/{tgt}.html`) { push(problems, `highlighter links to {tgt}.html but it was not generated`) } } } # report var wi = 0 - while wi < len(warnings) { print(` warning: {warnings[wi]}`); wi = wi + 1 } + while wi < len(warnings) { print(` warning: {warnings[wi]}`); wi += 1 } if len(problems) > 0 { print("docs check FAILED:") var pi = 0 - while pi < len(problems) { print(` - {problems[pi]}`); pi = pi + 1 } + while pi < len(problems) { print(` - {problems[pi]}`); pi += 1 } return 1 } print(`docs check OK: {site} ({string(len(id2src.keys))} symbols documented)`) @@ -271,7 +271,7 @@ function collect_targets_str(o: JVal, out: []pointer) -> void { var i = 0 while i < len(o.kids) { let v = o.kids[i] - i = i + 1 + i += 1 if v.t == JV_STR { set_add(out, v.s) } } } @@ -281,7 +281,7 @@ function collect_targets_id(o: JVal, out: []pointer) -> void { var i = 0 while i < len(o.kids) { let v = o.kids[i] - i = i + 1 + i += 1 let id = j_get(v, "id") if id.t == JV_STR { set_add(out, id.s) } } diff --git a/tools/x/docgen_gen.ludic b/tools/x/docgen_gen.ludic index 63f429ce..4d0aeff8 100644 --- a/tools/x/docgen_gen.ludic +++ b/tools/x/docgen_gen.ludic @@ -33,13 +33,13 @@ function meta_new() -> Meta { } function meta_put(m: Meta, k: pointer, v: pointer) -> void { var i = 0 - while i < len(m.keys) { if m.keys[i] == k { m.vals[i] = v; return }; i = i + 1 } + while i < len(m.keys) { if m.keys[i] == k { m.vals[i] = v; return }; i += 1 } push(m.keys, k) push(m.vals, v) } function meta_get(m: Meta, k: pointer, dflt: pointer) -> pointer { var i = 0 - while i < len(m.keys) { if m.keys[i] == k { return m.vals[i] }; i = i + 1 } + while i < len(m.keys) { if m.keys[i] == k { return m.vals[i] }; i += 1 } return dflt } @@ -54,7 +54,7 @@ function read_snippet(rel: pointer) -> pointer { let s = read_file(rel) if s == null { return "" } var n = slen(s) - while n > 0 and s[n - 1] == 10 { n = n - 1 } + while n > 0 and s[n - 1] == '\n' { n -= 1 } return sslice(s, 0, n) } @@ -90,23 +90,23 @@ function parse_doc(path: pointer) -> Doc { function param_match(line: pointer, p: Param) -> bool { let n = slen(line) var i = 0 - if i < n and line[i] == 45 { i = i + 1 } else { return false } - while i < n and is_ws(line[i]) { i = i + 1 } - if i < n and line[i] == 96 { i = i + 1 } + if i < n and line[i] == '-' { i += 1 } else { return false } + while i < n and is_ws(line[i]) { i += 1 } + if i < n and line[i] == '`' { i += 1 } let ws = i - while i < n and is_word(line[i]) { i = i + 1 } + while i < n and is_word(line[i]) { i += 1 } if i == ws { return false } let name = sslice(line, ws, i) - if i < n and line[i] == 96 { i = i + 1 } - while i < n and is_ws(line[i]) { i = i + 1 } + if i < n and line[i] == '`' { i += 1 } + while i < n and is_ws(line[i]) { i += 1 } var sep = 0 while i < n { - if line[i] == 45 { i = i + 1; sep = sep + 1 } - else if (line[i] & 255) == 226 and i + 2 < n and (line[i + 1] & 255) == 128 and (line[i + 2] & 255) == 148 { i = i + 3; sep = sep + 1 } + if line[i] == '-' { i += 1; sep += 1 } + else if (line[i] & 255) == 226 and i + 2 < n and (line[i + 1] & 255) == 128 and (line[i + 2] & 255) == 148 { i += 3; sep += 1 } else { break } } if sep == 0 { return false } - while i < n and is_ws(line[i]) { i = i + 1 } + while i < n and is_ws(line[i]) { i += 1 } p.name = name p.desc = codeify(s_trim(sslice(line, i, n))) return true @@ -119,10 +119,10 @@ function split_paras(desc: pointer) -> []pointer { var start = 0 var i = 0 while i < n { - if desc[i] == 10 { + if desc[i] == '\n' { var jj = i var nl = 0 - while jj < n and is_space_all(desc[jj]) { if desc[jj] == 10 { nl = nl + 1 }; jj = jj + 1 } + while jj < n and is_space_all(desc[jj]) { if desc[jj] == '\n' { nl += 1 }; jj += 1 } if nl >= 2 { let part = s_trim(sslice(desc, start, i)) if slen(part) > 0 { push(out, part) } @@ -132,7 +132,7 @@ function split_paras(desc: pointer) -> []pointer { } i = jj } else { - i = i + 1 + i += 1 } } let last = s_trim(sslice(desc, start, n)) @@ -152,16 +152,16 @@ function split_body(body: pointer) -> Split { let p = s_index(body, "```ludic\n", i) if p < 0 { var k = i - while k < n { sb_putc(wo, body[k]); k = k + 1 } + while k < n { sb_putc(wo, body[k]); k += 1 } i = n } else { var k = i - while k < p { sb_putc(wo, body[k]); k = k + 1 } + while k < p { sb_putc(wo, body[k]); k += 1 } let cs = p + 9 let q = s_index(body, "\n```", cs) if q < 0 { var k2 = p - while k2 < cs { sb_putc(wo, body[k2]); k2 = k2 + 1 } + while k2 < cs { sb_putc(wo, body[k2]); k2 += 1 } i = cs } else { push(r.examples, sslice(body, cs, q)) @@ -191,10 +191,10 @@ function split_body(body: pointer) -> Split { if param_match(t2, pp) { push(r.params, pp) } } } else { - if not firstkeep { sb_putc(keep, 10) } + if not firstkeep { sb_putc(keep, '\n') } firstkeep = false var kk = 0 - while kk < lnlen { sb_putc(keep, ln[kk]); kk = kk + 1 } + while kk < lnlen { sb_putc(keep, ln[kk]); kk += 1 } } } # 3) desc -> paragraphs joined

, each codeified @@ -205,7 +205,7 @@ function split_body(body: pointer) -> Split { while pi < len(paras) { if pi > 0 { sb_puts(ob, "

") } sb_puts(ob, codeify(paras[pi])) - pi = pi + 1 + pi += 1 } r.desc = sb_str(ob) return r @@ -217,26 +217,26 @@ function plain_text(s: pointer) -> pointer { let n = slen(s) var i = 0 while i < n { - if s[i] == 60 { - while i < n and s[i] != 62 { i = i + 1 } - if i < n { i = i + 1 } + if s[i] == '<' { + while i < n and s[i] != '>' { i += 1 } + if i < n { i += 1 } } else { sb_putc(sb, s[i]) - i = i + 1 + i += 1 } } let t0 = s_trim(sb_str(sb)) let tn = slen(t0) - var cut = 0 - 1 + var cut = -1 var j = 0 while j < tn { let c = t0[j] - if c == 10 { break } - if (c == 46 or c == 33 or c == 63) and j >= 1 { + if c == '\n' { break } + if (c == '.' or c == '!' or c == '?') and j >= 1 { if j + 1 >= tn { cut = j + 1; break } if is_space_all(t0[j + 1]) { cut = j + 1; break } } - j = j + 1 + j += 1 } var t = t0 if cut >= 0 { t = sslice(t0, 0, cut) } @@ -245,18 +245,18 @@ function plain_text(s: pointer) -> pointer { let tl = slen(t) var cpc = 0 var bi = 0 - while bi < tl { if (t[bi] & 192) != 128 { cpc = cpc + 1 }; bi = bi + 1 } + while bi < tl { if (t[bi] & 192) != 128 { cpc += 1 }; bi += 1 } if cpc <= 160 { return t } var idx = 0 var seen = 0 while idx < tl and seen < 160 { - idx = idx + 1 - while idx < tl and (t[idx] & 192) == 128 { idx = idx + 1 } - seen = seen + 1 + idx += 1 + while idx < tl and (t[idx] & 192) == 128 { idx += 1 } + seen += 1 } let pre = sslice(t, 0, idx) var sp = slen(pre) - 1 - while sp >= 0 and pre[sp] != 32 { sp = sp - 1 } + while sp >= 0 and pre[sp] != ' ' { sp -= 1 } var head = pre if sp >= 0 { head = sslice(pre, 0, sp) } return head + "…" @@ -305,9 +305,9 @@ function entries_sort(es: []Entry) -> void { while i < len(es) { let cur = es[i] var j = i - 1 - while j >= 0 and entry_gt(es[j], cur) { es[j + 1] = es[j]; j = j - 1 } + while j >= 0 and entry_gt(es[j], cur) { es[j + 1] = es[j]; j -= 1 } es[j + 1] = cur - i = i + 1 + i += 1 } } function section_gt(a: Section, b: Section) -> bool { @@ -319,9 +319,9 @@ function sections_sort(ss: []Section) -> void { while i < len(ss) { let cur = ss[i] var j = i - 1 - while j >= 0 and section_gt(ss[j], cur) { ss[j + 1] = ss[j]; j = j - 1 } + while j >= 0 and section_gt(ss[j], cur) { ss[j + 1] = ss[j]; j -= 1 } ss[j + 1] = cur - i = i + 1 + i += 1 } } @@ -331,7 +331,7 @@ function load_sections() -> []Section { var ci = 0 while ci < len(cats) { let cat = cats[ci] - ci = ci + 1 + ci += 1 let cdir = "docs/language/" + cat if not is_dir(cdir) { continue } var sid = cat @@ -351,7 +351,7 @@ function load_sections() -> []Section { var fi = 0 while fi < len(files) { let fn = files[fi] - fi = fi + 1 + fi += 1 if not s_ends(fn, ".md") { continue } if fn == "_section.md" { continue } push(entries, build_entry(cdir + "/" + fn)) @@ -362,7 +362,7 @@ function load_sections() -> []Section { while ei < len(entries) { let e = entries[ei] if e.kind == "namespace-method" and slen(e.ns) > 0 { nsname = e.ns; break } - ei = ei + 1 + ei += 1 } let sec = new Section sec.id = sid @@ -384,7 +384,7 @@ function ns_tip(nn: pointer, sections: []Section) -> pointer { var i = 0 while i < len(sections) { if sections[i].ns_name == nn { return plain_text(sections[i].blurb) } - i = i + 1 + i += 1 } return "" } @@ -414,11 +414,11 @@ function build_symbols(sections: []Section) -> Symbols { var si = 0 while si < len(sections) { let s = sections[si] - si = si + 1 + si += 1 var ei = 0 while ei < len(s.entries) { let e = s.entries[ei] - ei = ei + 1 + ei += 1 let eid = e.id let kind = e.kind let tip = e.tip @@ -444,7 +444,7 @@ function build_symbols(sections: []Section) -> Symbols { var ti = 0 while ti < len(e.tokens_list) { let tok = e.tokens_list[ti] - ti = ti + 1 + ti += 1 if kind == "keyword" { jvput(kw, tok, jvs(eid)) } else if kind == "type" { jvput(types, tok, jvs(eid)) } else if kind == "phase" { jvput(phases, tok, jvs(eid)) } @@ -453,7 +453,7 @@ function build_symbols(sections: []Section) -> Symbols { jvput(o, "id", jvs(eid)) let pa = jva() var qi = 0 - while qi < len(e.params) { jvpush(pa, jvs(e.params[qi].name)); qi = qi + 1 } + while qi < len(e.params) { jvpush(pa, jvs(e.params[qi].name)); qi += 1 } jvput(o, "params", pa) jvput(builtins, tok, o) } @@ -462,7 +462,7 @@ function build_symbols(sections: []Section) -> Symbols { jvput(o, "id", jvs(eid)) let pa = jva() var qi = 0 - while qi < len(e.params) { jvpush(pa, jvs(e.params[qi].name)); qi = qi + 1 } + while qi < len(e.params) { jvpush(pa, jvs(e.params[qi].name)); qi += 1 } jvput(o, "params", pa) jvput(nsm, tok, o) } @@ -475,7 +475,7 @@ function build_symbols(sections: []Section) -> Symbols { var ki = 0 while ki < len(nsm.keys) { let tok = nsm.keys[ki] - ki = ki + 1 + ki += 1 let dot = s_index(tok, ".", 0) if dot >= 0 { set_add(nsnames, sslice(tok, 0, dot)) } } @@ -484,7 +484,7 @@ function build_symbols(sections: []Section) -> Symbols { var ni = 0 while ni < len(nsnames) { let nn = nsnames[ni] - ni = ni + 1 + ni += 1 let pg = "ns-" + lower_ascii(nn) jvput(namespaces, nn, jvs(pg)) let cd = jvo() @@ -511,7 +511,7 @@ function js_get(v: JVal, key: pointer) -> pointer { function j_has(v: JVal, key: pointer) -> bool { if v.t != JV_OBJ { return false } var i = 0 - while i < len(v.keys) { if v.keys[i] == key { return true }; i = i + 1 } + while i < len(v.keys) { if v.keys[i] == key { return true }; i += 1 } return false } @@ -527,7 +527,7 @@ function nav_html(b: Sb, links: JVal, cta_href: pointer) -> void { var i = 0 while i < len(links.kids) { let nlk = links.kids[i] - i = i + 1 + i += 1 let href = js_get(nlk, "href") let label = js_get(nlk, "label") if href == cta_href { sb_puts(b, `{hesc(label)}`) } @@ -551,7 +551,7 @@ function render_params(e: Entry) -> pointer { var i = 0 while i < len(e.params) { let p = e.params[i] - i = i + 1 + i += 1 sb_puts(b, `

{hesc(p.name)}{p.desc}
`) } sb_puts(b, ``) @@ -564,7 +564,7 @@ function render_examples(e: Entry) -> pointer { var i = 0 while i < len(e.examples) { sb_puts(b, `
{hesc(e.examples[i])}
`) - i = i + 1 + i += 1 } sb_puts(b, ``) return sb_str(b) @@ -575,11 +575,11 @@ function render_related(e: Entry, items: JVal) -> pointer { var i = 0 while i < len(e.related_list) { let r = e.related_list[i] - i = i + 1 + i += 1 let it = j_get(items, r) if it.t == JV_NULL { continue } if cnt == 0 { sb_puts(b, `