sounisi5011/ts-peg

View on GitHub
src/parser/primary/literal-string.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { canonicalize, unicodeVersion } from '../../case-folding-map';
import {
    ParseFailureResult,
    Parser,
    ParseResult,
    ParserGenerator,
    ParseSuccessResult,
} from '../../internal';
import { CacheStore } from '../../utils/cache-store';

const caseInsensitiveLiteralStringParser = new CacheStore<
    [Function, ParserGenerator, string],
    CaseInsensitiveLiteralStringParser
>();

export class CaseInsensitiveLiteralStringParser extends Parser<string> {
    public readonly unicodeVersion = unicodeVersion;
    private readonly __literalString: string;

    constructor(literalString: string, parserGenerator: ParserGenerator) {
        super(parserGenerator);
        this.__literalString = canonicalize(literalString);

        const cachedParser = caseInsensitiveLiteralStringParser.upsert(
            [this.constructor, parserGenerator, this.__literalString],
            undefined,
            () => this,
        );
        if (cachedParser !== this) return cachedParser;
    }

    protected __parse(
        input: string,
        offsetStart: number,
        stopOffset: number,
    ): ParseResult<string> {
        const offsetEnd = offsetStart + this.__literalString.length;
        if (offsetEnd <= stopOffset) {
            const substr = input.substring(offsetStart, offsetEnd);
            if (this.__literalString === canonicalize(substr)) {
                return new ParseSuccessResult({
                    offsetEnd,
                    dataGenerator: () => substr,
                    allowCache: true,
                });
            }
        }
        return new ParseFailureResult({ allowCache: true });
    }
}

const literalStringParserCache = new CacheStore<
    [Function, ParserGenerator, string],
    LiteralStringParser<string>
>();

export class LiteralStringParser<T extends string> extends Parser<T> {
    private readonly __literalString: T;

    constructor(literalString: T, parserGenerator: ParserGenerator) {
        if (typeof literalString !== 'string') {
            throw new TypeError('first argument must be a string');
        }

        super(parserGenerator);
        this.__literalString = literalString;

        const cachedParser = literalStringParserCache.upsertWithTypeGuard(
            [this.constructor, parserGenerator, literalString],
            undefined,
            () => this,
            (value): value is LiteralStringParser<T> =>
                value instanceof this.constructor,
        );
        if (cachedParser && cachedParser !== this) return cachedParser;
    }

    get i(): CaseInsensitiveLiteralStringParser {
        return new CaseInsensitiveLiteralStringParser(
            this.__literalString,
            this.parserGenerator,
        );
    }

    protected __parse(
        input: string,
        offsetStart: number,
        stopOffset: number,
    ): ParseResult<T> {
        const offsetEnd = offsetStart + this.__literalString.length;
        return offsetEnd <= stopOffset &&
            input.startsWith(this.__literalString, offsetStart)
            ? new ParseSuccessResult({
                  offsetEnd,
                  dataGenerator: () => this.__literalString,
                  allowCache: true,
              })
            : new ParseFailureResult({ allowCache: true });
    }
}