Files
avcf/codegen/index.js
T
adrien f1eed2d4e0 refactor: extract pure codegen into shared module, remove duplication
- 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
2026-06-23 01:07:08 +02:00

151 lines
4.9 KiB
JavaScript

// ── 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;
}
})();