import {
isPureRhs,
literalIndex,
materialiseStackTopForDup,
resolvePackedValue,
stripOuterParens,
} from "./high-level-stack-shared.js";
function replaceDynamicStackSurvivors(state, helper, argument) {
const base = `${helper}(${argument ?? "???"})`;
const len = state.stack.length;
for (let index = 0; index < state.stack.length; index++) {
const value = state.stack[index];
state.packedValuesByExpression.delete(value);
state.pointerTargetsByExpression.delete(value);
const depthFromTop = len - 1 - index;
state.stack[index] = depthFromTop === 0 ? base : `${base}[${depthFromTop}]`;
}
}
export function tryStackShapeOperation(state, instruction) {
const mnemonic = instruction.opcode.mnemonic;
switch (mnemonic) {
case "DEPTH":
state.stack.push(`${state.stack.length}`);
return true;
case "DROP": {
const top = state.stack.pop();
if (top !== undefined && !isPureRhs(top)) {
const temp = `t${state.nextTempId}`;
state.nextTempId += 1;
state.statements.push(`let ${temp} = ${stripOuterParens(top)};`);
}
return true;
}
case "CLEAR":
state.stack.length = 0;
state.statements.push("// clear stack");
return true;
case "DUP": {
const top = state.stack.at(-1);
if (top === undefined) {
state.stack.push("???");
} else {
const materialised = materialiseStackTopForDup(state, top);
state.stack.push(materialised);
}
return true;
}
case "OVER": {
if (state.stack.length < 2) {
state.stack.push("???");
return true;
}
const idx = state.stack.length - 2;
const materialised = materialiseStackTopForDup(state, state.stack[idx], idx);
state.stack.push(materialised);
return true;
}
case "SWAP": {
if (state.stack.length >= 2) {
const last = state.stack.length - 1;
[state.stack[last - 1], state.stack[last]] = [state.stack[last], state.stack[last - 1]];
}
return true;
}
case "NIP":
if (state.stack.length >= 2) {
state.stack.splice(state.stack.length - 2, 1);
}
return true;
case "PICK": {
const indexText = state.stack.pop();
const index = literalIndex(indexText);
if (!Number.isFinite(index) || index < 0 || index >= state.stack.length) {
const temp = `t${state.nextTempId}`;
state.nextTempId += 1;
state.statements.push(`let ${temp} = pick(${indexText ?? "???"});`);
state.stack.push(temp);
return true;
}
const sourceSlot = state.stack.length - 1 - index;
const source = materialiseStackTopForDup(state, state.stack[sourceSlot], sourceSlot);
state.stack.push(source);
const packed = resolvePackedValue(state, source);
if (packed) {
state.packedValuesByExpression.set(source, packed);
}
return true;
}
case "ROT":
if (state.stack.length >= 3) {
const [a, b, c] = state.stack.splice(state.stack.length - 3, 3);
state.stack.push(b, c, a);
}
state.statements.push("// rotate top three stack values");
return true;
case "TUCK":
if (state.stack.length >= 2) {
const top = materialiseStackTopForDup(state, state.stack[state.stack.length - 1]);
state.stack.splice(state.stack.length - 2, 0, top);
}
return true;
case "ROLL": {
const indexText = state.stack.pop();
const index = literalIndex(indexText);
if (Number.isFinite(index) && index >= 0 && index < state.stack.length) {
const from = state.stack.length - 1 - index;
const [value] = state.stack.splice(from, 1);
state.stack.push(value);
} else {
const removed = state.stack.pop();
if (removed !== undefined) {
state.packedValuesByExpression.delete(removed);
state.pointerTargetsByExpression.delete(removed);
}
replaceDynamicStackSurvivors(state, "roll_remaining", indexText);
const temp = `t${state.nextTempId}`;
state.nextTempId += 1;
state.statements.push(`let ${temp} = roll(${indexText ?? "???"}); // dynamic roll`);
state.stack.push(temp);
}
return true;
}
case "REVERSE3":
if (state.stack.length >= 3) {
const stack = state.stack;
const last = stack.length - 1;
const tmp = stack[last - 2];
stack[last - 2] = stack[last];
stack[last] = tmp;
}
state.statements.push("// reverse top 3 stack values");
return true;
case "REVERSE4":
if (state.stack.length >= 4) {
const stack = state.stack;
const last = stack.length - 1;
let tmp = stack[last - 3];
stack[last - 3] = stack[last];
stack[last] = tmp;
tmp = stack[last - 2];
stack[last - 2] = stack[last - 1];
stack[last - 1] = tmp;
}
state.statements.push("// reverse top 4 stack values");
return true;
case "REVERSEN": {
const countText = state.stack.pop();
const count = literalIndex(countText);
if (Number.isFinite(count) && count >= 0 && count <= state.stack.length) {
const stack = state.stack;
let i = stack.length - count;
let j = stack.length - 1;
while (i < j) {
const tmp = stack[i];
stack[i] = stack[j];
stack[j] = tmp;
i++;
j--;
}
state.statements.push(`// reverse top ${count} stack values`);
} else {
state.statements.push(`// reverse top ${countText ?? "???"} stack values`);
replaceDynamicStackSurvivors(state, "reverse_n_top", countText);
}
return true;
}
case "XDROP": {
const indexText = state.stack.pop();
const index = literalIndex(indexText);
if (Number.isFinite(index) && index >= 0 && index < state.stack.length) {
const removeAt = state.stack.length - 1 - index;
state.stack.splice(removeAt, 1);
} else {
const removed = state.stack.pop();
if (removed !== undefined) {
state.packedValuesByExpression.delete(removed);
state.pointerTargetsByExpression.delete(removed);
}
replaceDynamicStackSurvivors(state, "xdrop_remaining", indexText);
state.statements.push(`// xdrop stack[${indexText ?? "???"}] (dynamic index, stack may be imprecise)`);
}
return true;
}
default:
return false;
}
}