Skip to content

Instantly share code, notes, and snippets.

@codehz
Last active May 29, 2017 09:53
Show Gist options
  • Save codehz/06f4cf8aacf7deba51303ac45b736466 to your computer and use it in GitHub Desktop.
Save codehz/06f4cf8aacf7deba51303ac45b736466 to your computer and use it in GitHub Desktop.
Simple ans naivie parser combinator for javascript
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