refactor(lang): rename the fn keyword to function
Expand the function-declaration keyword to the full word across the whole
language and toolchain:
fn name(...) -> T { ... } -> function name(...) -> T { ... }
Done as a self-hosting migration: teach the parser both spellings, reseed,
rewrite every .ludic definition to `function`, then drop `fn`. The compiler
now rejects `fn`. Touches the parser, all selfhost/tools/runtime/example/test
sources, the grammars (TextMate shared+vscode, ludic_syntax.h, JetBrains
LudicTokens.kt), the LSP and formatter, the Python doc/vocab tools
(check-impl, check-docs, validate, palette, test-lsp), and the docs
(fences, prose, kw-fn -> kw-function).
Reseeded; C-free bootstrap fixpoint holds. All suites green (45 regression,
24 self-host, 29 tool); the docs site generates and check.py passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2f19c8d8e2
commit
4c48077d68
86 changed files with 793 additions and 793 deletions
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
# compile a Ludic source to a native binary in bin/ via ludicc + clang (-O2).
|
||||
# Returns true on success. Used for x itself and the editor tools.
|
||||
fn build_tool(name: ptr, src: ptr) -> bool {
|
||||
function build_tool(name: ptr, src: ptr) -> bool {
|
||||
let ll = `build/{name}.ll`
|
||||
if not shq(`bin/ludicc {src} --emit-llvm -o {ll} 2>/dev/null`) { print(`build failed: {name} (compile)`); return false }
|
||||
# write to a temp then move, so a running bin/x can rebuild itself in place
|
||||
|
|
@ -18,7 +18,7 @@ fn build_tool(name: ptr, src: ptr) -> bool {
|
|||
# Both ludicc and ludic are the SAME multi-call binary assembled from the IR seed
|
||||
# with clang alone; invoked as `ludic` it compiles-and-runs, as `ludicc` it just
|
||||
# compiles.
|
||||
fn cmd_build_cli() -> int {
|
||||
function cmd_build_cli() -> int {
|
||||
run("mkdir -p bin build")
|
||||
print("cc: selfhost/ludicc.seed.ll -> bin/ludicc, bin/ludic (from the IR seed, no C compiler)")
|
||||
if not shq(`{cc()} selfhost/ludicc.seed.ll -o bin/ludicc`) { err("x: seed did not assemble\n"); return 1 }
|
||||
|
|
@ -28,7 +28,7 @@ fn cmd_build_cli() -> int {
|
|||
}
|
||||
|
||||
# ---- build: the whole toolchain (ludicc, ludic, x, ludic-fmt, ludic-lsp) -----
|
||||
fn cmd_build() -> int {
|
||||
function cmd_build() -> int {
|
||||
if cmd_build_cli() != 0 { return 1 }
|
||||
print("ludicc: tools/x/main.ludic -> bin/x (the task runner rebuilds itself)")
|
||||
if not build_tool("x", "tools/x/main.ludic") { return 1 }
|
||||
|
|
@ -44,7 +44,7 @@ fn cmd_build() -> int {
|
|||
# ---- app: compile a Ludic program to a native app ----------------------------
|
||||
# usage: x app <file.ludic> [--headless|--windowed] [--save-temps]
|
||||
# windowed is the default for a game; headless renders the last frame to out.ppm.
|
||||
fn cmd_app() -> int {
|
||||
function cmd_app() -> int {
|
||||
ensure_ludicc()
|
||||
var src = null
|
||||
var mode = 1 # 1 = windowed, 2 = headless
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ program X {
|
|||
import "selfhost_test.ludic"
|
||||
import "test.ludic"
|
||||
|
||||
fn usage() -> void {
|
||||
function usage() -> void {
|
||||
print("x — the Ludic task runner (run from the repository root)")
|
||||
print("")
|
||||
print("build & run:")
|
||||
|
|
@ -43,7 +43,7 @@ program X {
|
|||
}
|
||||
|
||||
# a positional argument, or a default when absent
|
||||
fn argn(i: int, dflt: ptr) -> ptr {
|
||||
function argn(i: int, dflt: ptr) -> ptr {
|
||||
if (i < arg_count()) { return arg(i) }
|
||||
return dflt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
# ---- file IO ----------------------------------------------------------------
|
||||
|
||||
# read a whole file into a fresh NUL-terminated buffer (null if it cannot open)
|
||||
fn read_file(path: ptr) -> ptr {
|
||||
function read_file(path: ptr) -> ptr {
|
||||
let f = file_open(path, "rb")
|
||||
if (f == null) { return null }
|
||||
file_seek(f, 0, 2)
|
||||
|
|
@ -27,7 +27,7 @@ fn read_file(path: ptr) -> ptr {
|
|||
}
|
||||
|
||||
# overwrite `path` with `s`; returns false if it could not be opened
|
||||
fn write_file(path: ptr, s: ptr) -> bool {
|
||||
function write_file(path: ptr, s: ptr) -> bool {
|
||||
let f = file_open(path, "wb")
|
||||
if (f == null) { return false }
|
||||
file_write(f, s, len(s))
|
||||
|
|
@ -35,22 +35,22 @@ fn write_file(path: ptr, s: ptr) -> bool {
|
|||
return true
|
||||
}
|
||||
|
||||
fn file_exists(path: ptr) -> bool { return shq(`test -e {path}`) }
|
||||
fn is_exec(path: ptr) -> bool { return shq(`test -x {path}`) }
|
||||
function file_exists(path: ptr) -> bool { return shq(`test -e {path}`) }
|
||||
function is_exec(path: ptr) -> bool { return shq(`test -x {path}`) }
|
||||
# is `a` newer than `b` (like the shell's `-nt`)?
|
||||
fn newer(a: ptr, b: ptr) -> bool { return shq(`test {a} -nt {b}`) }
|
||||
function newer(a: ptr, b: ptr) -> bool { return shq(`test {a} -nt {b}`) }
|
||||
|
||||
# ---- process control --------------------------------------------------------
|
||||
|
||||
# `run` returns the raw wait status; the program's exit code is the high byte.
|
||||
fn exit_code(st: int) -> int { return (st >> 8) & 255 }
|
||||
function exit_code(st: int) -> int { return (st >> 8) & 255 }
|
||||
# run a command, returning its exit code (0 = success)
|
||||
fn sh(cmd: ptr) -> int { return exit_code(run(cmd)) }
|
||||
function sh(cmd: ptr) -> int { return exit_code(run(cmd)) }
|
||||
# run a command, true when it succeeded
|
||||
fn shq(cmd: ptr) -> bool { return exit_code(run(cmd)) == 0 }
|
||||
function shq(cmd: ptr) -> bool { return exit_code(run(cmd)) == 0 }
|
||||
|
||||
# run `cmd` and return its stdout (stderr discarded). Never null.
|
||||
fn capture(cmd: ptr) -> ptr {
|
||||
function capture(cmd: ptr) -> ptr {
|
||||
let tmp = "/tmp/x_capture.out"
|
||||
run(`{cmd} > {tmp} 2>/dev/null`)
|
||||
let s = read_file(tmp)
|
||||
|
|
@ -60,16 +60,16 @@ fn capture(cmd: ptr) -> ptr {
|
|||
|
||||
# run `cmd`, join its output lines with single spaces and trim — the Ludic twin
|
||||
# of the shell idiom `$(cmd | tr '\n' ' ' | sed 's/ *$//')`.
|
||||
fn capture_line(cmd: ptr) -> ptr {
|
||||
function capture_line(cmd: ptr) -> ptr {
|
||||
return capture(`{cmd} | tr '\n' ' ' | sed 's/ *$//'`)
|
||||
}
|
||||
|
||||
# the last line of a command's output (for one-line error messages)
|
||||
fn capture_tail(cmd: ptr) -> ptr {
|
||||
function capture_tail(cmd: ptr) -> ptr {
|
||||
return capture(`{cmd} 2>&1 | tail -1`)
|
||||
}
|
||||
|
||||
fn getenv_or(name: ptr, dflt: ptr) -> ptr {
|
||||
function getenv_or(name: ptr, dflt: ptr) -> ptr {
|
||||
let v = getenv(name)
|
||||
if (v == null) { return dflt }
|
||||
return v
|
||||
|
|
@ -78,40 +78,40 @@ fn getenv_or(name: ptr, dflt: ptr) -> ptr {
|
|||
# ---- stdout helpers ---------------------------------------------------------
|
||||
|
||||
# write `s` with no trailing newline (print() always adds one)
|
||||
fn out(s: ptr) -> void { file_write(file_stdout(), s, len(s)) }
|
||||
fn err(s: ptr) -> void { file_write(file_stderr(), s, len(s)) }
|
||||
function out(s: ptr) -> void { file_write(file_stdout(), s, len(s)) }
|
||||
function err(s: ptr) -> void { file_write(file_stderr(), s, len(s)) }
|
||||
|
||||
# an ESC byte — the lexer has no \033, so build it by hand
|
||||
fn esc() -> ptr { let b = bytes(2); b[0] = 27; b[1] = 0; return b }
|
||||
fn c_green() -> ptr { return esc() + "[32m" }
|
||||
fn c_red() -> ptr { return esc() + "[31m" }
|
||||
fn c_reset() -> ptr { return esc() + "[0m" }
|
||||
function esc() -> ptr { let b = bytes(2); b[0] = 27; b[1] = 0; return b }
|
||||
function c_green() -> ptr { return esc() + "[32m" }
|
||||
function c_red() -> ptr { return esc() + "[31m" }
|
||||
function c_reset() -> ptr { return esc() + "[0m" }
|
||||
|
||||
# ---- the PASS/FAIL test harness ---------------------------------------------
|
||||
|
||||
var PASS: int = 0
|
||||
var FAIL: int = 0
|
||||
|
||||
fn ok(msg: ptr) -> void {
|
||||
function ok(msg: ptr) -> void {
|
||||
PASS = PASS + 1
|
||||
print(` {c_green()}PASS{c_reset()} {msg}`)
|
||||
}
|
||||
fn bad(msg: ptr) -> void {
|
||||
function bad(msg: ptr) -> void {
|
||||
FAIL = FAIL + 1
|
||||
print(` {c_red()}FAIL{c_reset()} {msg}`)
|
||||
}
|
||||
fn bad2(msg: ptr, detail: ptr) -> void {
|
||||
function bad2(msg: ptr, detail: ptr) -> void {
|
||||
bad(msg)
|
||||
print(` {detail}`)
|
||||
}
|
||||
# assert two strings equal, reporting the mismatch
|
||||
fn check(label: ptr, got: ptr, want: ptr) -> void {
|
||||
function check(label: ptr, got: ptr, want: ptr) -> void {
|
||||
if (got == want) { ok(label) }
|
||||
else { bad2(label, `expected [{want}] got [{got}]`) }
|
||||
}
|
||||
|
||||
# print the "== N passed, M failed ==" footer and return the process exit code
|
||||
fn report() -> int {
|
||||
function report() -> int {
|
||||
print("")
|
||||
print(`== {str(PASS)} passed, {str(FAIL)} failed ==`)
|
||||
if (FAIL == 0) { return 0 }
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@
|
|||
# now lives here once, in selfhost_frags().
|
||||
|
||||
# the C toolchain driver (clang) that assembles and links the emitted IR
|
||||
fn cc() -> ptr { return getenv_or("LUDIC_CC", "clang") }
|
||||
function cc() -> ptr { return getenv_or("LUDIC_CC", "clang") }
|
||||
|
||||
# the self-host compiler's source fragments, in link order. This is THE list;
|
||||
# the old scripts each carried their own copy.
|
||||
fn selfhost_frags() -> []ptr {
|
||||
function selfhost_frags() -> []ptr {
|
||||
let f = new []ptr
|
||||
push(f, "selfhost/str.ludic")
|
||||
push(f, "selfhost/buf.ludic")
|
||||
|
|
@ -52,7 +52,7 @@ fn selfhost_frags() -> []ptr {
|
|||
|
||||
# concatenate the fragments into `program SelfHost { ... }` at `outpath`.
|
||||
# This is the pure-Ludic equivalent of the scripts' `{ echo; for; cat; }` block.
|
||||
fn write_selfhost_src(outpath: ptr) -> bool {
|
||||
function write_selfhost_src(outpath: ptr) -> bool {
|
||||
let f = file_open(outpath, "wb")
|
||||
if (f == null) { return false }
|
||||
let hdr = "program SelfHost {\n"
|
||||
|
|
@ -72,13 +72,13 @@ fn write_selfhost_src(outpath: ptr) -> bool {
|
|||
}
|
||||
|
||||
# count the lines in a file (for the progress notes the scripts printed)
|
||||
fn line_count(path: ptr) -> ptr {
|
||||
function line_count(path: ptr) -> ptr {
|
||||
return capture_line(`wc -l < {path}`)
|
||||
}
|
||||
|
||||
# ensure bin/ludicc exists and is current with the seed. Built from the checked-in
|
||||
# IR seed with clang alone — no C compiler is ever involved.
|
||||
fn ensure_ludicc() -> void {
|
||||
function ensure_ludicc() -> void {
|
||||
run("mkdir -p bin build")
|
||||
if (not is_exec("bin/ludicc")) or newer("selfhost/ludicc.seed.ll", "bin/ludicc") {
|
||||
print("cc: selfhost/ludicc.seed.ll -> bin/ludicc (from the IR seed, no C compiler)")
|
||||
|
|
@ -90,7 +90,7 @@ fn ensure_ludicc() -> void {
|
|||
|
||||
# ---- selfhost-build: assemble + compile the self-host compiler ---------------
|
||||
# usage: x selfhost-build [ludicc] [outbin] (defaults: bin/ludicc, build/selfhost)
|
||||
fn cmd_selfhost_build(lc: ptr, outbin: ptr) -> int {
|
||||
function cmd_selfhost_build(lc: ptr, outbin: ptr) -> int {
|
||||
run("mkdir -p build")
|
||||
let src = "build/selfhost.ludic"
|
||||
if not write_selfhost_src(src) { err("x: cannot write build/selfhost.ludic\n"); return 1 }
|
||||
|
|
@ -104,7 +104,7 @@ fn cmd_selfhost_build(lc: ptr, outbin: ptr) -> int {
|
|||
|
||||
# ---- sh-compile: compile one .ludic with a given selfhost binary and link ----
|
||||
# usage: x sh-compile <selfhost-binary> <input.ludic> <output-binary>
|
||||
fn cmd_sh_compile(shbin: ptr, in: ptr, outbin: ptr) -> int {
|
||||
function cmd_sh_compile(shbin: ptr, in: ptr, outbin: ptr) -> int {
|
||||
if not shq(`{shbin} {in} > {outbin}.ll`) { return 1 }
|
||||
if not shq(`{cc()} {outbin}.ll -o {outbin}`) { return 1 }
|
||||
return 0
|
||||
|
|
@ -116,7 +116,7 @@ fn cmd_sh_compile(shbin: ptr, in: ptr, outbin: ptr) -> int {
|
|||
|
||||
# the quiet core, reused by the test suites; returns true on success. On failure
|
||||
# the self-host/link diagnostics are left in /tmp/x_gb.err.
|
||||
fn game_build_ok(shbin: ptr, game: ptr, outbin: ptr) -> bool {
|
||||
function game_build_ok(shbin: ptr, game: ptr, outbin: ptr) -> bool {
|
||||
let ll = `{outbin}.ll`
|
||||
if not shq(`{shbin} {game} > {ll} 2>/tmp/x_gb.err`) { return false }
|
||||
if not shq(`{cc()} -O2 {ll} -o {outbin} 2>/tmp/x_gb.err`) { return false }
|
||||
|
|
@ -125,7 +125,7 @@ fn game_build_ok(shbin: ptr, game: ptr, outbin: ptr) -> bool {
|
|||
}
|
||||
|
||||
# usage: x game-build <selfhost-binary> <game.ludic> <out-binary>
|
||||
fn cmd_game_build(shbin: ptr, game: ptr, outbin: ptr) -> int {
|
||||
function cmd_game_build(shbin: ptr, game: ptr, outbin: ptr) -> int {
|
||||
if game_build_ok(shbin, game, outbin) { print(`built {outbin}`); return 0 }
|
||||
print("build failed:")
|
||||
out(capture("grep -i error /tmp/x_gb.err | head -5"))
|
||||
|
|
@ -136,7 +136,7 @@ fn cmd_game_build(shbin: ptr, game: ptr, outbin: ptr) -> int {
|
|||
# stage0 bin/ludicc compiles the self-host source -> gen1 (built by a different
|
||||
# compiler, so its IR legitimately differs); gen1 -> gen2, gen2 -> gen3, and
|
||||
# gen2.ll == gen3.ll is the fixpoint that proves the compiler reproduces itself.
|
||||
fn cmd_bootstrap() -> int {
|
||||
function cmd_bootstrap() -> int {
|
||||
ensure_ludicc()
|
||||
run("mkdir -p build/boot")
|
||||
if cmd_selfhost_build("bin/ludicc", "build/boot/gen1") != 0 { print("FAIL: stage0 build"); return 1 }
|
||||
|
|
@ -163,7 +163,7 @@ fn cmd_bootstrap() -> int {
|
|||
# The seed is the compiler's own IR, a proven fixed point: clang assembles it,
|
||||
# the seed-built compiler compiles its own source, and the result must equal the
|
||||
# seed byte for byte.
|
||||
fn cmd_bootstrap_cfree() -> int {
|
||||
function cmd_bootstrap_cfree() -> int {
|
||||
run("mkdir -p build/cfree")
|
||||
if not shq(`{cc()} selfhost/ludicc.seed.ll -o build/cfree/sh_seed 2>/dev/null`) { print("FAIL: assemble seed"); return 1 }
|
||||
print(" seed.ll --clang--> sh_seed (no C compiler used)")
|
||||
|
|
@ -184,7 +184,7 @@ fn cmd_bootstrap_cfree() -> int {
|
|||
# fixed point of the NEW compiler, not a one-step image of the old. Falls back to
|
||||
# bin/ludicc only if the seed itself no longer assembles. Verify with
|
||||
# `x bootstrap-cfree` afterwards.
|
||||
fn cmd_reseed() -> int {
|
||||
function cmd_reseed() -> int {
|
||||
run("mkdir -p build/cfree")
|
||||
if not write_selfhost_src("build/cfree/selfhost.ludic") { print("FAIL: write source"); return 1 }
|
||||
if shq(`{cc()} selfhost/ludicc.seed.ll -o build/cfree/sh_old 2>/dev/null`) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# fixpoint). Replaces selfhost/test.sh.
|
||||
|
||||
# compile selfhost/tests/<name>.ludic with bin/ludicc, assemble, run, compare
|
||||
fn sh_case(name: ptr, exp: ptr) -> void {
|
||||
function sh_case(name: ptr, exp: ptr) -> void {
|
||||
let ll = `/tmp/x_sh_{name}.ll`
|
||||
let er = `/tmp/x_sh_{name}.err`
|
||||
let bn = `/tmp/x_sh_{name}`
|
||||
|
|
@ -16,14 +16,14 @@ fn sh_case(name: ptr, exp: ptr) -> void {
|
|||
}
|
||||
|
||||
# compile examples/<name>.ludic as a game and diff its render against the golden
|
||||
fn game_case(name: ptr, keys: ptr) -> void {
|
||||
function game_case(name: ptr, keys: ptr) -> void {
|
||||
if not game_build_ok("bin/ludicc", `examples/{name}.ludic`, `/tmp/x_g_{name}`) { bad(`{name}: build`); return }
|
||||
run(`printf '%s' '{keys}' | /tmp/x_g_{name} >/dev/null 2>&1`)
|
||||
if shq(`cmp -s selfhost/golden/{name}.ppm out.ppm`) { ok(`{name} matches the golden render`) }
|
||||
else { bad(`{name}: differs from golden`) }
|
||||
}
|
||||
|
||||
fn cmd_selfhost_test() -> int {
|
||||
function cmd_selfhost_test() -> int {
|
||||
PASS = 0
|
||||
FAIL = 0
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
# compile a feature example as a game, run it (optional keys on stdin) and match
|
||||
# its stdout against the expected space-joined output.
|
||||
fn feat_case(name: ptr, keys: ptr, exp: ptr, label: ptr) -> void {
|
||||
function feat_case(name: ptr, keys: ptr, exp: ptr, label: ptr) -> void {
|
||||
if not game_build_ok("bin/ludicc", `examples/{name}.ludic`, `/tmp/x_e_{name}`) {
|
||||
bad2(label, capture_line("grep -i error /tmp/x_gb.err | head -1")); return
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ fn feat_case(name: ptr, keys: ptr, exp: ptr, label: ptr) -> void {
|
|||
}
|
||||
|
||||
# a "does it still compile" smoke test (parse -> lower -> link), no run
|
||||
fn qsmoke(name: ptr) -> void {
|
||||
function qsmoke(name: ptr) -> void {
|
||||
if game_build_ok("bin/ludicc", `examples/{name}.ludic`, `/tmp/x_q_{name}`) {
|
||||
ok(`{name} compiles (@Queries desugars to S_QUERY)`)
|
||||
} else { bad2(name, capture_line("grep -i error /tmp/x_gb.err | head -1")) }
|
||||
|
|
@ -28,7 +28,7 @@ fn qsmoke(name: ptr) -> void {
|
|||
|
||||
# a pure-Ludic example that drives and asserts itself: compile headless to IR,
|
||||
# let clang assemble it, run, and compare stdout. No C host and no C compiled.
|
||||
fn net_case(name: ptr, exp: ptr) -> void {
|
||||
function net_case(name: ptr, exp: ptr) -> void {
|
||||
let ll = `/tmp/x_n_{name}.ll`
|
||||
let log = `/tmp/x_n_{name}.out`
|
||||
if not shq(`bin/ludicc --headless examples/{name}.ludic --emit-llvm -o {ll} > {log} 2>&1`) {
|
||||
|
|
@ -43,7 +43,7 @@ fn net_case(name: ptr, exp: ptr) -> void {
|
|||
if (got == exp) { ok(`{name} ({got})`) } else { bad2(name, `got [{got}] want [{exp}]`) }
|
||||
}
|
||||
|
||||
fn cmd_test() -> int {
|
||||
function cmd_test() -> int {
|
||||
PASS = 0
|
||||
FAIL = 0
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# driven through python3/node (they are not project shell scripts), invoked here.
|
||||
|
||||
# refresh one editor-copy of a shared grammar file if it has drifted
|
||||
fn sync_one(shared: ptr, dst: ptr) -> void {
|
||||
function sync_one(shared: ptr, dst: ptr) -> void {
|
||||
if not shq(`cmp -s {shared} {dst}`) {
|
||||
run(`cp {shared} {dst}`)
|
||||
print(`sync: {shared} -> {dst}`)
|
||||
|
|
@ -16,7 +16,7 @@ fn sync_one(shared: ptr, dst: ptr) -> void {
|
|||
|
||||
# VS Code needs the grammar inside its own extension directory, so it gets a
|
||||
# copy that build refreshes rather than trusts (test-tools asserts they match).
|
||||
fn sync_vscode_grammar() -> void {
|
||||
function sync_vscode_grammar() -> void {
|
||||
if not shq("test -d tools/editors/vscode") { return }
|
||||
run("mkdir -p tools/editors/vscode/syntaxes")
|
||||
sync_one("tools/editors/shared/ludic.tmLanguage.json", "tools/editors/vscode/syntaxes/ludic.tmLanguage.json")
|
||||
|
|
@ -25,7 +25,7 @@ fn sync_vscode_grammar() -> void {
|
|||
}
|
||||
|
||||
# is `flag` present anywhere in argv?
|
||||
fn has_flag(flag: ptr) -> bool {
|
||||
function has_flag(flag: ptr) -> bool {
|
||||
var i = 2
|
||||
while i < arg_count() { if (arg(i) == flag) { return true }; i = i + 1 }
|
||||
return false
|
||||
|
|
@ -33,7 +33,7 @@ fn has_flag(flag: ptr) -> bool {
|
|||
|
||||
# ---- tools: build the editor toolchain --------------------------------------
|
||||
# usage: x tools [--install] [--test]
|
||||
fn cmd_tools() -> int {
|
||||
function cmd_tools() -> int {
|
||||
ensure_ludicc()
|
||||
print("ludicc: tools/ludic-tools/fmt.ludic -> bin/ludic-fmt (Ludic -> LLVM IR -> binary, no C)")
|
||||
if not build_tool("ludic-fmt", "tools/ludic-tools/fmt.ludic") { return 1 }
|
||||
|
|
@ -53,7 +53,7 @@ fn cmd_tools() -> int {
|
|||
}
|
||||
|
||||
# ---- test-tools: the editor-toolchain regression suite -----------------------
|
||||
fn cmd_test_tools() -> int {
|
||||
function cmd_test_tools() -> int {
|
||||
PASS = 0
|
||||
FAIL = 0
|
||||
let fmt = "bin/ludic-fmt"
|
||||
|
|
@ -172,13 +172,13 @@ fn cmd_test_tools() -> int {
|
|||
return report()
|
||||
}
|
||||
|
||||
fn test_json(path: ptr) -> void {
|
||||
function test_json(path: ptr) -> void {
|
||||
let bn = capture_line(`basename {path}`)
|
||||
if shq(`python3 -c 'import json,sys;json.load(open(sys.argv[1]))' {path} 2>/dev/null`) {
|
||||
ok(`valid JSON: {bn}`)
|
||||
} else { bad(`invalid JSON: {path}`) }
|
||||
}
|
||||
fn test_xml(path: ptr) -> void {
|
||||
function test_xml(path: ptr) -> void {
|
||||
let bn = capture_line(`basename {path}`)
|
||||
if shq(`python3 -c 'import xml.dom.minidom,sys;xml.dom.minidom.parse(sys.argv[1])' {path} 2>/dev/null`) {
|
||||
ok(`valid XML: {bn}`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue