diff --git a/src/index.js b/src/index.js index e69de29..7236d68 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,2 @@ +export * from './parser.js' + diff --git a/src/parser.js b/src/parser.js index 0981956..eca5eb8 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,6 +1,6 @@ import { Ok, Err, curry } from '../vendor/kojima/src/index.js' -class ParseError extends Error { +export class ParseError extends Error { constructor(message, state, source) { super(message) this.state = state @@ -44,15 +44,15 @@ const fork = ([tokens, state]) => { ) } -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 succeed = (v, [x, y]) => Ok(Tuple(x.concat(v), y)) +export const fail = (msg, state, err = undefined) => Err(new ParseError(msg, state, err)) const nth = (n, iter) => iter[n] const next = state => nth(1, state).next().value const diff = (a, b) => b.slice(-Math.max(0, b.length - a.length)) const join = curry((delim, val) => val.join(delim)) 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) { const [original, clone] = fork(state) const result = parser(clone) @@ -64,11 +64,11 @@ const any = (...parsers) => state => { return fail('no matching parsers', state) } -const anyOf = curry((str, state) => ( +export const anyOf = curry((str, state) => ( any(...mapStr(char, str))(state) )) -const seq = (...parsers) => state => { +export const seq = (...parsers) => state => { let acc = Ok(state) for (const parser of parsers) { @@ -82,7 +82,7 @@ const seq = (...parsers) => state => { return acc } -const map = curry((fn, parser, state) => { +export const map = curry((fn, parser, state) => { return parser(state).bind(result => { try { 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) )) -const anyChar = state => { +export const anyChar = state => { const ch = next(state) return !!ch ? succeed(ch, state) : fail(`could not parse ${ch}`, state) } -const string = curry((str, state) => ( +export const str = curry((str, state) => ( map( join(''), seq(...mapStr(char, str)) )(state) )) -const digit = anyOf(Digits) -const lowerAlpha = anyOf(LowerAlpha) -const upperAlpha = anyOf(UpperAlpha) -const alpha = anyOf(Alpha) -const alphanumeric = anyOf(Alphanumeric) +export const digit = anyOf(Digits) +export const lowerAlpha = anyOf(LowerAlpha) +export const upperAlpha = anyOf(UpperAlpha) +export const alpha = anyOf(Alpha) +export const alphanumeric = anyOf(Alphanumeric) -const maybe = curry((parser, state) => { +export const maybe = curry((parser, state) => { const [original, clone] = fork(state) const result = parser(clone) return result.isOk ? result : succeed([], original) }) +export const parse = curry((parser, input) => parser(State(input)))