/
calc.js
76 lines (65 loc) · 1.9 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
/*
* simple calculator
*
* Example
* node calc.js <expression>
*/
"use strict";
// const lq = require("@loquat/simple");
const lq = require("../index");
// skips trailing spaces
const spaces = lq.spaces.label("");
function lexeme(parser) {
return parser.skip(spaces);
}
function symbol(str) {
return lexeme(lq.string(str).try());
}
// <number> (non-negative)
const numberRegExp = /(0|[1-9]\d*)(\.\d*)?([Ee][+-]?\d*)?/u;
const number = lexeme(lq.regexp(numberRegExp)).map(Number).label("number");
// <term> ::= <number> | "(" <expr> ")"
const lparen = symbol("(");
const rparen = symbol(")");
const term = lq.lazy(() => number.or(expr.between(lparen, rparen)));
// <expr1> ::= "+" <term> | "-" <term> | <term>
// <expr2> ::= <expr1> ** <expr2> | <expr1>
// <expr3> ::= <expr3> "*" <expr2> | <expr3> "/" <expr2> | <expr2>
// <expr> ::= <expr> "+" <expr3> | <expr> "-" <expr3> | <expr3>
const plus = symbol("+").return(x => x);
const minus = symbol("-").return(x => -x);
const pow = symbol("**").return((x, y) => Math.pow(x, y));
const mul = symbol("*").return((x, y) => x * y);
const div = symbol("/").return((x, y) => x / y);
const add = symbol("+").return((x, y) => x + y);
const sub = symbol("-").return((x, y) => x - y);
const expr = lq.buildExpressionParser(
[
[
lq.Operator.prefix(plus),
lq.Operator.prefix(minus),
],
[
lq.Operator.infix(pow, lq.OperatorAssoc.RIGHT),
],
[
lq.Operator.infix(mul, lq.OperatorAssoc.LEFT),
lq.Operator.infix(div, lq.OperatorAssoc.LEFT),
],
[
lq.Operator.infix(add, lq.OperatorAssoc.LEFT),
lq.Operator.infix(sub, lq.OperatorAssoc.LEFT),
],
],
term
);
const calc = spaces.and(expr).left(lq.eof);
function parse(src) {
const result = lq.parse(calc, "", src);
if (result.success) {
console.log(result.value);
} else {
console.error(result.error.toString());
}
}
parse(process.argv[2]);