UNPKG

fruitsconfits

Version:

FruitsConfits - A well typed and sugared parser combinator framework for TypeScript/JavaScript.

232 lines (202 loc) 8.53 kB
// Copyright (c) 2019 Shellyl_N and Authors // license: ISC // https://github.com/shellyln import { ParserFnWithCtx } from './types'; import { zeroWidth, zeroWidthError, beginning, end, quantify, first, or, transform, lookAhead, lookBehind, ApplyProductionRulesArg, applyProductionRules, makeProgram } from './parser'; export function objSequence<T extends ArrayLike<T[number]>, C, R>( helper: (token: T[number]) => R, comparator: (a: T[number], b: T[number]) => boolean, ): (needle: T) => ParserFnWithCtx<T, C, R> { return (needle => { return (input => { const len = Math.max(0, input.end - input.start); let matched = true; if (len >= needle.length) { for (let i = 0; i < needle.length; i++) { if (! comparator(input.src[input.start + i], needle[i])) { matched = false; break; } } } else { matched = false; } return (matched ? { succeeded: true, next: { src: input.src, start: input.start + needle.length, end: input.end, context: input.context, templateArgs: input.templateArgs, templateArgsPos: input.templateArgsPos, }, tokens: [helper(needle)], } : { succeeded: false, error: false, src: input.src, pos: input.start, // eslint-disable-next-line @typescript-eslint/restrict-template-expressions message: `operator "objSequence(${needle})"`, // TODO: BUG: `needle` is not stringify correctly. }); }); }); } export function objClass<T extends ArrayLike<T[number]>, C, R>( helper: (token: T[number]) => R, comparator: (a: T[number], b: T[number]) => boolean, ): (...needles: Array<T[number]>) => ParserFnWithCtx<T, C, R> { // NOTE: <T> version `needles` type is `T`. return ((...needles) => { return (input => { const len = Math.max(0, input.end - input.start); let index = -1; const succeeded = len > 0 ? needles.some((needle, idx) => { if (comparator(input.src[input.start], needle)) { index = idx; return true; } }) : false; return (succeeded ? { succeeded: true, next: { src: input.src, start: input.start + 1, end: input.end, context: input.context, templateArgs: input.templateArgs, templateArgsPos: input.templateArgsPos, }, tokens: [helper(needles[index])], } : { succeeded: false, error: false, src: input.src, pos: input.start, message: `operator "objClass(${needles.join(',')})"`, }); }); }); } export function objClassNot<T extends ArrayLike<T[number]>, C, R>( helper: (token: T[number]) => R, comparator: (a: T[number], b: T[number]) => boolean, ): (...needles: Array<T[number]>) => ParserFnWithCtx<T, C, R> { // NOTE: <T> version `needles` type is `T`. return ((...needles) => { return (input => { const len = Math.max(0, input.end - input.start); if (len > 0) { for (const needle of needles) { let matched = true; if (! comparator(input.src[input.start], needle)) { matched = false; break; } if (matched) { return ({ succeeded: false, error: false, src: input.src, pos: input.start, message: `operator "objClassNot(${needles.join(',')})"`, }); } } } return ({ succeeded: true, next: { src: input.src, start: input.start + 1, end: input.end, context: input.context, templateArgs: input.templateArgs, templateArgsPos: input.templateArgsPos, }, tokens: [helper(input.src[input.start])], }); }); }); } export function objClassByNeedleFn<T extends ArrayLike<T[number]>, C, R>( helper: (token: T[number]) => R, comparator: (a: T[number], b: T[number]) => boolean, ): (needle: (src: T[number]) => boolean) => ParserFnWithCtx<T, C, R> { // NOTE: needles[i] should be one character. surrogate pair and/or ligature are accepted. // NOTE: <T> version `needles` type is `T`. return (needle => { return (input => { const len = Math.max(0, input.end - input.start); const matched = len > 0 ? needle(input.src[input.start]) : false; return (matched ? { succeeded: true, next: { src: input.src, start: input.start + 1, end: input.end, context: input.context, templateArgs: input.templateArgs, templateArgsPos: input.templateArgsPos, }, tokens: [helper(input.src[input.start])], } : { succeeded: false, error: false, src: input.src, pos: input.start, message: `operator "objClassByNeedleFn"`, }); }); }); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function getObjectParsers<T extends ArrayLike<T[number]>, C, R>( params: { rawToToken: (rawToken: T[number]) => R, concatTokens: (tokens: R[]) => R[], comparator: (a: T[number], b: T[number]) => boolean, }) { const clsFn = objClassByNeedleFn<T, C, R>(params.rawToToken, params.comparator); const isAny = clsFn(src => true); // TODO: reduce unneccessary call for adding types. return ({ seq: objSequence<T, C, R>(params.rawToToken, params.comparator), cls: objClass<T, C, R>(params.rawToToken, params.comparator), notCls: objClassNot<T, C, R>(params.rawToToken, params.comparator), clsFn, classes: { any: isAny, }, cat: transform<T, C, R>(params.concatTokens), once: quantify<T, C, R>(1, 1), repeat: quantify<T, C, R>(), qty: (min?: number, max?: number) => quantify<T, C, R>(min, max), // TODO: zeroWidth: (helper?: () => R) => zeroWidth<T, C, R>(helper), // TODO: err: (message: string) => zeroWidthError<T, C, R>(message), // TODO: beginning: (helper?: () => R) => beginning<T, C, R>(helper), // TODO: end: (helper?: () => R) => end<T, C, R>(helper), // TODO: first: (...parsers: Array<ParserFnWithCtx<T, C, R>>) => first<T, C, R>(...parsers), // TODO: or: (...parsers: Array<ParserFnWithCtx<T, C, R>>) => or<T, C, R>(...parsers), // TODO: combine: transform<T, C, R>(), erase: transform<T, C, R>(tokens => []), trans: (fn: (tokens: R[]) => R[]) => transform<T, C, R>(fn), // TODO: ahead: (...parsers: Array<ParserFnWithCtx<T, C, R>>) => lookAhead<T, C, R>(...parsers), // TODO: behind: (n: number, helper?: () => R) => lookBehind<T, C, R>(n, helper), rules: (args: ApplyProductionRulesArg<T, C, R>) => applyProductionRules<T, C, R>(args), // TODO: makeProgram, }); }