83 lines
1.9 KiB
JavaScript
83 lines
1.9 KiB
JavaScript
import { chain, curry } from 'izuna'
|
|
import { any, map, seq } from './combinator.js'
|
|
import { not } from './cond.js'
|
|
import { Digits, LowerAlpha, UpperAlpha } from './const.js'
|
|
import { clone, fail, join, mapStr, next, succeed } from './fn.js'
|
|
import { ok } from 'kojima'
|
|
|
|
/** @import { ParserState } from './state.js' */
|
|
|
|
export const char = curry(
|
|
/**
|
|
* @param {string} ch
|
|
* @param {ParserState} state
|
|
*/
|
|
(ch, state) => {
|
|
return next(state) === ch ? succeed(ch, state) : fail(`could not parse ${ch} `, state)
|
|
})
|
|
|
|
export const tag = curry(
|
|
/**
|
|
* @param {string} str
|
|
* @param {ParserState} state
|
|
*/
|
|
(str, state) => (
|
|
map(
|
|
join(''),
|
|
seq(...mapStr(char, str))
|
|
)(state)
|
|
))
|
|
|
|
export const charNoCase = curry((ch, state) => {
|
|
return any(
|
|
char(ch.toLowerCase()),
|
|
char(ch.toUpperCase())
|
|
)(state)
|
|
})
|
|
|
|
export const tagNoCase = curry(
|
|
(str, state) => (
|
|
map(
|
|
join(''),
|
|
seq(...mapStr(charNoCase, str))
|
|
)(state)
|
|
))
|
|
|
|
export const anyChar = state => {
|
|
const ch = next(state)
|
|
return !!ch ? succeed(ch, state) : fail('end of input', state)
|
|
}
|
|
|
|
export const take = curry(
|
|
(limit, state) => {
|
|
let res = ok(state)
|
|
|
|
while (res.isOk() && limit > 0) {
|
|
res = chain(anyChar, res)
|
|
limit -= 1
|
|
}
|
|
return res
|
|
}
|
|
)
|
|
|
|
export const oneOf = curry(
|
|
/**
|
|
* @param {string} str
|
|
* @param {ParserState} state
|
|
*/
|
|
(str, state) => (
|
|
any(...mapStr(char, str))(state)
|
|
))
|
|
|
|
export const noneOf = curry((str, state) => seq(not(any(oneOf(str), eof)), anyChar)(state))
|
|
|
|
export const digit = oneOf(Digits)
|
|
export const lowerAlpha = oneOf(LowerAlpha)
|
|
export const upperAlpha = oneOf(UpperAlpha)
|
|
export const alpha = any(lowerAlpha, upperAlpha)
|
|
export const alphanumeric = any(alpha, digit)
|
|
|
|
export const eof = state => {
|
|
return clone(state).next().done ? succeed([], state) : fail('not end of stream', state)
|
|
}
|
|
|