- Add CodeGen.findBackEdges(root) pre-pass — DFS-based back-edge detection that finds all blocks targeted by at least one back-edge - Only those blocks get C labels; only those back-edges emit goto - emitBranch reverts to generateOwnCode + manual successor walking (respects stop set properly for shared blocks) - generateCode/generateNextCode accept loopTargets parameter; visited-but-not-back-edge blocks are silently skipped - No more -Wunused-label warnings in compiled output
630 lines
22 KiB
JavaScript
630 lines
22 KiB
JavaScript
// ── Procedure block definitions ────────────────────────────────────────────
|
|
//
|
|
// Blocks that define and call procedures:
|
|
// WCProcedure — procedure definition (lives in Procedures tab)
|
|
// WCProcCall — procedure call block (lives in Algorithm tab)
|
|
//
|
|
// Each extends WCBlock (from wcblock.js).
|
|
//
|
|
// Load order dependencies:
|
|
// wcblock.js → WCBlock base class
|
|
// helpers.js → findDataSource, resolveSourceName, escapeString, toCName, commentPrefix
|
|
// dom.js → $id
|
|
// draw.js → deleteLink
|
|
|
|
|
|
/** @type {Object<string,string>} Maps library IDs to icon image paths for procedure call blocks. */
|
|
const LIB_ICONS = {
|
|
stdio: "lib-icons/c.svg",
|
|
stdlib: "lib-icons/c.svg",
|
|
string: "lib-icons/c.svg",
|
|
math: "lib-icons/c.svg",
|
|
};
|
|
|
|
// ── WCProcedure — procedure definition ─────────────────────────────────────
|
|
|
|
/** Procedure definition block with inputs/outputs/description/return-style. Lives in the Procedures tab. */
|
|
class WCProcedure extends WCBlock {
|
|
delete() {
|
|
if (this.dataset.readonly) { showError("Cannot delete a library procedure."); return; }
|
|
super.delete();
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this._el.naturalName = this._require(".proc-natural-name");
|
|
this._el.techName = this._require(".proc-tech-name");
|
|
this._el.inputs = this._require(".proc-inputs");
|
|
this._el.outputs = this._require(".proc-outputs");
|
|
this._el.description = this.querySelector(".proc-description");
|
|
this._el.descBody = this.querySelector(".proc-desc-body");
|
|
|
|
this._require(".proc-add-input").addEventListener("click", () => {
|
|
if (this.dataset.readonly) return;
|
|
this._addParamRow(this._el.inputs, "in");
|
|
});
|
|
this._require(".proc-add-output").addEventListener("click", () => {
|
|
if (this.dataset.readonly) return;
|
|
this._addParamRow(this._el.outputs, "out");
|
|
});
|
|
|
|
this._el.naturalName.addEventListener("input", () => {
|
|
if (!this.dataset.readonly) {
|
|
this._el.techName.textContent = toCName(this._el.naturalName.value) || "proc";
|
|
}
|
|
});
|
|
|
|
this._el.returnStyle = this.querySelector(".proc-return-style");
|
|
if (this._el.returnStyle) {
|
|
this.dataset.returnStyle = this._el.returnStyle.value;
|
|
this._el.returnStyle.addEventListener("change", () => {
|
|
if (this.dataset.readonly) return;
|
|
this.dataset.returnStyle = this._el.returnStyle.value;
|
|
});
|
|
}
|
|
}
|
|
|
|
_addParamRow(container, dir) {
|
|
const row = document.createElement("div");
|
|
row.className = "proc-param-row";
|
|
const readonly = this.dataset.readonly;
|
|
row.innerHTML =
|
|
`<input class="proc-param-name" placeholder="name" value=""${readonly ? " readonly" : ""}>` +
|
|
`<input class="proc-param-type" placeholder="type" value="scalar"${readonly ? " readonly" : ""}>` +
|
|
(readonly ? "" : `<button class="proc-param-del" title="remove">×</button>`);
|
|
if (!readonly) {
|
|
row.querySelector(".proc-param-del").addEventListener("click", () => {
|
|
row.remove();
|
|
});
|
|
}
|
|
container.appendChild(row);
|
|
}
|
|
|
|
getLibrary() {
|
|
const pane = this.closest(".lib-pane");
|
|
return pane ? pane.dataset.libName : "unknown";
|
|
}
|
|
|
|
getQualifiedName() {
|
|
return this.getLibrary() + "/" + (this._el.techName.textContent || "unnamed");
|
|
}
|
|
|
|
getInputParams() {
|
|
return this._getParams(this._el.inputs);
|
|
}
|
|
|
|
getOutputParams() {
|
|
return this._getParams(this._el.outputs);
|
|
}
|
|
|
|
_getParams(container) {
|
|
return Array.from(container.querySelectorAll(".proc-param-row")).map(row => ({
|
|
name: row.querySelector(".proc-param-name").value || "arg",
|
|
type: row.querySelector(".proc-param-type").value || "scalar"
|
|
}));
|
|
}
|
|
|
|
saveData() {
|
|
return {
|
|
library: this.getLibrary(),
|
|
naturalName: this._el.naturalName.value,
|
|
name: this._el.techName.textContent,
|
|
inputs: this.getInputParams(),
|
|
outputs: this.getOutputParams(),
|
|
returnStyle: this._el.returnStyle ? this._el.returnStyle.value : (this.dataset.returnStyle || "pointer"),
|
|
readonly: !!this.dataset.readonly,
|
|
formatString: this.dataset.formatString !== undefined,
|
|
description: this._el.descBody ? this._el.descBody.textContent : "",
|
|
descOpen: this._el.description ? this._el.description.open : false
|
|
};
|
|
}
|
|
restoreData(d) {
|
|
this._el.naturalName.value = d.naturalName || "";
|
|
this._el.techName.textContent = d.name || "proc";
|
|
if (d.readonly) this.dataset.readonly = "true";
|
|
if (d.formatString) this.dataset.formatString = "true";
|
|
if (d.returnStyle) {
|
|
this.dataset.returnStyle = d.returnStyle;
|
|
if (this._el.returnStyle) this._el.returnStyle.value = d.returnStyle;
|
|
}
|
|
if (this._el.descBody && d.description) this._el.descBody.textContent = d.description;
|
|
if (this._el.description && d.descOpen) this._el.description.open = true;
|
|
for (const p of (d.inputs || [])) {
|
|
this._addParamRow(this._el.inputs, "in");
|
|
const rows = this._el.inputs.querySelectorAll(".proc-param-row");
|
|
const last = rows[rows.length - 1];
|
|
last.querySelector(".proc-param-name").value = p.name || "arg";
|
|
last.querySelector(".proc-param-type").value = p.type || "scalar";
|
|
}
|
|
for (const p of (d.outputs || [])) {
|
|
this._addParamRow(this._el.outputs, "out");
|
|
const rows = this._el.outputs.querySelectorAll(".proc-param-row");
|
|
const last = rows[rows.length - 1];
|
|
last.querySelector(".proc-param-name").value = p.name || "arg";
|
|
last.querySelector(".proc-param-type").value = p.type || "scalar";
|
|
}
|
|
}
|
|
|
|
generateOwnCode() { return ""; }
|
|
}
|
|
|
|
|
|
// ── WCProcCall — procedure call block ──────────────────────────────────────
|
|
|
|
/** @type {RegExp} Matches C printf-format specifiers, capturing the type character. */
|
|
const _FMT_SPEC_RE = /%(?:[+\- #0]*\d*(?:\.\d+)?)([diufFeEgGxXoscpaAn%])/g;
|
|
|
|
/**
|
|
* Maps a printf format specifier character to a C data type.
|
|
* @param {string} spec Single format character (e.g. "d", "f", "s")
|
|
* @returns {string} "int", "double", or "string"
|
|
*/
|
|
function _specDataType(spec) {
|
|
if ("di".includes(spec)) return "int";
|
|
if ("u".includes(spec)) return "int";
|
|
if ("fFeEgGaA".includes(spec)) return "double";
|
|
if ("s".includes(spec)) return "string";
|
|
return "int";
|
|
}
|
|
|
|
/**
|
|
* Adapter: wraps CodeGen.branchDescendants with the browser's instanceof check.
|
|
* @param {HTMLElement[]} roots
|
|
* @returns {Set<HTMLElement>}
|
|
*/
|
|
function _branchDescendants(roots) {
|
|
return CodeGen.branchDescendants(roots, function (b) {
|
|
return b instanceof WCDecision;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Adapter: wraps CodeGen.emitBranch with the browser's instanceof check.
|
|
* @param {HTMLElement[]} roots
|
|
* @param {Set<HTMLElement>} stop
|
|
* @param {Set<HTMLElement>} visited
|
|
* @returns {string}
|
|
*/
|
|
function _emitBranch(roots, stop, visited) {
|
|
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. */
|
|
class WCProcCall extends WCBlock {
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this._selectedProc = "";
|
|
this._fmtProc = null;
|
|
this._fmtInput = null;
|
|
this._fmtObserver = null;
|
|
this._el.icon = this._require(".proccall-icon");
|
|
this._el.natName = this._require(".proccall-nat-name");
|
|
this._el.techName = this._require(".proccall-tech-name");
|
|
this._el.changeBtn = this._require(".proccall-change-btn");
|
|
this._el.nameInput = this._require(".proccall-name");
|
|
this._el.nameRow = this._require(".proccall-name-row");
|
|
this._el.inputs = this._require(".proccall-inputs");
|
|
this._el.outputs = this._require(".proccall-outputs");
|
|
this._el.errHook = this.querySelector(".proccall-error-hook");
|
|
this._el.fmtargs = this._require(".proccall-fmtargs");
|
|
this._el.fmtargsList = this._require(".proccall-fmtargs-list");
|
|
this._el.addFmtArgBtn = this._require(".proccall-add-fmtarg");
|
|
this._el.addFmtArgBtn.addEventListener("click", () => this._addFormatArg());
|
|
|
|
const openBrowser = () => {
|
|
const current = this._selectedProc;
|
|
showProcedureBrowser(current, (qname) => {
|
|
if (qname) this.setProcedure(qname);
|
|
});
|
|
};
|
|
|
|
this._el.natName.addEventListener("click", openBrowser);
|
|
this._el.changeBtn.addEventListener("click", openBrowser);
|
|
this._initTimer = setTimeout(() => this._rebuildParams(), 0);
|
|
}
|
|
|
|
setProcedure(qname) {
|
|
this._selectedProc = qname || "";
|
|
const libName = qname ? qname.split("/")[0] : "";
|
|
if (this._el.natName) {
|
|
if (qname) {
|
|
let natName = qname.split("/")[1] || qname;
|
|
let techName = qname.split("/")[1] || qname;
|
|
for (const proc of document.querySelectorAll("wc-procedure")) {
|
|
if (proc.getQualifiedName() === qname) {
|
|
const nat = proc.querySelector(".proc-natural-name");
|
|
if (nat && nat.value) natName = nat.value;
|
|
techName = (proc.querySelector(".proc-tech-name") || {}).textContent || techName;
|
|
break;
|
|
}
|
|
}
|
|
this._el.natName.textContent = natName;
|
|
this._el.natName.title = qname;
|
|
this._el.techName.textContent = techName;
|
|
this._el.techName.title = qname;
|
|
this._el.icon.src = LIB_ICONS[libName] || "";
|
|
this._el.icon.alt = libName;
|
|
} else {
|
|
this._el.natName.textContent = "(select procedure)";
|
|
this._el.natName.title = "";
|
|
this._el.techName.textContent = "";
|
|
this._el.techName.title = "";
|
|
this._el.icon.src = "";
|
|
this._el.icon.alt = "";
|
|
}
|
|
}
|
|
this._rebuildParams();
|
|
}
|
|
|
|
_rebuildParams() {
|
|
const qname = this._selectedProc;
|
|
|
|
for (const container of [this._el.inputs, this._el.outputs]) {
|
|
for (const hook of container.querySelectorAll(".hook")) {
|
|
const paths = [...document.querySelectorAll("path.flow, path.flow-data")];
|
|
for (const path of paths) {
|
|
if (path.fromHook === hook || path.toHook === hook) {
|
|
deleteLink(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this._el.inputs.innerHTML = "";
|
|
this._el.outputs.innerHTML = "";
|
|
|
|
let returnStyle = "pointer";
|
|
if (qname) {
|
|
for (const proc of document.querySelectorAll("wc-procedure")) {
|
|
if (proc.getQualifiedName() === qname) {
|
|
returnStyle = proc.dataset.returnStyle || "pointer";
|
|
this._cachedReturnStyle = returnStyle;
|
|
for (const p of proc.getInputParams()) {
|
|
this._createArgField(this._el.inputs, p, "in");
|
|
}
|
|
if (returnStyle !== "error") {
|
|
for (const p of proc.getOutputParams()) {
|
|
this._createArgField(this._el.outputs, p, "out");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
this._setupFormatStringArgs();
|
|
|
|
const isError = returnStyle === "error";
|
|
const hasReturnValue = returnStyle === "error" || returnStyle === "value";
|
|
if (this._el.errHook) {
|
|
this._el.errHook.classList.toggle("visible", hasReturnValue);
|
|
}
|
|
if (this._el.nameRow) {
|
|
this._el.nameRow.style.display = isError ? "none" : "";
|
|
}
|
|
}
|
|
|
|
_setupFormatStringArgs() {
|
|
const qname = this._selectedProc;
|
|
let fmtProc = null;
|
|
if (qname) {
|
|
for (const proc of document.querySelectorAll("wc-procedure")) {
|
|
if (proc.getQualifiedName() === qname && proc.dataset.formatString !== undefined) {
|
|
fmtProc = proc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this._fmtProc = fmtProc;
|
|
|
|
if (this._fmtObserver) { this._fmtObserver.disconnect(); this._fmtObserver = null; }
|
|
|
|
if (!fmtProc) {
|
|
this._el.fmtargs.style.display = "none";
|
|
this._el.addFmtArgBtn.style.display = "none";
|
|
this._el.fmtargsList.innerHTML = "";
|
|
return;
|
|
}
|
|
|
|
this._el.fmtargs.style.display = "";
|
|
this._fmtInput = this._el.inputs.querySelector(".proccall-arg-input");
|
|
|
|
if (this._fmtInput) {
|
|
this._fmtInput.addEventListener("input", () => this._rebuildFormatArgs());
|
|
}
|
|
|
|
this._fmtObserver = new MutationObserver(() => {
|
|
if (this._isFmtDynamic()) this._ensureFmtUnconnected();
|
|
});
|
|
this._fmtObserver.observe(this._el.fmtargsList, { attributes: true, attributeFilter: ["data-connected"], subtree: true });
|
|
|
|
this._rebuildFormatArgs();
|
|
}
|
|
|
|
_isFmtDynamic() {
|
|
if (!this._fmtInput) return false;
|
|
const hook = this._fmtInput.parentElement.querySelector(".hook");
|
|
return hook ? findDataSource(hook) !== null : false;
|
|
}
|
|
|
|
_rebuildFormatArgs() {
|
|
this._el.fmtargsList.innerHTML = "";
|
|
const dynamic = this._isFmtDynamic();
|
|
this._el.addFmtArgBtn.style.display = dynamic ? "" : "none";
|
|
|
|
if (dynamic) {
|
|
this._ensureFmtUnconnected();
|
|
return;
|
|
}
|
|
|
|
const fmt = this._fmtInput ? this._fmtInput.value : "";
|
|
const specs = this._parseFormatSpecifiers(fmt);
|
|
for (let i = 0; i < specs.length; i++) {
|
|
this._createFormatArg(specs[i], i);
|
|
}
|
|
}
|
|
|
|
_parseFormatSpecifiers(fmt) {
|
|
const specs = [];
|
|
let m;
|
|
_FMT_SPEC_RE.lastIndex = 0;
|
|
while ((m = _FMT_SPEC_RE.exec(fmt)) !== null) {
|
|
if (m[1] === "%") continue;
|
|
specs.push({ full: m[0], spec: m[1] });
|
|
}
|
|
return specs;
|
|
}
|
|
|
|
_createFormatArg(spec, index) {
|
|
const row = document.createElement("div");
|
|
row.className = "proccall-arg-row";
|
|
|
|
const hook = document.createElement("button");
|
|
hook.className = "hook";
|
|
hook.dataset.direction = "in";
|
|
hook.dataset.linkType = "data";
|
|
hook.dataset.type = _specDataType(spec.spec);
|
|
hook.dataset.exit = "left";
|
|
hook.title = `expects ${_specDataType(spec.spec)}`;
|
|
|
|
const label = document.createElement("span");
|
|
label.className = "proccall-arg-label";
|
|
label.textContent = `arg${index + 1}:`;
|
|
|
|
const input = document.createElement("input");
|
|
input.className = "proccall-arg-input";
|
|
input.dataset.dir = "in";
|
|
input.dataset.paramName = `fmtArg${index}`;
|
|
input.placeholder = spec.full;
|
|
|
|
row.appendChild(hook);
|
|
row.appendChild(label);
|
|
row.appendChild(input);
|
|
this._el.fmtargsList.appendChild(row);
|
|
this._setupHook(hook);
|
|
this._wrapArgInput(input);
|
|
}
|
|
|
|
_addFormatArg() {
|
|
const idx = this._el.fmtargsList.querySelectorAll(".proccall-arg-row").length;
|
|
const row = document.createElement("div");
|
|
row.className = "proccall-arg-row";
|
|
|
|
const hook = document.createElement("button");
|
|
hook.className = "hook";
|
|
hook.dataset.direction = "in";
|
|
hook.dataset.linkType = "data";
|
|
hook.dataset.type = "scalar";
|
|
hook.dataset.exit = "left";
|
|
hook.title = "expects scalar";
|
|
|
|
const label = document.createElement("span");
|
|
label.className = "proccall-arg-label";
|
|
label.textContent = `arg${idx + 1}:`;
|
|
|
|
const input = document.createElement("input");
|
|
input.className = "proccall-arg-input";
|
|
input.dataset.dir = "in";
|
|
input.dataset.paramName = `fmtArg${idx}`;
|
|
input.placeholder = `arg${idx + 1}`;
|
|
|
|
row.appendChild(hook);
|
|
row.appendChild(label);
|
|
row.appendChild(input);
|
|
this._el.fmtargsList.appendChild(row);
|
|
this._setupHook(hook);
|
|
this._wrapArgInput(input);
|
|
this._ensureFmtUnconnected();
|
|
}
|
|
|
|
_ensureFmtUnconnected() {
|
|
if (!this._isFmtDynamic()) return;
|
|
const hooks = this._el.fmtargsList.querySelectorAll(".hook");
|
|
const hasFree = [...hooks].some(h => !h.dataset.connected);
|
|
if (!hasFree) this._addFormatArg();
|
|
}
|
|
|
|
_wrapArgInput(input) {
|
|
if (input.style.display === "none") return;
|
|
const display = document.createElement("span");
|
|
display.className = "field-display";
|
|
const update = () => { display.textContent = input.value || input.placeholder || ""; };
|
|
update();
|
|
input.addEventListener("change", update);
|
|
input.addEventListener("input", update);
|
|
input.parentNode.insertBefore(display, input);
|
|
input.style.display = "none";
|
|
}
|
|
|
|
_createArgField(container, param, dir) {
|
|
const row = document.createElement("div");
|
|
row.className = "proccall-arg-row";
|
|
|
|
const label = document.createElement("span");
|
|
label.className = "proccall-arg-label";
|
|
label.textContent = param.name + ":";
|
|
|
|
const input = document.createElement("input");
|
|
input.className = "proccall-arg-input";
|
|
input.placeholder = param.name;
|
|
input.dataset.dir = dir;
|
|
input.dataset.paramName = param.name;
|
|
input.dataset.paramType = param.type;
|
|
|
|
const hook = document.createElement("button");
|
|
hook.className = "hook";
|
|
hook.dataset.direction = dir === "in" ? "in" : "out";
|
|
hook.dataset.linkType = "data";
|
|
hook.dataset.type = param.type;
|
|
hook.dataset.exit = dir === "in" ? "left" : "right";
|
|
hook.title = (dir === "in" ? "expects " : "") + param.type;
|
|
|
|
if (dir === "in") {
|
|
row.appendChild(hook);
|
|
row.appendChild(label);
|
|
row.appendChild(input);
|
|
} else {
|
|
row.appendChild(label);
|
|
row.appendChild(input);
|
|
row.appendChild(hook);
|
|
}
|
|
|
|
container.appendChild(row);
|
|
this._setupHook(hook);
|
|
this._wrapArgInput(input);
|
|
}
|
|
|
|
_sourceName(block) {
|
|
const name = resolveSourceName(block);
|
|
if (!name) return "";
|
|
return block.dataType === "string" ? name + ".data" : name;
|
|
}
|
|
|
|
generateCode(visited = new Set(), loopTargets) {
|
|
if (visited.has(this)) {
|
|
if (loopTargets && loopTargets.has(this)) {
|
|
return "\n goto " + this.id + ";\n";
|
|
}
|
|
return "";
|
|
}
|
|
visited.add(this);
|
|
let own = this.generateOwnCode();
|
|
// The PROC CALL itself might be targeted by a back-edge for the success path.
|
|
// That's handled by generateNextCode on the predecessor block.
|
|
// If this block is a loop target, emit the label before ownCode.
|
|
if (loopTargets && loopTargets.has(this)) {
|
|
own = "\n" + this.id + ":\n" + own;
|
|
}
|
|
if (this._el.errHook && this._el.errHook.classList.contains("visible") &&
|
|
Array.from(this.wcNextFlow).some(p => p.fromHook === this._el.errHook)) {
|
|
return own;
|
|
}
|
|
return own + this.generateNextCode(visited, loopTargets);
|
|
}
|
|
|
|
generateOwnCode() {
|
|
const qname = this._selectedProc;
|
|
if (!qname) return "";
|
|
const parts = qname.split("/");
|
|
const procName = parts[1] || qname;
|
|
|
|
let returnStyle = this._cachedReturnStyle || "pointer";
|
|
|
|
const inputs = [];
|
|
const outputs = [];
|
|
for (const row of this._el.inputs.querySelectorAll(".proccall-arg-row")) {
|
|
const hook = row.querySelector(".hook");
|
|
const inp = row.querySelector(".proccall-arg-input");
|
|
const source = hook ? findDataSource(hook) : null;
|
|
if (source) {
|
|
inputs.push(this._sourceName(source));
|
|
} else {
|
|
let val = inp.value || inp.placeholder;
|
|
if (inp.dataset.paramType === "string") val = `"${escapeString(val)}"`;
|
|
inputs.push(val);
|
|
}
|
|
}
|
|
for (const row of this._el.outputs.querySelectorAll(".proccall-arg-row")) {
|
|
const inp = row.querySelector(".proccall-arg-input");
|
|
outputs.push("&" + (inp.value || inp.placeholder));
|
|
}
|
|
if (this._fmtProc) {
|
|
for (const row of this._el.fmtargsList.querySelectorAll(".proccall-arg-row")) {
|
|
const hook = row.querySelector(".hook");
|
|
const inp = row.querySelector(".proccall-arg-input");
|
|
const source = hook ? findDataSource(hook) : null;
|
|
if (source) {
|
|
inputs.push(this._sourceName(source));
|
|
} else {
|
|
let val = inp.value || inp.placeholder;
|
|
if (hook.dataset.type === "string") val = `"${escapeString(val)}"`;
|
|
inputs.push(val);
|
|
}
|
|
}
|
|
}
|
|
|
|
const errPaths = this._el.errHook && this._el.errHook.classList.contains("visible")
|
|
? Array.from(this.wcNextFlow).filter(p => p.fromHook === this._el.errHook)
|
|
: [];
|
|
const hasErrPath = errPaths.length > 0;
|
|
|
|
if (returnStyle === "error") {
|
|
const call = `${procName}(${inputs.join(", ")})`;
|
|
if (hasErrPath) {
|
|
return this._generateCallWithError(`\nif (${call} != 0)`, errPaths);
|
|
}
|
|
return `\n${call};\n`;
|
|
}
|
|
|
|
if (returnStyle === "value" && outputs.length === 1) {
|
|
const outVar = outputs[0].replace(/^&/, "");
|
|
if (hasErrPath) {
|
|
return this._generateCallWithError(`\nif ((${outVar} = ${procName}(${inputs.join(", ")})) != 0)`, errPaths);
|
|
}
|
|
return `\n${procName}(${inputs.join(", ")});\n`;
|
|
}
|
|
const args = [...inputs, ...outputs].join(", ");
|
|
return `\n${procName}(${args});\n`;
|
|
}
|
|
|
|
_generateCallWithError(ifHeader, errPaths) {
|
|
const errTargets = errPaths.map(p => p.toElement);
|
|
const okTargets = Array.from(this.wcNextFlow)
|
|
.filter(p => p.fromHook !== this._el.errHook)
|
|
.map(p => p.toElement);
|
|
|
|
return CodeGen.generateCallWithError(ifHeader, errTargets, okTargets, function (b) {
|
|
return b instanceof WCDecision;
|
|
}, this._loopTargets);
|
|
}
|
|
|
|
saveData() {
|
|
const args = {};
|
|
for (const inp of this.querySelectorAll(".proccall-arg-input")) {
|
|
args[inp.dataset.dir + ":" + inp.dataset.paramName] = inp.value;
|
|
}
|
|
return { selected: this._selectedProc, name: this._el.nameInput.value, args };
|
|
}
|
|
restoreData(d) {
|
|
if (this._initTimer) { clearTimeout(this._initTimer); this._initTimer = null; }
|
|
this.setProcedure(d.selected || "");
|
|
this._el.nameInput.value = d.name || "";
|
|
if (d.args) {
|
|
for (const inp of this.querySelectorAll(".proccall-arg-input")) {
|
|
const key = inp.dataset.dir + ":" + inp.dataset.paramName;
|
|
if (d.args[key] !== undefined) inp.value = d.args[key];
|
|
}
|
|
}
|
|
if (this._fmtProc && d.args) {
|
|
this._rebuildFormatArgs();
|
|
for (const inp of this.querySelectorAll(".proccall-arg-input")) {
|
|
const key = inp.dataset.dir + ":" + inp.dataset.paramName;
|
|
if (key.startsWith("in:fmtArg") && d.args[key] !== undefined) {
|
|
inp.value = d.args[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|