'use strict';

var lang_extensions = require('../../lang/lang_extensions.js');
var forEach = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/forEach.js');
var isEmpty = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/isEmpty.js');
var map = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/map.js');
var isArray = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/isArray.js');
var isUndefined = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/isUndefined.js');
var keys = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/keys.js');
var filter = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/filter.js');
var isFunction = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/isFunction.js');
var compact = require('../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/compact.js');

function defaultVisit(ctx, param) {
    const childrenNames = keys.default(ctx);
    const childrenNamesLength = childrenNames.length;
    for (let i = 0; i < childrenNamesLength; i++) {
        const currChildName = childrenNames[i];
        const currChildArray = ctx[currChildName];
        const currChildArrayLength = currChildArray.length;
        for (let j = 0; j < currChildArrayLength; j++) {
            const currChild = currChildArray[j];
            // distinction between Tokens Children and CstNode children
            if (currChild.tokenTypeIdx === undefined) {
                this[currChild.name](currChild.children, param);
            }
        }
    }
    // defaultVisit does not support generic out param
}
function createBaseSemanticVisitorConstructor(grammarName, ruleNames) {
    const derivedConstructor = function () { };
    // can be overwritten according to:
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/
    // name?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fname
    lang_extensions.defineNameProp(derivedConstructor, grammarName + "BaseSemantics");
    const semanticProto = {
        visit: function (cstNode, param) {
            // enables writing more concise visitor methods when CstNode has only a single child
            if (isArray.default(cstNode)) {
                // A CST Node's children dictionary can never have empty arrays as values
                // If a key is defined there will be at least one element in the corresponding value array.
                cstNode = cstNode[0];
            }
            // enables passing optional CstNodes concisely.
            if (isUndefined.default(cstNode)) {
                return undefined;
            }
            return this[cstNode.name](cstNode.children, param);
        },
        validateVisitor: function () {
            const semanticDefinitionErrors = validateVisitor(this, ruleNames);
            if (!isEmpty.default(semanticDefinitionErrors)) {
                const errorMessages = map.default(semanticDefinitionErrors, (currDefError) => currDefError.msg);
                throw Error(`Errors Detected in CST Visitor <${this.constructor.name}>:\n\t` +
                    `${errorMessages.join("\n\n").replace(/\n/g, "\n\t")}`);
            }
        },
    };
    derivedConstructor.prototype = semanticProto;
    derivedConstructor.prototype.constructor = derivedConstructor;
    derivedConstructor._RULE_NAMES = ruleNames;
    return derivedConstructor;
}
function createBaseVisitorConstructorWithDefaults(grammarName, ruleNames, baseConstructor) {
    const derivedConstructor = function () { };
    // can be overwritten according to:
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/
    // name?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fname
    lang_extensions.defineNameProp(derivedConstructor, grammarName + "BaseSemanticsWithDefaults");
    const withDefaultsProto = Object.create(baseConstructor.prototype);
    forEach.default(ruleNames, (ruleName) => {
        withDefaultsProto[ruleName] = defaultVisit;
    });
    derivedConstructor.prototype = withDefaultsProto;
    derivedConstructor.prototype.constructor = derivedConstructor;
    return derivedConstructor;
}
exports.CstVisitorDefinitionError = void 0;
(function (CstVisitorDefinitionError) {
    CstVisitorDefinitionError[CstVisitorDefinitionError["REDUNDANT_METHOD"] = 0] = "REDUNDANT_METHOD";
    CstVisitorDefinitionError[CstVisitorDefinitionError["MISSING_METHOD"] = 1] = "MISSING_METHOD";
})(exports.CstVisitorDefinitionError || (exports.CstVisitorDefinitionError = {}));
function validateVisitor(visitorInstance, ruleNames) {
    const missingErrors = validateMissingCstMethods(visitorInstance, ruleNames);
    return missingErrors;
}
function validateMissingCstMethods(visitorInstance, ruleNames) {
    const missingRuleNames = filter.default(ruleNames, (currRuleName) => {
        return isFunction.default(visitorInstance[currRuleName]) === false;
    });
    const errors = map.default(missingRuleNames, (currRuleName) => {
        return {
            msg: `Missing visitor method: <${currRuleName}> on ${(visitorInstance.constructor.name)} CST Visitor.`,
            type: exports.CstVisitorDefinitionError.MISSING_METHOD,
            methodName: currRuleName,
        };
    });
    return compact.default(errors);
}

exports.createBaseSemanticVisitorConstructor = createBaseSemanticVisitorConstructor;
exports.createBaseVisitorConstructorWithDefaults = createBaseVisitorConstructorWithDefaults;
exports.defaultVisit = defaultVisit;
exports.validateMissingCstMethods = validateMissingCstMethods;
exports.validateVisitor = validateVisitor;
