(この記事はQiitaで僕が書いたものを移行した記事です。記事中のコメントはQiitaの該当記事を参照ください)
parsimmonで数式をパースしました。あとは操車場アルゴリズムでも適用してやれば計算も可能です。
const Parsimmon = require('parsimmon'); const regex = Parsimmon.regex; const string = Parsimmon.string; const optWhitespace = Parsimmon.optWhitespace; const lazy = Parsimmon.lazy; const alt = Parsimmon.alt; const seq = Parsimmon.seq; const parser = lazy(() => alt(functions, constNum, paren, num, operator, comma).skip(optWhitespace).many()); const paren = lazy(() => lparen.then(parser).skip(rparen)); const plus = string('+'); const minus = string('-'); const mul = alt(string('*'), string('×')); const div = alt(string('/'), string('÷')); const factorial = string('!'); const operator = alt(plus, minus, mul, div, factorial); const num = alt(regex(/-?[1-9][0-9]*/), regex(/0\.[0-9]+/)).map(n => parseInt(n, 10)); const constNum = alt( alt(regex(/pi/i), string('π')).result(Math.PI), regex(/e/i).result(Math.E), alt(regex(/infinity/i), regex(/inf/i), string('∞')).result(Infinity), alt(regex(/phi/i), string('φ')).result((1 + Math.sqrt(5)) / 2) ); const comma = string(','); const functions = seq(alt( regex(/abs/i), regex(/acos/i), regex(/asin/i), regex(/atan/i), regex(/ceiling/i), regex(/cos/i), regex(/exp/i), regex(/exp/i), regex(/fib/i), regex(/floor/i), regex(/ln/i), regex(/log/i), regex(/log10/i), regex(/log2/i), regex(/loge/i), regex(/pow/i), regex(/random/i), regex(/root/i), regex(/sin/i), regex(/tan/i) ), paren); const lparen = string('('); const rparen = string(')'); console.log(parser.parse('1+2+12').value); console.log(parser.parse('(1+2)*3').value); console.log(parser.parse('(1+2)*(3+4)').value); console.log(parser.parse('(1*(2+3))*(3+4)').value); console.log(parser.parse('-2*3').value); console.log(parser.parse('-2*(-3)').value); console.log(parser.parse('2*(-3)').value); console.log(parser.parse('(-2)*(-3)').value); console.log(parser.parse('(-2)-(-3)').value); console.log(parser.parse('3!!!').value); console.log(parser.parse('sin(3pi)').value); console.log(parser.parse('exp(3pi)').value); console.log(parser.parse('log(10,100*3)').value); console.log(parser.parse('e').value); console.log(parser.parse('π').value);