improving combinators
This commit is contained in:
parent
170e4b2ae6
commit
ffbb58b95e
7 changed files with 54 additions and 26 deletions
|
@ -1,5 +1,5 @@
|
|||
import { State } from './state.js'
|
||||
import { anyOf, map } from './combinator.js'
|
||||
import { any, anyOf, map } from './combinator.js'
|
||||
import { Alpha, Alphanumeric, Digits, LowerAlpha, UpperAlpha } from './const.js'
|
||||
import { fail, join, mapStr, next, succeed } from './fn.js'
|
||||
import { seq } from './seq.js'
|
||||
|
@ -36,6 +36,6 @@ export const anyChar = state => {
|
|||
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)
|
||||
export const alpha = any(lowerAlpha, upperAlpha)
|
||||
export const alphanumeric = any(alpha, digit)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { char } from './char.js'
|
||||
import { diff, fail, fork, mapStr, succeed, Tuple } from './fn.js'
|
||||
import { clone, diff, fail, mapStr, succeed, Tuple } from './fn.js'
|
||||
import { curry } from '../vendor/izuna/src/curry.js'
|
||||
|
||||
/** @import { ParserState } from './state.js' */
|
||||
|
@ -13,9 +13,8 @@ export const any = (...parsers) =>
|
|||
*/
|
||||
state => {
|
||||
for (const parser of parsers) {
|
||||
const [original, clone] = fork(state)
|
||||
const result = parser(clone)
|
||||
if (result.isOk) {
|
||||
const result = parser(clone(state))
|
||||
if (result.isOk()) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
@ -53,3 +52,7 @@ export const map = curry(
|
|||
})
|
||||
})
|
||||
|
||||
export const eof = state => {
|
||||
return clone(state).next().done ? succeed([], state) : fail('not end of stream', state)
|
||||
}
|
||||
|
||||
|
|
22
src/cond.js
22
src/cond.js
|
@ -1,8 +1,8 @@
|
|||
import { ParseError } from './state.js'
|
||||
import { fail, fork, succeed } from './fn.js'
|
||||
import { clone, fail, fork, succeed } from './fn.js'
|
||||
import { anyChar } from './char.js'
|
||||
import { ok } from '../vendor/kojima/src/index.js'
|
||||
import { curry } from '../vendor/izuna/src/index.js'
|
||||
import { curry, pipe } from '../vendor/izuna/src/index.js'
|
||||
|
||||
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
||||
/** @import { ParserState } from './state.js' */
|
||||
|
@ -13,19 +13,17 @@ export const maybe = curry(
|
|||
* @param {ParserState} state
|
||||
*/
|
||||
(parser, state) => {
|
||||
const [original, clone] = fork(state)
|
||||
const result = parser(clone)
|
||||
return result.isOk() ? result : succeed([], original)
|
||||
const result = parser(clone(state))
|
||||
return result.isOk() ? result : succeed([], state)
|
||||
})
|
||||
|
||||
export const not = curry((parser, state) => {
|
||||
const [original, clone] = fork(state)
|
||||
const result = parser(clone)
|
||||
const result = parser(clone(state))
|
||||
|
||||
if (result.isOk()) {
|
||||
return fail('"not" parser failed', original)
|
||||
return fail(`'not' parser failed for ${parser.name}`, state)
|
||||
} else {
|
||||
return succeed([], original)
|
||||
return succeed([], state)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -33,12 +31,12 @@ export const until = curry((parser, state) => {
|
|||
let result = ok(state)
|
||||
|
||||
while (result.isOk()) {
|
||||
const [original, clone] = result.chain(fork)
|
||||
result = result.chain(x => parser(clone))
|
||||
console.log(parser.name, state)
|
||||
result = result.chain(pipe(clone, parser))
|
||||
if (result.isOk()) {
|
||||
break
|
||||
} else {
|
||||
result = anyChar(original)
|
||||
result = anyChar(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ export const fork = ([tokens, state]) => {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
export const clone = ([h, iter]) => [h, iter.clone()]
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T | T[]} v
|
||||
|
|
15
src/iter.js
15
src/iter.js
|
@ -5,12 +5,15 @@
|
|||
*/
|
||||
export class Iter {
|
||||
_iterator
|
||||
_source
|
||||
|
||||
/**
|
||||
* @param {Iterator<T>} iterator
|
||||
* @param {Iterabl<T>} [source]
|
||||
*/
|
||||
constructor(iterator) {
|
||||
constructor(iterator, source) {
|
||||
this._iterator = iterator
|
||||
this._source = source
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,12 +41,20 @@ export class Iter {
|
|||
return iterator
|
||||
}
|
||||
|
||||
return new Iter(iterator)
|
||||
return new Iter(iterator, value)
|
||||
}
|
||||
|
||||
throw new TypeError('object is not an iterator')
|
||||
}
|
||||
|
||||
clone() {
|
||||
if (this._source) {
|
||||
return Iter.from(this._source)
|
||||
}
|
||||
|
||||
throw new Error('Cannot clone Iterator: not created from an iterable')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} [value]
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { diff, fail, fork, succeed, Tuple } from './fn.js'
|
||||
import { clone, diff, fail, succeed, Tuple } from './fn.js'
|
||||
import { ok } from '../vendor/kojima/src/index.js'
|
||||
import { curry } from '../vendor/izuna/src/curry.js'
|
||||
import { anyChar } from './char.js'
|
||||
|
@ -56,8 +56,7 @@ export const many = curry((parser, state) => {
|
|||
let result = ok(state)
|
||||
|
||||
while (true) {
|
||||
const [original, clone] = result.chain(fork)
|
||||
const res = parser(clone)
|
||||
const res = parser(clone(state))
|
||||
if (res.isOk()) {
|
||||
result = res
|
||||
} else {
|
||||
|
@ -66,5 +65,7 @@ export const many = curry((parser, state) => {
|
|||
}
|
||||
|
||||
return result
|
||||
|
||||
})
|
||||
|
||||
export const many1 = parser => seq(parser, many(parser))
|
||||
|
||||
|
|
12
src/state.js
12
src/state.js
|
@ -1,5 +1,8 @@
|
|||
import { Iter } from './iter.js'
|
||||
import { curry } from '../vendor/izuna/src/curry.js'
|
||||
import { curry, pipe } from '../vendor/izuna/src/index.js'
|
||||
import { seq } from './seq.js'
|
||||
import { until } from './cond.js'
|
||||
import { eof } from './combinator.js'
|
||||
/**
|
||||
* @typedef {Readonly<[any[], Iterator<any>]>} ParserState
|
||||
*/
|
||||
|
@ -23,4 +26,11 @@ export class ParseError extends Error {
|
|||
export const State = value => Object.freeze([[], Iter.from(value)])
|
||||
|
||||
export const parse = curry((parser, input) => parser(State(input)))
|
||||
export const parseAll = curry((parser, input) => pipe(
|
||||
State,
|
||||
seq(
|
||||
parser,
|
||||
until(eof)
|
||||
)
|
||||
)(input))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue