add more combinators
This commit is contained in:
parent
8323c9f6a1
commit
5152336bdc
6 changed files with 98 additions and 3 deletions
|
@ -28,6 +28,11 @@ export const str = curry(
|
||||||
)(state)
|
)(state)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
export const anyChar = state => {
|
||||||
|
const ch = next(state)
|
||||||
|
return !!ch ? succeed(ch, state) : fail('end of input', 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)
|
||||||
|
|
44
src/cond.js
44
src/cond.js
|
@ -1,6 +1,7 @@
|
||||||
import { ParseError } from './state.js'
|
import { ParseError } from './state.js'
|
||||||
import { curry } from '../vendor/izuna/src/index.js'
|
|
||||||
import { fork, succeed } from './fn.js'
|
import { fork, succeed } from './fn.js'
|
||||||
|
import { curry } from '../vendor/izuna/src/index.js'
|
||||||
|
import { anyChar } from './char.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' */
|
||||||
|
@ -16,3 +17,44 @@ export const maybe = curry(
|
||||||
return result.isOk() ? result : succeed([], original)
|
return result.isOk() ? result : succeed([], original)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const not = parser => state => {
|
||||||
|
const result = parser(state)
|
||||||
|
if (result.isOk()) {
|
||||||
|
return fail('"not" parser failed', state)
|
||||||
|
} else {
|
||||||
|
return succeed([], state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const until = parser => state => {
|
||||||
|
let acc = ok(state)
|
||||||
|
|
||||||
|
while (result.isOk()) {
|
||||||
|
const [original, clone] = fork(state)
|
||||||
|
const result = acc.chain(x => parser(clone))
|
||||||
|
if (result.isOk()) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
acc = anyChar(original)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
export const many = curry((parser, state) => {
|
||||||
|
let result = ok(state)
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const [original, clone] = fork(result)
|
||||||
|
const res = result.chain(x => parser(clone))
|
||||||
|
if (res.isOk()) {
|
||||||
|
result = res
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ParseError } from './state.js'
|
import { ParseError } from './state.js'
|
||||||
import { Iter } from './iter.js'
|
import { Iter } from './iter.js'
|
||||||
import { err, ok } from '../vendor/kojima/src/index.js'
|
import { err, ok } from '../vendor/kojima/src/index.js'
|
||||||
import { curry } from '/vendor/izuna/src/index.js'
|
import { curry } from '../vendor/izuna/src/index.js'
|
||||||
|
|
||||||
/** @import { ParserState } from './state.js'* /
|
/** @import { ParserState } from './state.js'* /
|
||||||
|
|
||||||
|
|
|
@ -4,3 +4,7 @@ export * from './cond.js'
|
||||||
export * from './seq.js'
|
export * from './seq.js'
|
||||||
export * from './state.js'
|
export * from './state.js'
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
parse(until(not(char('!'))), '!!!!!!!a!!!!')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
43
src/seq.js
43
src/seq.js
|
@ -1,6 +1,7 @@
|
||||||
import { diff, fail, succeed, Tuple } from './fn.js'
|
import { diff, fail, fork, 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 { ParseError, ParserState } from './state.js' */
|
/** @import { ParseError, ParserState } from './state.js' */
|
||||||
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
||||||
|
@ -9,6 +10,29 @@ import { curry } from '../vendor/izuna/src/curry.js'
|
||||||
* @typedef {(value: any) => Result<ParserState, ParseError>} Parser
|
* @typedef {(value: any) => Result<ParserState, ParseError>} Parser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export const take = n => state => {
|
||||||
|
let result = anyChar(state)
|
||||||
|
for (let i = n; i > 0; i--) {
|
||||||
|
if (result.isErr()) {
|
||||||
|
return result.chain(e => fail(`"take(${n})" failed`, state, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.chain(anyChar)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export const skip = parser => state => {
|
||||||
|
const tokens = state[0]
|
||||||
|
const result = parser(state)
|
||||||
|
if (result.isOk()) {
|
||||||
|
return result.map(other => [tokens, other[1]])
|
||||||
|
} else {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {...Parser} parsers
|
* @param {...Parser} parsers
|
||||||
*/
|
*/
|
||||||
|
@ -53,3 +77,20 @@ export const map = curry(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export const many = curry((parser, state) => {
|
||||||
|
let result = ok(state)
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const [a, b] = fork(state)
|
||||||
|
const res = parser(clone(result.unwrap()))
|
||||||
|
if (res.isOk()) {
|
||||||
|
result = res
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Iter } from './iter.js'
|
import { Iter } from './iter.js'
|
||||||
|
import { curry } from '../vendor/izuna/src/curry.js'
|
||||||
/**
|
/**
|
||||||
* @typedef {Readonly<[any[], Iterator<any>]>} ParserState
|
* @typedef {Readonly<[any[], Iterator<any>]>} ParserState
|
||||||
*/
|
*/
|
||||||
|
@ -21,3 +22,5 @@ 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)))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue