Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

parsimmonで数式の解析

(この記事は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);