Created
October 6, 2015 00:07
-
-
Save wavebeem/b0d599377256b7fcc6b0 to your computer and use it in GitHub Desktop.
This does not actually generate the correct AST because I was using arrays and dang does that get confusing over time, but this helped me figure out how to parse my binary operators correctly while also including unary operators at the right precedence level and make foo.bar foo[bar] and foo(bar) all chain together correctly.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const util = require("util"); | |
const P = require("parsimmon"); | |
console.log(Date() + "\n"); | |
const _ = P.optWhitespace; | |
const tag = name => rest => | |
[name].concat(rest) | |
const combine = (acc, xs) => | |
[xs[0], acc].concat(xs.slice(1)); | |
const spaced = p => | |
_.then(p).skip(_); | |
const word = str => | |
spaced(P.string(str)); | |
const arrow = length => { | |
let s = ""; | |
let n = length; | |
while (n-- > 0) { | |
s += "-"; | |
} | |
s += "^"; | |
return s; | |
}; | |
const wrap = (l, x, r) => | |
word(l).then(x).skip(word(r)); | |
const Separator = word(","); | |
const list0 = p => | |
P.seq( | |
p, | |
Separator.then(p).many() | |
) | |
.map(x => [x[0]].concat(x[1])) | |
.or(P.of([])); | |
const squish = xs => | |
[xs[0]].concat(xs.slice(1)[0]); | |
const bh = (ops, next) => { | |
const opParsers = ops | |
.split(" ") | |
.map(op => word(op)); | |
const combinedOpParser = P.alt.apply(P, opParsers); | |
return P.seq( | |
next, | |
P.seq(combinedOpParser, next).atLeast(1) | |
) | |
.map(xs => { | |
const firstArgument = xs[0]; | |
const opSections = xs[1]; | |
const combineOps = (acc, x) => | |
["BinOp", x[0], acc].concat(x.slice(1)); | |
return opSections.reduce(combineOps, firstArgument); | |
}) | |
.or(next); | |
}; | |
const parsers = { | |
Expr: ps => | |
ps.OrExpr, | |
OrExpr: ps => bh("or", ps.AndExpr), | |
AndExpr: ps => bh("and", ps.EqExpr), | |
EqExpr: ps => bh("== !=", ps.HasExpr), | |
HasExpr: ps => | |
ps.UnaryExpr, | |
UnaryExpr: ps => | |
P.alt( | |
word("-").then(ps.PropCallExpr).map(tag("Negate")), | |
word("not").then(ps.PropCallExpr).map(tag("Not")), | |
ps.PropCallExpr | |
), | |
PropCallExpr: ps => | |
P.seq( | |
ps.BasicExpr, | |
P.alt( | |
wrap("(", list0(ps.Expr), ")").map(tag("FnCall")), | |
wrap("[", ps.Expr, "]").map(tag("ComputedMember")), | |
word(".").then(ps.Identifier).map(tag("DotMember")) | |
).many() | |
) | |
.or(ps.BasicExpr), | |
BasicExpr: ps => | |
ps.Literal.or(ps.ParenExpr), | |
Literal: ps => | |
P.alt(ps.Identifier, ps.Number), | |
Identifier: ps => | |
P.regex(/[a-zA-Z][a-zA-Z0-9]*/) | |
.desc("Identifier") | |
.map(tag("Identifier")), | |
Number: ps => | |
P.regex(/0-9+/) | |
.desc("Number") | |
.map(tag("Number")), | |
ParenExpr: ps => | |
word("(") | |
.then(ps.Expr) | |
.skip(word(")")), | |
}; | |
Object | |
.keys(parsers) | |
.forEach(k => { | |
// Injected parsers as dependency. | |
const bound = parsers[k].bind(null, parsers); | |
parsers[k] = P.lazy(bound); | |
}); | |
Object.freeze(parsers); | |
const inspectOpts = | |
Object.freeze({ | |
depth: null, | |
colors: true | |
}); | |
const inspect = x => | |
util.inspect(x, inspectOpts); | |
const show = x => { | |
console.log(inspect(x)); | |
}; | |
const main = () => { | |
// const text = "\\"; | |
// const text = "p1.x or p2.y or f(q) or global.document.querySelector(css)"; | |
// const text = "p1.x + p2.y * f(q) * global.document.querySelector(css)"; | |
// const text = "-player.vx * modifiers(player).vx"; | |
// const text = "modifiers(player).vx"; | |
// const text = "a.b.x(x).q + q.w"; | |
// const text = "a and b or c and d and q or x == z != a == b"; | |
// const text = "a == b or c or z"; | |
// const text = "a or b or c"; | |
// const text = "-a or b or c"; | |
// const text = "a.b()" | |
// const text = "foo(a)"; | |
// const text = "f.a"; | |
// const text = "a.b[c](d).x(x)(x)[y][y][z].q"; | |
const text = "foo.bar[baz].quux().q[q]"; | |
const result = parsers.Expr.parse(text); | |
// const result = parsers.GetPropExpr.parse(text); | |
// const result = parsers.FnCall.parse(text); | |
if (result.status) { | |
show(result.value); | |
} else { | |
show(result); | |
console.log(); | |
console.log(text); | |
console.log(arrow(result.index)); | |
} | |
}; | |
main(); | |
module.exports = word; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment