-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcalc.js
128 lines (110 loc) · 3.85 KB
/
calc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* Simple calculator implemented using JavaScript arithmetic.
*
* Fully-featured calculator using an AST-based math interpreted:
* https://github.com/D10f/calculator_parser
*
* Codepen with graphical interface:
* https://codepen.io/D10f/pen/XWoMJZE
*
* TODO:
* - Fix Bugs.
* - Implement modulus operator.
* - (web version only) Handle input.
* - (web version only) Handle keyboard events.
* - (web version only) Allow delete single characters from input.
* - (web version only) Implement input history.
* - (web version only) Display result of operation separately from input;
*
* KNOWN BUGS:
* - Divide by 0 returns Infinity.
* - (web version only) Cannot handle negative numbers.
*/
const assert = require("assert");
function calculate(expression) {
let [left, operator, ...right] = expression;
if (expression.length === 0) return 1;
if (expression.length === 1) return Number(left) || 1;
if (left === "(") {
const _expression = reduceExpression(expression);
return calculate([calculate(_expression[0]), ..._expression[1]]);
} else if (left === ")") {
throw new Error("Malformed expression!");
}
if (right[0] === "(") {
const _expression = reduceExpression(right);
right = [calculate(_expression[0]), ..._expression[1]];
} else if (right[0] === ")") {
throw new Error("Malformed expression!");
}
let _left;
let _right;
switch (operator) {
case "+":
return left + calculate(right);
case "-":
return left - calculate(right);
case "*":
_left = left * right[0];
_right = right.slice(1);
return _right.length === 0 ? _left : calculate([_left, ..._right]);
case "/":
_left = left / right[0];
_right = right.slice(1);
return _right.length === 0 ? _left : calculate([_left, ..._right]);
case "^":
_left = left ** right[0];
_right = right.slice(1);
return _right.length === 0 ? _left : calculate([_left, ..._right]);
}
}
function reduceExpression(expression) {
let parenCount = 0;
for (let i = 0; i < expression.length; i++) {
switch (expression[i]) {
case ")":
parenCount--;
break;
case "(":
parenCount++;
break;
}
if (parenCount === 0)
return [expression.slice(1, i), expression.slice(i + 1)];
}
throw new Error("Malformed expression!");
}
assert.throws(() => calculate(["(", 2, "+", 2]), "Error: Malformed expression");
assert.throws(
() => calculate([")", 2, "+", 2]),
"Error: Malformed expression!",
);
assert.throws(() => calculate([2, "+", ")"]), "Error: Malformed expression!");
assert.strictEqual(calculate([]), 1);
assert.strictEqual(calculate(["+"]), 1);
assert.strictEqual(calculate([23]), 23);
assert.strictEqual(calculate([5, "+", 6]), 11);
assert.strictEqual(calculate([5, "+", 6, "*", 8]), 53);
assert.strictEqual(calculate([5, "*", 6, "+", 8]), 38);
assert.strictEqual(calculate([5, "+", 6, "*", 8, "*", 2]), 101);
assert.strictEqual(calculate([5, "*", 6, "+", 8, "*", 2]), 46);
assert.strictEqual(calculate([5, "*", 6, "/", 8, "*", 2]), 7.5);
assert.strictEqual(calculate([5, "*", 6, "/", "(", 8, "*", 2, ")"]), 1.875);
assert.strictEqual(calculate([5, "*", 6, "-", 8, "/", 2]), 26);
assert.strictEqual(calculate([5, "^", 2]), 25);
assert.strictEqual(calculate([-20, "+", 33]), 13);
assert.strictEqual(calculate([-20, "-", 33]), -53);
assert.strictEqual(calculate(["(", 5, "+", 6, ")", "*", 8]), 88);
assert.strictEqual(
calculate(["(", 5, "+", "(", 6, "+", 2, "^", 2, ")", ")", "*", 8]),
120,
);
assert.strictEqual(
calculate(["(", 5, "*", "(", 6, "+", 2, "^", 2, ")", ")", "*", 8]),
400,
);
assert.strictEqual(calculate([8, "*", "(", 2, "+", 2, ")"]), 32);
assert.strictEqual(calculate([37, "*", 5]), 185);
assert.strictEqual(calculate([37, "*", 0]), 0);
assert.strictEqual(calculate([37, "/", 0]), Infinity);
console.log("All assertions passed!");