UNPKG

@masala/parser

Version:
147 lines (112 loc) 4.05 kB
import { getMathGenLex } from '../../lib/genlex/genlex' import { F } from '../../lib/parsec/index' import stream from '../../lib/stream/index' /* Implementing general solution : E -> T E' E' -> + TE' | eps T -> F T' T' -> * FT' | eps F -> NUMBER | ( E ) (https://en.wikipedia.org/wiki/Operator-precedence_parser) * Expr -> SubExpr then OptYieldExpr * OptYieldExpr -> YieldExpr.opt() * YieldExpr -> + then SubExpr then YieldExpr * PriorExpr -> Terminal then OptPriorExpr * OptPriorExpr -> PriorExpr.opt() * PriorExpr -> * then Terminal then OptPriorExpr * Terminal -> (Expr)| Number | -Terminal | Expr // care of priority ! */ // tokens const genlex = getMathGenLex() const { number, plus, minus, mult, div, open, close } = genlex.tokens() const priorToken = () => mult.or(div) const yieldToken = () => plus.or(minus) function terminal() { return parenthesis().or(number).or(negative()).or(F.lazy(expression)) } function negative() { return minus .drop() .then(F.lazy(terminal)) .single() .map((x) => -x) } function parenthesis() { return open.drop().then(F.lazy(expression)).then(close.drop()).single() } function expression() { return priorExpr().flatMap(optYieldExpr) } function optYieldExpr(left) { return yieldExpr(left) .opt() .map((opt) => (opt.isPresent() ? opt.get() : left)) } function yieldExpr(left) { return yieldToken() .then(priorExpr()) .array() .map(([token, right]) => (token === '+' ? left + right : left - right)) .flatMap(optYieldExpr) } function priorExpr() { return terminal().flatMap(optSubPriorExp) } function optSubPriorExp(priorValue) { return subPriorExpr(priorValue) .opt() .map((opt) => (opt.isPresent() ? opt.get() : priorValue)) } function subPriorExpr(priorValue) { return priorToken() .then(terminal()) .array() .map(([token, left]) => token === '*' ? priorValue * left : priorValue / left, ) .flatMap(optSubPriorExp) } function multParser() { const parser = expression() return genlex.use(parser.then(F.eos().drop()).single()) } export default { setUp: function (done) { done() }, 'expect multExpr to make mults': function (test) { let parsing = multParser().parse(stream.ofString('3 * 4')) test.equal(parsing.value, 12, 'simple multiplication') parsing = multParser().parse(stream.ofString('14 / 4')) test.equal(parsing.value, 3.5, 'simple division') parsing = multParser().parse(stream.ofString('14 / 4*3 ')) test.equal(parsing.value, 10.5, 'combine mult and div') parsing = multParser().parse(stream.ofString('14 / 4*3 /2* 2 ')) test.equal(parsing.value, 10.5, 'combine more mult and div') test.done() }, 'expect multExpr to make negative priorities': function (test) { let parsing = multParser().parse(stream.ofString('3 * -4')) test.equal(parsing.value, -12, 'negative multiplication') test.done() }, 'expect Expr to be inside parenthesis': function (test) { let parsing = multParser().parse(stream.ofString('3 * (4)')) test.equal(parsing.value, 12, 'simple parenthesis expr') parsing = multParser().parse(stream.ofString('3 * (2*4)')) test.equal(parsing.value, 24, 'more complexe parenthesis expr') parsing = multParser().parse(stream.ofString('3 * (2*(4))')) test.equal(parsing.value, 24, 'deep parenthesis expr') test.done() }, 'expect + and * to respect priorities': function (test) { let parsing = multParser().parse(stream.ofString('3 +2*4 ')) test.equal(parsing.value, 11, 'simple multiplication') test.done() }, 'expect - and / to respect priorities': function (test) { let parsing = multParser().parse(stream.ofString('3 + -4/2*5 ')) test.equal(parsing.value, -7, 'bad priorities') test.done() }, }