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)
|
||||
))
|
||||
|
||||
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 lowerAlpha = anyOf(LowerAlpha)
|
||||
export const upperAlpha = anyOf(UpperAlpha)
|
||||
|
|
44
src/cond.js
44
src/cond.js
|
@ -1,6 +1,7 @@
|
|||
import { ParseError } from './state.js'
|
||||
import { curry } from '../vendor/izuna/src/index.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 { ParserState } from './state.js' */
|
||||
|
@ -16,3 +17,44 @@ export const maybe = curry(
|
|||
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 { Iter } from './iter.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'* /
|
||||
|
||||
|
|
|
@ -4,3 +4,7 @@ export * from './cond.js'
|
|||
export * from './seq.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 { curry } from '../vendor/izuna/src/curry.js'
|
||||
import { anyChar } from './char.js'
|
||||
|
||||
/** @import { ParseError, ParserState } from './state.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
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -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 { curry } from '../vendor/izuna/src/curry.js'
|
||||
/**
|
||||
* @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 parse = curry((parser, input) => parser(State(input)))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue