rpn-cli
Version:
Command line RPN calculator using math.js engine. example: '$ rpn 1 2 +'
299 lines (290 loc) • 8.14 kB
JavaScript
// Generated by CoffeeScript 2.4.1
/* jshint -W083 */
var E, T, all, create, display, displayElement, math, rpn;
({create, all} = require('mathjs'));
//math=require 'mathjs'
//math.evaluate=math.evaluateuate
math = create(all);
T = console.log;
E = console.error;
displayElement = function(x) {
if (typeof x === 'string') {
return math.evaluate(x).toString();
} else {
return x.toString();
}
};
display = function(stack) {
return stack.map(displayElement);
};
rpn = function(formulaOrOptions) {
var applyAll, debugConsole, debugText, f, f1, f2, formula, i, l, nElements, p, precision, r, ref, ref1, ref2, registerNumber, registers, stack, step, v;
formula = [];
debugConsole = null;
precision = 14;
switch (false) {
case !Array.isArray(formulaOrOptions):
formula = formulaOrOptions;
break;
case !(typeof formulaOrOptions === 'object' && Array.isArray(formulaOrOptions.formula)):
formula = (ref = formulaOrOptions.formula) != null ? ref : formula;
debugConsole = (ref1 = formulaOrOptions.debugConsole) != null ? ref1 : debugConsole;
precision = (ref2 = formulaOrOptions.precision) != null ? ref2 : precision;
break;
default:
throw 'rpn:invalid formula';
}
math.config({
number: 'BigNumber',
precision: precision
});
step = 0;
stack = [];
registers = {};
applyAll = false;
while (formula.length > 0) {
p = formula.shift();
switch (false) {
case typeof p !== 'number':
stack.push(math.evaluate(p));
break;
case p !== '+' && p !== '-' && p !== 'x' && p !== '/' && p !== '%' && p !== '^' && p !== '*' && p !== 'v':
if (stack.length < 2) {
throw `'${p}' needs 2 values`;
}
r = stack.pop();
f2 = function(p, l, r) {
var ex;
ex = '';
switch (p) {
case '+':
ex = `${l} + ${r}`;
break;
case '-':
ex = `${l} - ${r}`;
break;
case 'x':
case '*':
ex = `${l} * ${r}`;
break;
case '/':
ex = `${l} / ${r}`;
break;
case '%':
ex = `${l} % ${r}`;
break;
case '^':
ex = `pow(${l},${r})`;
break;
case 'v':
ex = `nthRoot(${l},${r})`;
}
return math.evaluate(ex);
};
if (applyAll) {
stack = stack.map(function(l) {
return f2(p, l, r);
});
applyAll = false;
} else {
l = stack.pop();
stack.push(f2(p, l, r));
}
break;
case p !== 'n' && p !== 'i' && p !== 'a' && p !== '_' && p !== '=' && p !== 'f' && p !== '!' && p !== 'l' && p !== 'L' && p !== 's' && p !== 'S' && p !== 'c' && p !== 'C' && p !== 't' && p !== 'T':
if (stack.length < 1) {
throw `'${p}' needs 1 values`;
}
f1 = function(p, l) {
var ex;
ex = '';
switch (p) {
case 'n':
ex = `0-${l}`;
break;
case 'i':
ex = `1/${l}`;
break;
case 'a':
ex = `abs(${l})`;
break;
case 'l':
ex = `log(${l})`;
break;
case 'L':
ex = `exp(${l})`;
break;
case '_':
ex = `floor(${l})`;
break;
case '=':
ex = `round(${l})`;
break;
case 'f':
case '!':
ex = `factorial(${l})`;
break;
case 's':
ex = `sin(${l})`;
break;
case 'c':
ex = `cos(${l})`;
break;
case 't':
ex = `tan(${l})`;
break;
case 'S':
ex = `asin(${l})`;
break;
case 'C':
ex = `acos(${l})`;
break;
case 'T':
ex = `atan(${l})`;
}
return math.evaluate(ex);
};
if (applyAll) {
stack = stack.map(function(l) {
return f1(p, l);
});
applyAll = false;
} else {
l = stack.pop();
stack.push(f1(p, l));
}
break;
case p !== 'w':
if (stack.length < 2) {
throw `'${p}' needs 2 values`;
}
switch (p) {
case 'w':
r = stack.pop();
l = stack.pop();
stack.push(r);
stack.push(l);
}
break;
case p !== 'r' && p !== 'R' && p !== 'd' && p !== 'p' && p !== 'D' && p !== 'V':
if (stack.length < 1) {
throw `'${p}' needs 1 values`;
}
switch (p) {
case 'r':
r = stack.pop();
stack.unshift(r);
break;
case 'R':
r = stack.shift();
stack.push(r);
break;
case 'd':
r = stack.pop();
break;
case 'p':
r = stack.pop();
stack.push(math.clone(r));
stack.push(r);
break;
case 'D':
stack = [];
break;
case 'V':
stack = [stack.pop()];
}
break;
case p !== 'P':
stack.push('PI');
break;
case p !== 'F':
applyAll = true;
break;
case p !== 'q' && p !== 'Q' && p !== 'y' && p !== 'Y':
nElements = {
'q': 1,
'Q': 2,
'y': 0,
'Y': 1
};
if (stack.length < nElements[p]) {
throw `'${p}' needs ${nElements[p]} values`;
}
registerNumber = '0';
if (p === 'q' || p === 'Q') {
registerNumber = displayElement(stack.pop());
}
switch (false) {
case p !== 'Q' && p !== 'Y':
registers[registerNumber] = stack.pop();
break;
case p !== 'q' && p !== 'y':
if (!registers.hasOwnProperty(registerNumber)) {
throw `Register ${registerNumber} is empty`;
}
stack.push(math.clone(registers[registerNumber]));
}
break;
case p !== 'N' && p !== 'M' && p !== 'm' && p !== 'A' && p !== 'X':
if (stack.length < 1) {
throw `'${p}' needs 1 values`;
}
switch (p) {
case 'N':
f = function(i, x) {
return i.add(1);
};
i = math.evaluate(0);
break;
case 'M':
f = function(i, x) {
return math.max(i, x);
};
i = math.evaluate('-Infinity');
break;
case 'm':
f = function(i, x) {
return math.min(i, x);
};
i = math.evaluate('Infinity');
break;
case 'A':
f = function(i, x) {
return math.add(i, x);
};
i = math.evaluate(0);
break;
case 'X':
f = function(i, x) {
return math.multiply(i, x);
};
i = math.evaluate(1);
}
stack.push(stack.reduce(f, i));
break;
case !p.match(/^(-?[0-9.]+(e-?[0-9.]+)?)$/):
stack.push(math.evaluate(p));
break;
default:
throw `'${p}' unknown op`;
}
step++;
if (debugConsole != null) {
debugText = `step ${step}:[${p}] ${display(stack).join(' ')}${(applyAll ? '(F)' : '')}`;
if (Object.keys(registers).length > 0) {
debugText += `\t:reg{${((function() {
var results;
results = [];
for (i in registers) {
v = registers[i];
results.push(`${i}:${displayElement(v)}`);
}
return results;
})()).join(' ')}}`;
}
debugConsole(debugText);
}
}
return display(stack);
};
module.exports = rpn;