Skip to content

Instantly share code, notes, and snippets.

@JohnEarnest
Created March 31, 2017 03:05
Show Gist options
  • Save JohnEarnest/19f13638949ea86e47e3a199434a7677 to your computer and use it in GitHub Desktop.
Save JohnEarnest/19f13638949ea86e47e3a199434a7677 to your computer and use it in GitHub Desktop.
A compact set of PEG parser combinators in ES6.
// PEG parser combinators
const lookup = x => typeof x == 'string' ? eval(x) : x
const match = (x, y, z) => x ? { c:y, v:z } : { e: true }
const eof = s => match(s.length, 0, '')
const char = s => match(s.length, 1, s[0])
const not = g => s => match(g(s).e, 0, '')
const has = g => s => match(!g(s).e, 0, '')
const oneof = t => s => match(t.indexOf(s[0]) >= 0, 1, s[0])
const lit = t => s => match(!s.indexOf(t), t.length, t)
const option = g => choose(g, lit(''))
const prod = (g, f) => s => (r => r.e ? r : { c:r.c, v:f(r.v) })(g(s))
const choose = (...a) => s => a.reduce((x, y) => x.e ? y(s) : x, { e: true })
const seq = (...a) => s => a.reduce(function(x, y) {
var r = x.e ? x : lookup(y)(s.slice(x.c))
return r.e ? r : { c:x.c+r.c, v:x.v.concat([r.v]) }
}, { c:0, v:[] })
// usage example:
const noun = oneof('abcdefghijklmnopqrstuvwxyz')
const xcolon = x => prod(seq(oneof(x), option(lit(':'))), x => x.join(''))
const verb = xcolon('+-*%!&|<>=~,^#_$?@.')
const adverb = xcolon('\'\\/')
const term = choose(prod(seq(lit('('), 'kexpr', lit(')')), x => x[1]), noun)
const kexpr = choose(
prod(seq(term, adverb, 'kexpr'), x => [x[1], x[0], x[2]]),
prod(seq(term, verb, 'kexpr'), x => [x[1], x[0], x[2]]),
seq(adverb, 'kexpr'),
seq(verb, 'kexpr'),
term
)
const test = x => console.log(JSON.stringify(x.v))
test(kexpr('+b'))
test(kexpr('a+b'))
test(kexpr('(a+b)*c'))
test(kexpr('a+b*c'))
test(kexpr('f/b+c'))
test(kexpr('a+f/b+c'))
test(kexpr('(a+f)/b+c'))
test(kexpr('a+f/*:\'z-w'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment