import { assert } from "../../assert.js";
import { Cache } from "../../cache/Cache.js";

import { noop } from "../../function/noop.js";
import { computeStringHash } from "../../primitives/strings/computeStringHash.js";

/**
 * @template T
 */
export class AbstractCachingParser {

    /**
     * Parser cache
     * @type {Cache<string, T>}
     */
    __cache = new Cache({
        maxWeight: 1000,
        keyHashFunction: computeStringHash
    });


    /**
     *
     * @param {string} code
     * @returns {T}
     * @private
     */
    __invoke_parser(code) {
        throw new Error('Not implemented');
    }

    /**
     *
     * @param {string} code
     * @returns {T}
     */
    parse(code) {
        assert.isString(code, 'code');

        // applying trim increases chances of cache keys matching
        const trimmedCode = code.trim();

        assert.notEqual(trimmedCode, "", 'code is empty');

        return this.__cache.getOrCompute(trimmedCode, this.__invoke_parser, this);
    }

    /**
     *
     * @param {string} code
     * @param {function(e:Error)} [errorConsumer]
     */
    validate(code, errorConsumer = noop) {
        assert.isString(code, 'code');
        assert.isFunction(errorConsumer, 'errorConsumer');

        try {
            this.parse(code);
        } catch (e) {
            errorConsumer(e);

            return false;
        }

        return true;
    }
}
