Last active
May 29, 2017 09:53
-
-
Save codehz/06f4cf8aacf7deba51303ac45b736466 to your computer and use it in GitHub Desktop.
Simple ans naivie parser combinator for javascript
This file contains hidden or 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
class Stream { | |
constructor(tokens) { | |
this.tokens = tokens | |
this.pos = 0 | |
} | |
get next() { | |
return this.tokens[this.pos++] || '' | |
} | |
cancel() { | |
this.pos-- | |
} | |
get done() { | |
return this.pos >= this.tokens.length | |
} | |
get rest() { | |
return this.tokens.slice(this.pos) | |
} | |
transaction(fn) { | |
const rpos = this.pos | |
const ret = fn() | |
if (ret === failure) { | |
this.pos = rpos | |
return failure | |
} | |
return ret | |
} | |
} | |
const preventRec = (fn, stack = []) => stream => { | |
if (stack.includes(stream.pos)) return ignore | |
stack.push(stream.pos) | |
const ret = fn(stream) | |
stack.pop() | |
return ret | |
} | |
const failure = Symbol('failure') | |
const ignore = Symbol('ignore') | |
const eq = str => stream => stream.transaction(() => stream.next == str ? ignore : failure) | |
const re = regex => fn => stream => stream.transaction(() => { | |
const ret = stream.next.match(regex) | |
if (!ret) return failure | |
return fn(ret) | |
}) | |
const assembly = (...parsers) => stream => stream.transaction(() => { | |
let result = ignore | |
for (const parser of parsers) { | |
result = parser(result)(stream) | |
if (result == failure) | |
return failure | |
} | |
return result | |
}) | |
const chain = (...parsers) => fn => stream => stream.transaction(() => { | |
const arr = [] | |
for (const parser of parsers) { | |
const result = parser(stream) | |
if (result == failure) | |
return failure | |
if (result != ignore) | |
arr.push(result) | |
} | |
return fn(...arr) | |
}) | |
const choice = (...parsers) => stream => { | |
for (const parser of parsers) { | |
const result = parser(stream) | |
if (result != failure) | |
return result | |
} | |
return failure | |
} | |
const repeatRange = parser => (low, high = Number.MAX_SAFE_INTEGER) => stream => stream.transaction(() => { | |
const arr = [] | |
let i = 0 | |
for (; i < high; i++) { | |
const result = parser(stream) | |
if (result == failure) | |
break | |
if (result != ignore) | |
arr.push(result) | |
} | |
if (i < low || i > high) return failure | |
return arr | |
}) | |
const repeat = parser => number => repeatRange(parser)(number, number) | |
const many = parser => repeatRange(parser)(1) | |
const zeroOrMany = parser => repeatRange(parser)(0) | |
const maybe = parser => repeatRange(parser)(0, 1) | |
const must = parser => repeatRange(parser)(1, 1) | |
const packer = parser => fn => stream => { | |
const ret = parser(stream) | |
if (ret === failure) | |
return failure | |
return fn(ret) | |
} | |
const $log = (...param) => stream => { | |
console.log(...param, stream.rest) | |
return ignore | |
} | |
const $lazy = fn => a => b => fn(a)(b) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment