import { stripOuterParens, wrapExpression } from "./high-level-stack-shared.js";
import { warnStackUnderflow } from "./high-level-state.js";
const BINARY_OPERATORS = {
ADD: "+",
SUB: "-",
MUL: "*",
DIV: "/",
MOD: "%",
AND: "&",
OR: "|",
XOR: "^",
EQUAL: "==",
NOTEQUAL: "!=",
LT: "<",
LE: "<=",
GT: ">",
GE: ">=",
BOOLAND: "&&",
BOOLOR: "||",
NUMEQUAL: "==",
NUMNOTEQUAL: "!=",
CAT: "cat",
};
export function tryBinaryExpression(state, instruction) {
const { stack } = state;
const mnemonic = instruction.opcode.mnemonic;
const operator = BINARY_OPERATORS[mnemonic];
if (operator) {
if (!ensureStack(state, instruction, 2)) return true;
const right = stack.pop();
const left = stack.pop();
stack.push(`${wrapExpression(left)} ${operator} ${wrapExpression(right)}`);
return true;
}
switch (mnemonic) {
case "POW": {
if (!ensureStack(state, instruction, 2)) return true;
const exponent = stack.pop();
const base = stack.pop();
stack.push(`pow(${base}, ${exponent})`);
return true;
}
case "MODPOW": {
if (!ensureStack(state, instruction, 3)) return true;
const modulus = stack.pop();
const exponent = stack.pop();
const base = stack.pop();
stack.push(`modpow(${base}, ${exponent}, ${modulus})`);
return true;
}
case "MODMUL": {
if (!ensureStack(state, instruction, 3)) return true;
const modulus = stack.pop();
const right = stack.pop();
const left = stack.pop();
stack.push(`modmul(${left}, ${right}, ${modulus})`);
return true;
}
case "MAX": {
if (!ensureStack(state, instruction, 2)) return true;
const right = stack.pop();
const left = stack.pop();
stack.push(`max(${left}, ${right})`);
return true;
}
case "MIN": {
if (!ensureStack(state, instruction, 2)) return true;
const right = stack.pop();
const left = stack.pop();
stack.push(`min(${left}, ${right})`);
return true;
}
case "WITHIN": {
if (!ensureStack(state, instruction, 3)) return true;
const upper = stack.pop();
const lower = stack.pop();
const value = stack.pop();
stack.push(`within(${value}, ${lower}, ${upper})`);
return true;
}
case "LEFT": {
if (!ensureStack(state, instruction, 2)) return true;
const count = stack.pop();
const value = stack.pop();
stack.push(`left(${value}, ${count})`);
return true;
}
case "RIGHT": {
if (!ensureStack(state, instruction, 2)) return true;
const count = stack.pop();
const value = stack.pop();
stack.push(`right(${value}, ${count})`);
return true;
}
case "SHL": {
if (!ensureStack(state, instruction, 2)) return true;
const shift = stripOuterParens(stack.pop());
const value = stripOuterParens(stack.pop());
stack.push(`${wrapExpression(value)} << ${shift}`);
return true;
}
case "SHR": {
if (!ensureStack(state, instruction, 2)) return true;
const shift = stripOuterParens(stack.pop());
const value = stripOuterParens(stack.pop());
stack.push(`${wrapExpression(value)} >> ${shift}`);
return true;
}
default:
return false;
}
}
function ensureStack(state, instruction, needed) {
if (state.stack.length >= needed) return true;
warnStackUnderflow(state, instruction, needed);
return false;
}