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 { 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 { Alpha, Alphanumeric, Digits, LowerAlpha, UpperAlpha } from './const.js'
|
||||||
import { fail, join, mapStr, next, succeed } from './fn.js'
|
import { fail, join, mapStr, next, succeed } from './fn.js'
|
||||||
import { seq } from './seq.js'
|
import { seq } from './seq.js'
|
||||||
|
@ -36,6 +36,6 @@ export const anyChar = state => {
|
||||||
export const digit = anyOf(Digits)
|
export const digit = anyOf(Digits)
|
||||||
export const lowerAlpha = anyOf(LowerAlpha)
|
export const lowerAlpha = anyOf(LowerAlpha)
|
||||||
export const upperAlpha = anyOf(UpperAlpha)
|
export const upperAlpha = anyOf(UpperAlpha)
|
||||||
export const alpha = anyOf(Alpha)
|
export const alpha = any(lowerAlpha, upperAlpha)
|
||||||
export const alphanumeric = anyOf(Alphanumeric)
|
export const alphanumeric = any(alpha, digit)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { char } from './char.js'
|
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 { curry } from '../vendor/izuna/src/curry.js'
|
||||||
|
|
||||||
/** @import { ParserState } from './state.js' */
|
/** @import { ParserState } from './state.js' */
|
||||||
|
@ -13,9 +13,8 @@ export const any = (...parsers) =>
|
||||||
*/
|
*/
|
||||||
state => {
|
state => {
|
||||||
for (const parser of parsers) {
|
for (const parser of parsers) {
|
||||||
const [original, clone] = fork(state)
|
const result = parser(clone(state))
|
||||||
const result = parser(clone)
|
if (result.isOk()) {
|
||||||
if (result.isOk) {
|
|
||||||
return result
|
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 { 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 { anyChar } from './char.js'
|
||||||
import { ok } from '../vendor/kojima/src/index.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 { Result } from '../vendor/kojima/src/index.js' */
|
||||||
/** @import { ParserState } from './state.js' */
|
/** @import { ParserState } from './state.js' */
|
||||||
|
@ -13,19 +13,17 @@ export const maybe = curry(
|
||||||
* @param {ParserState} state
|
* @param {ParserState} state
|
||||||
*/
|
*/
|
||||||
(parser, state) => {
|
(parser, state) => {
|
||||||
const [original, clone] = fork(state)
|
const result = parser(clone(state))
|
||||||
const result = parser(clone)
|
return result.isOk() ? result : succeed([], state)
|
||||||
return result.isOk() ? result : succeed([], original)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const not = curry((parser, state) => {
|
export const not = curry((parser, state) => {
|
||||||
const [original, clone] = fork(state)
|
const result = parser(clone(state))
|
||||||
const result = parser(clone)
|
|
||||||
|
|
||||||
if (result.isOk()) {
|
if (result.isOk()) {
|
||||||
return fail('"not" parser failed', original)
|
return fail(`'not' parser failed for ${parser.name}`, state)
|
||||||
} else {
|
} else {
|
||||||
return succeed([], original)
|
return succeed([], state)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -33,12 +31,12 @@ export const until = curry((parser, state) => {
|
||||||
let result = ok(state)
|
let result = ok(state)
|
||||||
|
|
||||||
while (result.isOk()) {
|
while (result.isOk()) {
|
||||||
const [original, clone] = result.chain(fork)
|
console.log(parser.name, state)
|
||||||
result = result.chain(x => parser(clone))
|
result = result.chain(pipe(clone, parser))
|
||||||
if (result.isOk()) {
|
if (result.isOk()) {
|
||||||
break
|
break
|
||||||
} else {
|
} 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
|
* @template T
|
||||||
* @param {T | T[]} v
|
* @param {T | T[]} v
|
||||||
|
|
15
src/iter.js
15
src/iter.js
|
@ -5,12 +5,15 @@
|
||||||
*/
|
*/
|
||||||
export class Iter {
|
export class Iter {
|
||||||
_iterator
|
_iterator
|
||||||
|
_source
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Iterator<T>} iterator
|
* @param {Iterator<T>} iterator
|
||||||
|
* @param {Iterabl<T>} [source]
|
||||||
*/
|
*/
|
||||||
constructor(iterator) {
|
constructor(iterator, source) {
|
||||||
this._iterator = iterator
|
this._iterator = iterator
|
||||||
|
this._source = source
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,12 +41,20 @@ export class Iter {
|
||||||
return iterator
|
return iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Iter(iterator)
|
return new Iter(iterator, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeError('object is not an iterator')
|
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]
|
* @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 { ok } from '../vendor/kojima/src/index.js'
|
||||||
import { curry } from '../vendor/izuna/src/curry.js'
|
import { curry } from '../vendor/izuna/src/curry.js'
|
||||||
import { anyChar } from './char.js'
|
import { anyChar } from './char.js'
|
||||||
|
@ -56,8 +56,7 @@ export const many = curry((parser, state) => {
|
||||||
let result = ok(state)
|
let result = ok(state)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const [original, clone] = result.chain(fork)
|
const res = parser(clone(state))
|
||||||
const res = parser(clone)
|
|
||||||
if (res.isOk()) {
|
if (res.isOk()) {
|
||||||
result = res
|
result = res
|
||||||
} else {
|
} else {
|
||||||
|
@ -66,5 +65,7 @@ export const many = curry((parser, state) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
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 { 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
|
* @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 State = value => Object.freeze([[], Iter.from(value)])
|
||||||
|
|
||||||
export const parse = curry((parser, input) => parser(State(input)))
|
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