website/experiments/rpncalc-v2/calc.js

158 lines
3.2 KiB
JavaScript
Executable File

// Reads the input box for a RPN expression
// Calculates result
// Outputs it as nicely formatted boxes.
function calculateFromInput() {
var expr = document.getElementById("input").value;
var output = document.getElementById("output")
var result = calculateRPN(expr.split(" "));
output.innerHTML = ""; // Clear the output div
result.stack.forEach(function(num) {
num = num.toString().replace("NaN", "Error");
var box = createBox(num);
box.style["max-width"] = num.length + "em";
output.appendChild(box);
});
if (result.errors.length > 0) { // If errors exist output them separated by line breaks.
result.errors.forEach(error => {
output.appendChild(document.createTextNode(error.toString()))
output.appendChild(document.createElement("br"))
})
}
}
function add(x, y) {
return x + y;
}
function multiply(x, y) {
return x * y;
}
function divide(x, y) {
return x / y;
}
function flip(x, y) {
return [y, x];
}
function subtract(x, y) {
return x - y;
}
function sum(vals) {
var acc = 0;
vals.forEach(function(el) {
acc += el;
});
return acc;
}
function range(low, high) {
var r = [];
for (var i = low; i <= high; i++) {
r.push(i);
}
return r;
}
function createBox(contents) {
var el = document.createElement("div");
el.setAttribute("class", "stack-box");
el.innerText = contents;
return el;
}
// Takes a two-argument function and lifts it to a binary stack op
function binaryOp(fun) {
return function(stack) {
var x = stack.pop();
var y = stack.pop();
stack.push(fun(y, x));
return stack;
}
}
// Takes a function and lifts it to a unary op using a stack. Returns new stack.
function unaryOp(fun) {
return function(stack) {
var x = stack.pop();
stack.push(fun(x));
return stack;
}
}
// Takes a function and lifts it to an op which takes the entire stack and reduces it to one value. Returns new stack.
function greedyOp(fun) {
return function(stack) {
return [fun(stack)];
}
}
// Lifs to an operator which takes two values and adds an array of them to the stack. Returns a new stack.
function binaryMultioutOp(fun) {
return function(stack) {
var x = stack.pop();
var y = stack.pop();
return stack.concat(fun(y, x));
}
}
function calculateRPN(tokens) {
var stack = [];
var errors = [];
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
switch (token) {
case "+":
stack = binaryOp(add)(stack);
break;
case "*":
stack = binaryOp(multiply)(stack);
break;
case "/":
stack = binaryOp(divide)(stack);
break;
case "-":
stack = binaryOp(subtract)(stack);
break;
case "range":
stack = binaryMultioutOp(range)(stack);
break;
case "flip":
stack = binaryMultioutOp(flip)(stack);
break;
case "sum":
stack = greedyOp(sum)(stack);
break;
case "": // This may be entered by accident.
break;
default:
var parsed = parseFloat(token);
if (parsed === parsed) { // check for NaNs from a failed parse
stack.push(parsed);
} else {
errors.push("Token '" + token + "' (#" + (i + 1) + ")" + " invalid");
}
}
}
return {stack: stack, errors: errors};
}