Files
avcf/tests/compile/run.js
T
adrien 35959784de refactor: flat DFA code generation — every block is a label, every edge is a goto
Replace the nested if-else branch-analysis architecture with a flat DFA
model using CodeGen.emitFlat — a single DFS walk emitting each block as
a C label followed by its code and gotos to all successors.

Removed:
- _branchDescendants / emitBranch / generateCallWithError
- _generateCallWithError, _noWalkNext, _genVisited
- generateNextCode, findBackEdges
- WCDecision._branchDescendants / _emitBranch / overridden generateCode

Added:
- CodeGen.emitFlat(entry) — flat DFS emit with labels + gotos
- WCBlock.generateFlowCode() — base emits gotos from wcNextFlow
- WCDecision.generateFlowCode() — if-goto / goto for decisions
- WCProcCall.generateFlowCode() — if (call != 0) goto err / goto ok

Output is flat — zero nested if-else for branch/error handling.
Tests use local out/ directory.
2026-06-23 03:21:22 +02:00

195 lines
7.2 KiB
JavaScript

#!/usr/bin/env node
/**
* C compilation test runner — flat DFA code generation.
*
* Block data objects are minimal: generateOwnCode() + optional generateFlowCode().
* The shared CodeGen.emitFlat walks the graph, emitting labels + gotos.
*/
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const CodeGen = require("../../codegen/index.js");
const OUTDIR = path.join(__dirname, "out");
const MKFILE = path.join(__dirname, "Makefile");
// ── Block data helpers ────────────────────────────────────────────────
function startBlock(id) {
return {
id: id,
isDecision: false,
nextFlow: [],
generateOwnCode: function () { return ""; },
};
}
function endBlock(id, exitCode) {
var code = exitCode === 0 ? " return 0;\n" : " exit(" + exitCode + ");\n";
return {
id: id,
isDecision: false,
nextFlow: [],
generateOwnCode: function () { return code; },
};
}
function procCallBlock(id, procName, inputs, returnStyle, returnValueName, okTargets, errTargets) {
var call = procName + "(" + inputs.join(", ") + ")";
var self = {
id: id,
isDecision: false,
nextFlow: (okTargets || []).concat(errTargets || []).map(function (t) { return { toElement: t }; }),
errTargets: errTargets || [],
generateOwnCode: function () {
if (returnStyle === "error" && self.errTargets.length > 0) return "";
if (returnStyle === "value" && returnValueName && self.errTargets.length > 0) {
return " " + returnValueName + " = " + call + ";\n";
}
return " " + call + ";\n";
},
generateFlowCode: function () {
if (self.errTargets.length === 0) {
return self.nextFlow.map(function (p) { return " goto " + p.toElement.id + ";\n"; }).join("");
}
var errT = self.errTargets[0];
var okT = null;
for (var i = 0; i < self.nextFlow.length; i++) {
var t = self.nextFlow[i].toElement;
if (self.errTargets.indexOf(t) === -1) { okT = t; break; }
}
var out = "";
if (returnStyle === "error") {
out += " if (" + call + " != 0) goto " + errT.id + ";\n";
} else if (returnStyle === "value" && returnValueName) {
out += " if (" + returnValueName + " != 0) goto " + errT.id + ";\n";
}
if (okT) out += " goto " + okT.id + ";\n";
return out;
},
};
return self;
}
function assignBlock(id, target, value) {
return {
id: id,
isDecision: false,
nextFlow: [],
generateOwnCode: function () { return " " + target + " = " + value + ";\n"; },
};
}
function link(from, to) {
from.nextFlow.push({ toElement: to });
}
// ── Template builders ──────────────────────────────────────────────────
function buildConsoleTemplate() {
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 <stdio.h>", "#include <stdlib.h>"], declarations: " int result;" };
}
function buildErrorSimpleTemplate() {
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 <stdlib.h>"], declarations: "", proto: ["int func(void);"] };
}
function buildErrorMergeTemplate() {
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 <stdlib.h>"], declarations: " int logged;\n int done;", proto: ["int func(void);"] };
}
function errorSequentialTemplate() {
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 <stdlib.h>"], declarations: "", proto: ["int init_a(void);", "int init_b(void);"] };
}
function buildErrorValueTemplate() {
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 <stdlib.h>"], declarations: " int fd;", proto: ["int open_file(const char *path, int flags);"] };
}
// ── 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(CodeGen.emitFlat(opts.entry));
parts.push("}");
parts.push("// --- END ---");
return parts.join("\n");
}
// ── Main ────────────────────────────────────────────────────────────────
function writeSource(label, source) {
fs.mkdirSync(OUTDIR, { recursive: true });
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");
try { execSync("rm -f " + OUTDIR + "/*.c", { stdio: "pipe" }); } catch (_) {}
writeSource("test_console", generateSource(buildConsoleTemplate()));
writeSource("test_error_simple", generateSource(buildErrorSimpleTemplate()));
writeSource("test_error_merge", generateSource(buildErrorMergeTemplate()));
writeSource("test_error_sequential", generateSource(errorSequentialTemplate()));
writeSource("test_error_value", generateSource(buildErrorValueTemplate()));
console.log("\nRunning make...\n");
try {
execSync('make -C "' + OUTDIR + '" -f "' + MKFILE + '" OUTDIR="' + OUTDIR + '" all 2>&1', { stdio: "inherit" });
} catch (e) {
console.error("\n\u2717 Compilation failed.\n");
process.exit(1);
}
try {
execSync("pkg-config --exists sdl3", { stdio: "pipe" });
console.log("\nSDL3 headers found, generating SDL3 test...\n");
console.log(" (SDL3 generation not yet implemented; skipped)\n");
} catch (_) {
console.log("\nSDL3 headers not found (skipping SDL3 compilation test).\n");
}
console.log("\u2713 All compilation tests passed.\n");