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) }