"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) /* istanbul ignore next */  if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    /* istanbul ignore next */  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var gast_public_1 = require("./gast_public");
var utils_1 = require("../../utils/utils");
var lang_extensions_1 = require("../../lang/lang_extensions");
function isSequenceProd(prod) {
    return prod instanceof gast_public_1.gast.Flat ||
        prod instanceof gast_public_1.gast.Option ||
        prod instanceof gast_public_1.gast.Repetition ||
        prod instanceof gast_public_1.gast.RepetitionMandatory ||
        prod instanceof gast_public_1.gast.RepetitionMandatoryWithSeparator ||
        prod instanceof gast_public_1.gast.RepetitionWithSeparator ||
        prod instanceof gast_public_1.gast.Terminal ||
        prod instanceof gast_public_1.gast.Rule;
}
exports.isSequenceProd = isSequenceProd;
function isOptionalProd(prod, alreadyVisited) {
    if (alreadyVisited === void 0) { alreadyVisited = []; }
    var isDirectlyOptional = prod instanceof gast_public_1.gast.Option ||
        prod instanceof gast_public_1.gast.Repetition ||
        prod instanceof gast_public_1.gast.RepetitionWithSeparator;
    if (isDirectlyOptional) {
        return true;
    }
    // note that this can cause infinite loop if one optional empty TOP production has a cyclic dependency with another
    // empty optional top rule
    // may be indirectly optional ((A?B?C?) | (D?E?F?))
    if (prod instanceof gast_public_1.gast.Alternation) {
        // for OR its enough for just one of the alternatives to be optional
        return utils_1.some(prod.definition, function (subProd) {
            return isOptionalProd(subProd, alreadyVisited);
        });
    }
    else if (prod instanceof gast_public_1.gast.NonTerminal && utils_1.contains(alreadyVisited, prod)) {
        // avoiding stack overflow due to infinite recursion
        return false;
    }
    else if (prod instanceof gast_public_1.gast.AbstractProduction) {
        if (prod instanceof gast_public_1.gast.NonTerminal) {
            alreadyVisited.push(prod);
        }
        return utils_1.every(prod.definition, function (subProd) {
            return isOptionalProd(subProd, alreadyVisited);
        });
    }
    else {
        return false;
    }
}
exports.isOptionalProd = isOptionalProd;
function isBranchingProd(prod) {
    return prod instanceof gast_public_1.gast.Alternation;
}
exports.isBranchingProd = isBranchingProd;
var productionToDslName = {};
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.NonTerminal)] = "SUBRULE";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.Option)] = "OPTION";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.RepetitionMandatory)] = "AT_LEAST_ONE";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.RepetitionMandatoryWithSeparator)] = "AT_LEAST_ONE_SEP";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.RepetitionWithSeparator)] = "MANY_SEP";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.Repetition)] = "MANY";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.Alternation)] = "OR";
productionToDslName[lang_extensions_1.functionName(gast_public_1.gast.Terminal)] = "CONSUME";
function getProductionDslName(prod) {
    var clazz = prod.constructor;
    var prodName = lang_extensions_1.functionName(clazz);
    return productionToDslName[prodName];
}
exports.getProductionDslName = getProductionDslName;
var GastCloneVisitor = (function (_super) {
    __extends(GastCloneVisitor, _super);
    function GastCloneVisitor() {
        _super.apply(this, arguments);
    }
    GastCloneVisitor.prototype.visitNonTerminal = function (node) {
        return new gast_public_1.gast.NonTerminal(node.nonTerminalName, undefined, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitFlat = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.Flat(definition);
    };
    GastCloneVisitor.prototype.visitOption = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.Option(definition, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitRepetition = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.Repetition(definition, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitRepetitionMandatory = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.RepetitionMandatory(definition, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitRepetitionMandatoryWithSeparator = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.RepetitionMandatoryWithSeparator(definition, node.separator, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitRepetitionWithSeparator = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.RepetitionWithSeparator(definition, node.separator, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitAlternation = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.Alternation(definition, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitTerminal = function (node) {
        return new gast_public_1.gast.Terminal(node.terminalType, node.occurrenceInParent);
    };
    GastCloneVisitor.prototype.visitRule = function (node) {
        var _this = this;
        var definition = utils_1.map(node.definition, function (currSubDef) { return _this.visit(currSubDef); });
        return new gast_public_1.gast.Rule(node.name, definition, node.orgText);
    };
    return GastCloneVisitor;
}(gast_public_1.gast.GAstVisitor));
function cloneProduction(prod) {
    var cloningVisitor = new GastCloneVisitor();
    return cloningVisitor.visit(prod);
}
exports.cloneProduction = cloneProduction;
