export parser combinators
This commit is contained in:
parent
2baf53640d
commit
28e213d5c2
2 changed files with 19 additions and 16 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './parser.js'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Ok, Err, curry } from '../vendor/kojima/src/index.js'
|
import { Ok, Err, curry } from '../vendor/kojima/src/index.js'
|
||||||
|
|
||||||
class ParseError extends Error {
|
export class ParseError extends Error {
|
||||||
constructor(message, state, source) {
|
constructor(message, state, source) {
|
||||||
super(message)
|
super(message)
|
||||||
this.state = state
|
this.state = state
|
||||||
|
@ -44,15 +44,15 @@ const fork = ([tokens, state]) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const succeed = (v, [x, y]) => Ok(Tuple(x.concat(v), y))
|
export const succeed = (v, [x, y]) => Ok(Tuple(x.concat(v), y))
|
||||||
const fail = (msg, state, err = undefined) => Err(new ParseError(msg, state, err))
|
export const fail = (msg, state, err = undefined) => Err(new ParseError(msg, state, err))
|
||||||
const nth = (n, iter) => iter[n]
|
const nth = (n, iter) => iter[n]
|
||||||
const next = state => nth(1, state).next().value
|
const next = state => nth(1, state).next().value
|
||||||
const diff = (a, b) => b.slice(-Math.max(0, b.length - a.length))
|
const diff = (a, b) => b.slice(-Math.max(0, b.length - a.length))
|
||||||
const join = curry((delim, val) => val.join(delim))
|
const join = curry((delim, val) => val.join(delim))
|
||||||
const mapStr = curry((fn, str) => Array.from(str).map(v => fn(v)))
|
const mapStr = curry((fn, str) => Array.from(str).map(v => fn(v)))
|
||||||
|
|
||||||
const any = (...parsers) => state => {
|
export const any = (...parsers) => state => {
|
||||||
for (const parser of parsers) {
|
for (const parser of parsers) {
|
||||||
const [original, clone] = fork(state)
|
const [original, clone] = fork(state)
|
||||||
const result = parser(clone)
|
const result = parser(clone)
|
||||||
|
@ -64,11 +64,11 @@ const any = (...parsers) => state => {
|
||||||
return fail('no matching parsers', state)
|
return fail('no matching parsers', state)
|
||||||
}
|
}
|
||||||
|
|
||||||
const anyOf = curry((str, state) => (
|
export const anyOf = curry((str, state) => (
|
||||||
any(...mapStr(char, str))(state)
|
any(...mapStr(char, str))(state)
|
||||||
))
|
))
|
||||||
|
|
||||||
const seq = (...parsers) => state => {
|
export const seq = (...parsers) => state => {
|
||||||
let acc = Ok(state)
|
let acc = Ok(state)
|
||||||
|
|
||||||
for (const parser of parsers) {
|
for (const parser of parsers) {
|
||||||
|
@ -82,7 +82,7 @@ const seq = (...parsers) => state => {
|
||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
const map = curry((fn, parser, state) => {
|
export const map = curry((fn, parser, state) => {
|
||||||
return parser(state).bind(result => {
|
return parser(state).bind(result => {
|
||||||
try {
|
try {
|
||||||
const parsed = diff(state[0], result[0])
|
const parsed = diff(state[0], result[0])
|
||||||
|
@ -94,32 +94,33 @@ const map = curry((fn, parser, state) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const char = curry((ch, state) => (
|
export const char = curry((ch, state) => (
|
||||||
next(state) === ch ? succeed(ch, state) : fail(`could not parse ${ch} `, state)
|
next(state) === ch ? succeed(ch, state) : fail(`could not parse ${ch} `, state)
|
||||||
))
|
))
|
||||||
|
|
||||||
const anyChar = state => {
|
export const anyChar = state => {
|
||||||
const ch = next(state)
|
const ch = next(state)
|
||||||
return !!ch ? succeed(ch, state) : fail(`could not parse ${ch}`, state)
|
return !!ch ? succeed(ch, state) : fail(`could not parse ${ch}`, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
const string = curry((str, state) => (
|
export const str = curry((str, state) => (
|
||||||
map(
|
map(
|
||||||
join(''),
|
join(''),
|
||||||
seq(...mapStr(char, str))
|
seq(...mapStr(char, str))
|
||||||
)(state)
|
)(state)
|
||||||
))
|
))
|
||||||
|
|
||||||
const digit = anyOf(Digits)
|
export const digit = anyOf(Digits)
|
||||||
const lowerAlpha = anyOf(LowerAlpha)
|
export const lowerAlpha = anyOf(LowerAlpha)
|
||||||
const upperAlpha = anyOf(UpperAlpha)
|
export const upperAlpha = anyOf(UpperAlpha)
|
||||||
const alpha = anyOf(Alpha)
|
export const alpha = anyOf(Alpha)
|
||||||
const alphanumeric = anyOf(Alphanumeric)
|
export const alphanumeric = anyOf(Alphanumeric)
|
||||||
|
|
||||||
const maybe = curry((parser, state) => {
|
export const maybe = curry((parser, state) => {
|
||||||
const [original, clone] = fork(state)
|
const [original, clone] = fork(state)
|
||||||
const result = parser(clone)
|
const result = parser(clone)
|
||||||
return result.isOk ? result : succeed([], original)
|
return result.isOk ? result : succeed([], original)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const parse = curry((parser, input) => parser(State(input)))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue