From f1eed2d4e08bb5eb61e736370f8b664836d644bf Mon Sep 17 00:00:00 2001 From: Adrien Jaguenet Date: Tue, 23 Jun 2026 01:07:08 +0200 Subject: [PATCH] refactor: extract pure codegen into shared module, remove duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create codegen/index.js with pure branchDescendants, emitBranch, generateCallWithError, and generateCFile — zero DOM dependencies - blocks-proc.js adapters delegate to CodeGen with browser instanceof check - run.js uses the same CodeGen module instead of duplicating block classes with their own branchDescendants/emitBranch logic - Load codegen/index.js in browser before blocks-proc.js --- blocks-proc.js | 57 ++---- codegen/index.js | 150 ++++++++++++++ index.html | 1 + tests/compile/run.js | 469 ++++++++++++++++++------------------------- 4 files changed, 364 insertions(+), 313 deletions(-) create mode 100644 codegen/index.js diff --git a/blocks-proc.js b/blocks-proc.js index c7d8844..d5e6420 100644 --- a/blocks-proc.js +++ b/blocks-proc.js @@ -168,49 +168,27 @@ function _specDataType(spec) { } /** - * Collects all blocks reachable from the given root blocks via wcNextFlow, - * excluding the interior of decision blocks (decisions handle their own branching). - * Used by WCProcCall._generateCallWithError to compute shared vs exclusive branches. + * Adapter: wraps CodeGen.branchDescendants with the browser's instanceof check. * @param {HTMLElement[]} roots * @returns {Set} */ function _branchDescendants(roots) { - const d = new Set(), q = [...roots]; - for (const el of q) { - if (d.has(el)) continue; - d.add(el); - if (el instanceof WCDecision) continue; - for (const p of el.wcNextFlow) { - if (!d.has(p.toElement)) q.push(p.toElement); - } - } - return d; + return CodeGen.branchDescendants(roots, function (b) { + return b instanceof WCDecision; + }); } /** - * Emits C code for a branch by walking from root blocks, stopping at shared blocks - * and decision blocks (which emit their own branching). Uses visited sets to prevent - * double-emission when shared code is later emitted separately. + * Adapter: wraps CodeGen.emitBranch with the browser's instanceof check. * @param {HTMLElement[]} roots - * @param {Set} stop blocks to exclude (shared between branches) - * @param {Set} visited blocks already emitted + * @param {Set} stop + * @param {Set} visited * @returns {string} */ function _emitBranch(roots, stop, visited) { - const v = new Set(visited); - let c = ""; - const walk = el => { - if (v.has(el) || stop.has(el)) return; - v.add(el); - if (el instanceof WCDecision) { - c += el.generateCode(v); - } else { - c += el.generateOwnCode(); - for (const p of el.wcNextFlow) walk(p.toElement); - } - }; - for (const el of roots) walk(el); - return c; + return CodeGen.emitBranch(roots, stop, visited, function (b) { + return b instanceof WCDecision; + }); } /** Procedure call block with dynamic argument fields, format-string support, and error-handling hooks. Lives in the Algorithm tab. */ @@ -605,18 +583,9 @@ class WCProcCall extends WCBlock { .filter(p => p.fromHook !== this._el.errHook) .map(p => p.toElement); - const errAll = _branchDescendants(errTargets); - const okAll = _branchDescendants(okTargets); - const shared = new Set([...errAll].filter(el => okAll.has(el))); - - const errCode = _emitBranch(errTargets, shared, new Set()); - const okCode = _emitBranch(okTargets, shared, new Set()); - const sharedCode = _emitBranch([...shared], new Set(), new Set()); - - let out = `${ifHeader} {\n${errCode}}\n`; - if (okCode) out += okCode; - if (sharedCode) out += sharedCode; - return out; + return CodeGen.generateCallWithError(ifHeader, errTargets, okTargets, function (b) { + return b instanceof WCDecision; + }); } saveData() { diff --git a/codegen/index.js b/codegen/index.js new file mode 100644 index 0000000..cb37646 --- /dev/null +++ b/codegen/index.js @@ -0,0 +1,150 @@ +// ── Pure C code generation — no DOM dependencies ───────────────────────── +// +// These functions operate on generic block objects with the interface: +// { generateOwnCode(): string, +// generateCode(visited: Set): string, +// wcNextFlow?: [{ toElement: block }], +// nextFlow?: [{ toElement: block }] } +// +// Browser DOM blocks use wcNextFlow; test/mock blocks use nextFlow. +// Decision blocks must expose isDecision = true. + +(function () { + "use strict"; + + var CodeGen = {}; + + /** + * Collects all blocks reachable from the given roots via nextFlow, + * excluding the interior of decision blocks (they handle their own + * branching). Returns a Set of block references. + * @param {Array} roots + * @param {function(block): boolean} [isDecision] + * @returns {Set} + */ + CodeGen.branchDescendants = function (roots, isDecision) { + isDecision = + isDecision || + function (b) { + return b.isDecision; + }; + var d = new Set(), + q = roots.slice(); + for (var i = 0; i < q.length; i++) { + var el = q[i]; + if (d.has(el)) continue; + d.add(el); + if (isDecision(el)) continue; + var flow = el.wcNextFlow || el.nextFlow || []; + for (var j = 0; j < flow.length; j++) { + var target = flow[j].toElement; + if (target && !d.has(target)) q.push(target); + } + } + return d; + }; + + /** + * Emits C code for a branch by walking from root blocks, stopping at + * blocks in the stop set and at decision boundaries. Each visited block + * is added to the visited set to prevent double-emission. + * @param {Array} roots + * @param {Set} stop blocks to exclude (shared between branches) + * @param {Set} visited blocks already emitted + * @param {function(block): boolean} [isDecision] + * @returns {string} + */ + CodeGen.emitBranch = function (roots, stop, visited, isDecision) { + isDecision = + isDecision || + function (b) { + return b.isDecision; + }; + var v = new Set(visited); + var parts = []; + + function walk(el) { + if (v.has(el) || (stop && stop.has(el))) return; + v.add(el); + if (isDecision(el)) { + parts.push(el.generateCode(v)); + } else { + parts.push(el.generateOwnCode()); + var flow = el.wcNextFlow || el.nextFlow || []; + for (var j = 0; j < flow.length; j++) { + walk(flow[j].toElement); + } + } + } + + for (var i = 0; i < roots.length; i++) walk(roots[i]); + return parts.join(""); + }; + + /** + * Generates the error-path branch for a proc call: computes shared blocks + * between error and success paths, emits error-exclusive code inside `if`, + * then success-exclusive and shared code after it (no else). + * @param {string} ifHeader e.g. 'if (func() != 0)' or 'if ((r=f()) != 0)' + * @param {Array} errTargets root blocks reachable from the error hook + * @param {Array} okTargets root blocks reachable from the success hook + * @param {function(block): boolean} [isDecision] + * @returns {string} + */ + CodeGen.generateCallWithError = function ( + ifHeader, + errTargets, + okTargets, + isDecision + ) { + var errAll = CodeGen.branchDescendants(errTargets, isDecision); + var okAll = CodeGen.branchDescendants(okTargets, isDecision); + var shared = new Set(); + errAll.forEach(function (b) { + if (okAll.has(b)) shared.add(b); + }); + + var errCode = CodeGen.emitBranch(errTargets, shared, new Set(), isDecision); + var okCode = CodeGen.emitBranch(okTargets, shared, new Set(), isDecision); + var sharedCode = CodeGen.emitBranch( + Array.from(shared), + new Set(), + new Set(), + isDecision + ); + + var out = ifHeader + " {\n" + errCode + "}\n"; + if (okCode) out += okCode; + if (sharedCode) out += sharedCode; + return out; + }; + + /** + * Assembles a complete C source file from a program's entry block, + * include directives, and variable declarations. + * @param {Object} entryBlock the first block in the flow (e.g. START) + * @param {string[]} includes #include directives + * @param {string} declarations variable declarations (already indented) + * @returns {string} + */ + CodeGen.generateCFile = function (entryBlock, includes, declarations) { + var lines = ["// --- WiseCode\u00ae ---"]; + for (var i = 0; i < includes.length; i++) lines.push(includes[i]); + lines.push(""); + lines.push("int main() {"); + if (declarations) lines.push(declarations); + var body = entryBlock.generateCode(new Set()); + lines.push(body); + lines.push("}"); + lines.push("// --- END ---"); + return lines.join("\n"); + }; + + // ── Export ────────────────────────────────────────────────────────── + if (typeof module !== "undefined" && module.exports) { + module.exports = CodeGen; + } + if (typeof window !== "undefined") { + window.CodeGen = CodeGen; + } +})(); diff --git a/index.html b/index.html index fdaa6be..ceaaa56 100644 --- a/index.html +++ b/index.html @@ -521,6 +521,7 @@ + diff --git a/tests/compile/run.js b/tests/compile/run.js index ab1b371..34bc8c6 100644 --- a/tests/compile/run.js +++ b/tests/compile/run.js @@ -3,324 +3,258 @@ * C compilation test runner. * * Generates C source files matching each template's expected output, - * then runs `make` to compile them with `gcc -Wall -Wextra -Werror -fsyntax-only`. + * then runs `make` to compile them with `gcc -Wall -Wextra -fsyntax-only`. * - * Code generation is not hardcoded — each source is built from block-like - * data structures walked by the same algorithm the browser templates use. + * Block data objects are created manually (matching the browser template + * structure) and passed to the shared CodeGen module for graph walking + * and code emission — no duplication of branch/emit logic. */ const fs = require("fs"); const path = require("path"); const { execSync } = require("child_process"); +const CodeGen = require("../../codegen/index.js"); const OUTDIR = "/tmp/wisecode-compile"; const MKFILE = path.join(__dirname, "Makefile"); -// ── Branch analysis (mirrors blocks-proc.js _branchDescendants / _emitBranch) ── +// ── Block data helpers ──────────────────────────────────────────────── +// Plain objects matching the interface CodeGen expects: +// { generateOwnCode(), generateCode(visited), nextFlow, isDecision } -function branchDescendants(roots) { - const d = new Set(), q = [...roots]; - for (const el of q) { - if (d.has(el)) continue; - d.add(el); - if (el.type === "decision") continue; - for (const p of (el.nextFlow || [])) { - if (!d.has(p.toElement)) q.push(p.toElement); - } - } - return d; -} - -function emitBranch(roots, stop, visited) { - const v = new Set(visited); - const lines = []; - const walk = (el) => { - if (v.has(el) || stop.has(el)) return; - v.add(el); - if (el.type === "decision") { - lines.push(el.generateCode(v)); - } else { - lines.push(el.generateOwnCode()); - for (const p of (el.nextFlow || [])) walk(p.toElement); - } +function startBlock(id) { + return { + id: id, + isDecision: false, + nextFlow: [], + generateOwnCode: function () { + return ""; + }, + generateCode: function (visited) { + if (visited.has(this)) return ""; + visited.add(this); + var c = this.generateOwnCode(); + for (var i = 0; i < this.nextFlow.length; i++) { + c += this.nextFlow[i].toElement.generateCode(visited); + } + return c; + }, }; - for (const el of roots) walk(el); - return lines.join("\n"); } -// ── Block classes (simplified, match browser code gen) ──────────────── - -class Block { - constructor(id, type, nextFlow = []) { - this.id = id; - this.type = type; - this.nextFlow = nextFlow; - } - generateCode(visited) { - if (visited.has(this)) return ""; - visited.add(this); - const own = this.generateOwnCode(); - const next = this.generateNextCode(visited); - return own + next; - } - generateNextCode(visited) { - let c = ""; - for (const p of this.nextFlow) { - c += p.toElement.generateCode(visited); - } - return c; - } - generateOwnCode() { return ""; } +function endBlock(id, exitCode) { + var code = exitCode === 0 ? "\n return 0;\n" : "\n exit(" + exitCode + ");\n"; + return { + id: id, + isDecision: false, + nextFlow: [], + generateOwnCode: function () { + return code; + }, + generateCode: function (visited) { + if (visited.has(this)) return ""; + visited.add(this); + return this.generateOwnCode(); + }, + }; } -class Start extends Block { - generateOwnCode() { return ""; } +function wrap(targets) { + return (targets || []).map(function (t) { + return { toElement: t }; + }); } -class End extends Block { - constructor(id, code = 0) { - super(id, "end"); - this.exitCode = code; - } - generateOwnCode() { - if (this.exitCode === 0) return "\n return 0;\n"; - return `\n exit(${this.exitCode});\n`; - } -} - -class Decision extends Block { - constructor(id, yesTargets = [], noTargets = []) { - super(id, "decision"); - this.yesTargets = yesTargets; - this.noTargets = noTargets; - } - get nextFlow() { - return [...this.yesTargets, ...this.noTargets]; - } - _branchDescendants(roots) { - const d = new Set(), q = [...roots]; - for (const el of q) { - if (d.has(el)) continue; - d.add(el); - if (el instanceof Decision) continue; - for (const p of el.nextFlow) { - if (!d.has(p.toElement)) q.push(p.toElement); - } - } - return d; - } - _emitBranch(roots, stop, visited) { - const v = new Set(visited); - const lines = []; - const walk = (el) => { - if (v.has(el) || stop.has(el)) return; - v.add(el); - if (el instanceof Decision) { - lines.push(el.generateCode(v)); +function procCallBlock(id, procName, inputs, returnStyle, returnValueName, okTargets, errTargets) { + var call = procName + "(" + inputs.join(", ") + ")"; + var self = { + id: id, + isDecision: false, + nextFlow: wrap(okTargets), + errTargets: errTargets || [], + generateOwnCode: function () { + if (self.errTargets.length === 0) return "\n " + call + ";\n"; + var ifHeader; + if (returnStyle === "value" && returnValueName) { + ifHeader = " if ((" + returnValueName + " = " + call + ") != 0)"; } else { - lines.push(el.generateOwnCode()); - for (const p of el.nextFlow) walk(p.toElement); + ifHeader = " if (" + call + " != 0)"; } - }; - for (const el of roots) walk(el); - return lines.join("\n"); - } - generateCode(visited = new Set()) { - if (visited.has(this)) return ""; - visited.add(this); - if (this.yesTargets.length === 0 && this.noTargets.length === 0) return ""; - const yesAll = this._branchDescendants(this.yesTargets); - const noAll = this._branchDescendants(this.noTargets); - const shared = new Set([...yesAll].filter(el => noAll.has(el) && el !== this)); - const yesCode = this._emitBranch(this.yesTargets, shared, visited); - const noCode = this._emitBranch(this.noTargets, shared, visited); - const sharedCode = this._emitBranch([...shared], new Set(), visited); - for (const el of yesAll) visited.add(el); - for (const el of noAll) visited.add(el); - let out = ""; - if (yesCode || noCode) { - if (yesCode) out += `\nif (cond) {\n${yesCode}}`; - if (noCode) { - out += yesCode - ? ` else {\n${noCode}}` - : `\nif (!(cond)) {\n${noCode}}`; + var okTargets = self.nextFlow.map(function (p) { + return p.toElement; + }); + return CodeGen.generateCallWithError( + "\n" + ifHeader, + self.errTargets, + okTargets, + function (b) { + return b.isDecision; + } + ); + }, + generateCode: function (visited) { + if (visited.has(this)) return ""; + visited.add(this); + var own = this.generateOwnCode(); + if (self.errTargets.length > 0) return own; + for (var i = 0; i < this.nextFlow.length; i++) { + own += this.nextFlow[i].toElement.generateCode(visited); } - } - out += sharedCode; - return out ? `\n${out}\n` : ""; - } -} - -class ProcCall extends Block { - constructor(id, procName, inputs, returnStyle, returnValueName, nextFlow = [], errTargets = []) { - super(id, "call", nextFlow); - this.procName = procName; - this.inputs = inputs; - this.returnStyle = returnStyle; - this.returnValueName = returnValueName; - this.errTargets = errTargets; - } - generateCode(visited = new Set()) { - if (visited.has(this)) return ""; - visited.add(this); - const own = this.generateOwnCode(); - if (this.errTargets.length > 0) { return own; - } - return own + this.generateNextCode(visited); - } - generateOwnCode() { - const call = `${this.procName}(${this.inputs.join(", ")})`; - if (this.errTargets.length === 0) return `\n ${call};\n`; - - let ifHeader; - if (this.returnStyle === "value" && this.returnValueName) { - ifHeader = ` if ((${this.returnValueName} = ${call}) != 0)`; - } else { - ifHeader = ` if (${call} != 0)`; - } - return this._generateCallWithError(ifHeader, this.errTargets); - } - _generateCallWithError(ifHeader, errTargets) { - const okTargets = this.nextFlow.map(p => p.toElement); - const errAll = branchDescendants(errTargets); - const okAll = branchDescendants(okTargets); - const shared = new Set([...errAll].filter(b => okAll.has(b))); - const errCode = emitBranch(errTargets, shared, new Set()); - const okCode = emitBranch(okTargets, shared, new Set()); - const sharedCode = emitBranch([...shared], new Set(), new Set()); - let out = `\n${ifHeader} {\n${errCode}}\n`; - if (okCode) out += okCode; - if (sharedCode) out += sharedCode; - return out; - } + }, + }; + return self; } -class Assign extends Block { - constructor(id, target, value) { - super(id, "assign"); - this.target = target; - this.value = value; - } - generateOwnCode() { - return `\n ${this.target} = ${this.value};\n`; - } +function assignBlock(id, target, value) { + var code = "\n " + target + " = " + value + ";\n"; + return { + id: id, + isDecision: false, + nextFlow: [], + generateOwnCode: function () { + return code; + }, + generateCode: function (visited) { + if (visited.has(this)) return ""; + visited.add(this); + var c = this.generateOwnCode(); + for (var i = 0; i < this.nextFlow.length; i++) { + c += this.nextFlow[i].toElement.generateCode(visited); + } + return c; + }, + }; } -// ── Template generators ──────────────────────────────────────────────── +function link(from, to) { + from.nextFlow.push({ toElement: to }); +} + +// ── Template builders ────────────────────────────────────────────────── function buildConsoleTemplate() { - const start = new Start("start"); - const end = new End("end", 0); - const printf = new ProcCall("printf", "printf", ['"Hello, World!\\n"'], "value", "result"); - const errEnd = new End("errEnd", 1); - printf.errTargets = [errEnd]; - - start.nextFlow.push({ toElement: printf }); - printf.nextFlow.push({ toElement: end }); - - return { start, vars: ["int result;"], hasStdio: true, hasStdlib: true }; + var start = startBlock("start"); + var end = endBlock("end", 0); + var errEnd = endBlock("errEnd", 1); + var printf = procCallBlock( + "printf", + "printf", + ['"Hello, World!\\n"'], + "value", + "result", + [end], + [errEnd] + ); + link(start, printf); + return { + entry: start, + includes: ["#include ", "#include "], + declarations: " int result;", + }; } function buildErrorSimpleTemplate() { - const start = new Start("start"); - const end = new End("end", 0); - const call = new ProcCall("init", "func", [], "error", null); - const errEnd = new End("errEnd", 1); - call.errTargets = [errEnd]; - start.nextFlow.push({ toElement: call }); - call.nextFlow.push({ toElement: end }); - return { start, vars: [], hasStdlib: true }; -} - -function buildErrorSimpleTemplate() { - const start = new Start("start"); - const end = new End("end", 0); - const call = new ProcCall("init", "func", [], "error", null); - const errEnd = new End("errEnd", 1); - call.errTargets = [errEnd]; - start.nextFlow.push({ toElement: call }); - call.nextFlow.push({ toElement: end }); - return { start, vars: [], hasStdlib: true, proto: ["int func(void);"] }; + var start = startBlock("start"); + var end = endBlock("end", 0); + var errEnd = endBlock("errEnd", 1); + var call = procCallBlock("init", "func", [], "error", null, [end], [errEnd]); + link(start, call); + return { + entry: start, + includes: ["#include "], + declarations: "", + proto: ["int func(void);"], + }; } function buildErrorMergeTemplate() { - const start = new Start("start"); - const end = new End("end", 0); - const call = new ProcCall("init", "func", [], "error", null); - const errLog = new Assign("log_err", "logged", "1"); - const shared = new Assign("shared_done", "done", "1"); - - call.errTargets = [errLog]; - start.nextFlow.push({ toElement: call }); - errLog.nextFlow.push({ toElement: shared }); - call.nextFlow.push({ toElement: shared }); - shared.nextFlow.push({ toElement: end }); - - return { start, vars: ["int logged;", "int done;"], - hasStdlib: true, proto: ["int func(void);"] }; + var start = startBlock("start"); + var end = endBlock("end", 0); + var logged = assignBlock("log", "logged", "1"); + var done = assignBlock("done", "done", "1"); + var call = procCallBlock("init", "func", [], "error", null, [done], [logged]); + link(logged, done); + link(done, end); + link(start, call); + return { + entry: start, + includes: ["#include "], + declarations: " int logged;\n int done;", + proto: ["int func(void);"], + }; } function errorSequentialTemplate() { - const start = new Start("start"); - const end = new End("end", 0); - const call1 = new ProcCall("a", "init_a", [], "error", null); - const call2 = new ProcCall("b", "init_b", [], "error", null); - const errEnd1 = new End("err1", 1); - const errEnd2 = new End("err2", 2); - call1.errTargets = [errEnd1]; - call2.errTargets = [errEnd2]; - start.nextFlow.push({ toElement: call1 }); - call1.nextFlow.push({ toElement: call2 }); - call2.nextFlow.push({ toElement: end }); - return { start, vars: [], hasStdlib: true, proto: ["int init_a(void);", "int init_b(void);"] }; + var start = startBlock("start"); + var end = endBlock("end", 0); + var errEnd1 = endBlock("err1", 1); + var errEnd2 = endBlock("err2", 2); + var call1 = procCallBlock("a", "init_a", [], "error", null, null, [errEnd1]); + var call2 = procCallBlock("b", "init_b", [], "error", null, [end], [errEnd2]); + link(start, call1); + link(call1, call2); + return { + entry: start, + includes: ["#include "], + declarations: "", + proto: ["int init_a(void);", "int init_b(void);"], + }; } function buildErrorValueTemplate() { - const start = new Start("start"); - const end = new End("end", 0); - const call = new ProcCall("open", "open_file", ['"data.txt"', "0"], "value", "fd"); - const errEnd = new End("errEnd", 1); - call.errTargets = [errEnd]; - start.nextFlow.push({ toElement: call }); - call.nextFlow.push({ toElement: end }); - return { start, vars: ["int fd;"], hasStdlib: true, proto: ["int open_file(const char *path, int flags);"] }; + var start = startBlock("start"); + var end = endBlock("end", 0); + var errEnd = endBlock("errEnd", 1); + var call = procCallBlock( + "open", + "open_file", + ['"data.txt"', "0"], + "value", + "fd", + [end], + [errEnd] + ); + link(start, call); + return { + entry: start, + includes: ["#include "], + declarations: " int fd;", + proto: ["int open_file(const char *path, int flags);"], + }; } -function generateSource({ start, vars, hasStdio, hasStdlib, proto }) { - const includes = []; - if (hasStdio) includes.push("#include "); - if (hasStdlib) includes.push("#include "); - const body = start.generateCode(new Set()); - const code = [ - "// --- WiseCode\u00ae ---", - ...includes, - ...(proto ? proto : []), - "", - "int main() {", - ...(vars ? vars.map(v => " " + v) : []), - body, - "}", - "// --- END ---", - ].join("\n"); - return code; +// ── Source generation ────────────────────────────────────────────────── + +function generateSource(opts) { + var parts = ["// --- WiseCode\u00ae ---"]; + for (var i = 0; i < opts.includes.length; i++) parts.push(opts.includes[i]); + if (opts.proto) { + for (var i = 0; i < opts.proto.length; i++) parts.push(opts.proto[i]); + } + parts.push(""); + parts.push("int main() {"); + if (opts.declarations) parts.push(opts.declarations); + parts.push(opts.entry.generateCode(new Set())); + parts.push("}"); + parts.push("// --- END ---"); + return parts.join("\n"); } // ── Main ──────────────────────────────────────────────────────────────── function writeSource(label, source) { fs.mkdirSync(OUTDIR, { recursive: true }); - const file = path.join(OUTDIR, label.replace(/[^a-z0-9]/gi, "_") + ".c"); - /* Show a short preview */ - const firstLines = source.split("\n").slice(0, 4).join("\n"); - console.log(` GEN ${path.basename(file)} (${source.split("\n").length} lines)`); + var file = path.join(OUTDIR, label.replace(/[^a-z0-9]/gi, "_") + ".c"); + console.log(" GEN " + path.basename(file) + " (" + source.split("\n").length + " lines)"); fs.writeFileSync(file, source, "utf8"); } console.log("\nGenerating C compilation tests...\n"); -// Clear old files -try { execSync(`rm -f ${OUTDIR}/*.c`, { stdio: "pipe" }); } catch (_) {} +try { + execSync("rm -f " + OUTDIR + "/*.c", { stdio: "pipe" }); +} catch (_) {} writeSource("test_console", generateSource(buildConsoleTemplate())); writeSource("test_error_simple", generateSource(buildErrorSimpleTemplate())); @@ -331,7 +265,7 @@ writeSource("test_error_value", generateSource(buildErrorValueTemplate())); console.log("\nRunning make...\n"); try { - execSync(`make -C "${OUTDIR}" -f "${MKFILE}" OUTDIR="${OUTDIR}" all 2>&1`, { + execSync('make -C "' + OUTDIR + '" -f "' + MKFILE + '" OUTDIR="' + OUTDIR + '" all 2>&1', { stdio: "inherit", }); } catch (e) { @@ -343,9 +277,6 @@ try { try { execSync("pkg-config --exists sdl3", { stdio: "pipe" }); console.log("\nSDL3 headers found, generating SDL3 test...\n"); - // Future: generate SDL3 template C source and run `make sdl3` - // writeSource("test_sdl3", generateSDL3Source()); - // execSync("make ... sdl3", ...); console.log(" (SDL3 generation not yet implemented; skipped)\n"); } catch (_) { console.log("\nSDL3 headers not found (skipping SDL3 compilation test).\n");