"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 utils_1 = require("../../utils/utils");
var tokens_public_1 = require("../../scan/tokens_public");
var gast;
(function (gast) {
    var AbstractProduction = (function () {
        function AbstractProduction(definition) {
            this.definition = definition;
            this.implicitOccurrenceIndex = false;
        }
        AbstractProduction.prototype.accept = function (visitor) {
            visitor.visit(this);
            utils_1.forEach(this.definition, function (prod) {
                prod.accept(visitor);
            });
        };
        return AbstractProduction;
    }());
    gast.AbstractProduction = AbstractProduction;
    var NonTerminal = (function (_super) {
        __extends(NonTerminal, _super);
        function NonTerminal(nonTerminalName, referencedRule, occurrenceInParent) {
            if (referencedRule === void 0) { referencedRule = undefined; }
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, []);
            this.nonTerminalName = nonTerminalName;
            this.referencedRule = referencedRule;
            this.occurrenceInParent = occurrenceInParent;
        }
        Object.defineProperty(NonTerminal.prototype, "definition", {
            get: function () {
                if (this.referencedRule !== undefined) {
                    return this.referencedRule.definition;
                }
                return [];
            },
            set: function (definition) {
                // immutable
            },
            enumerable: true,
            configurable: true
        });
        NonTerminal.prototype.accept = function (visitor) {
            visitor.visit(this);
            // don't visit children of a reference, we will get cyclic infinite loops if we do so
        };
        return NonTerminal;
    }(AbstractProduction));
    gast.NonTerminal = NonTerminal;
    var Rule = (function (_super) {
        __extends(Rule, _super);
        function Rule(name, definition, orgText) {
            if (orgText === void 0) { orgText = ""; }
            _super.call(this, definition);
            this.name = name;
            this.orgText = orgText;
        }
        return Rule;
    }(AbstractProduction));
    gast.Rule = Rule;
    var Flat = (function (_super) {
        __extends(Flat, _super);
        function Flat(definition) {
            _super.call(this, definition);
        }
        return Flat;
    }(AbstractProduction));
    gast.Flat = Flat;
    var Option = (function (_super) {
        __extends(Option, _super);
        function Option(definition, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, definition);
            this.occurrenceInParent = occurrenceInParent;
        }
        return Option;
    }(AbstractProduction));
    gast.Option = Option;
    var RepetitionMandatory = (function (_super) {
        __extends(RepetitionMandatory, _super);
        function RepetitionMandatory(definition, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, definition);
            this.occurrenceInParent = occurrenceInParent;
        }
        return RepetitionMandatory;
    }(AbstractProduction));
    gast.RepetitionMandatory = RepetitionMandatory;
    var RepetitionMandatoryWithSeparator = (function (_super) {
        __extends(RepetitionMandatoryWithSeparator, _super);
        function RepetitionMandatoryWithSeparator(definition, separator, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, definition);
            this.separator = separator;
            this.occurrenceInParent = occurrenceInParent;
        }
        return RepetitionMandatoryWithSeparator;
    }(AbstractProduction));
    gast.RepetitionMandatoryWithSeparator = RepetitionMandatoryWithSeparator;
    var Repetition = (function (_super) {
        __extends(Repetition, _super);
        function Repetition(definition, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, definition);
            this.occurrenceInParent = occurrenceInParent;
        }
        return Repetition;
    }(AbstractProduction));
    gast.Repetition = Repetition;
    var RepetitionWithSeparator = (function (_super) {
        __extends(RepetitionWithSeparator, _super);
        function RepetitionWithSeparator(definition, separator, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, definition);
            this.separator = separator;
            this.occurrenceInParent = occurrenceInParent;
        }
        return RepetitionWithSeparator;
    }(AbstractProduction));
    gast.RepetitionWithSeparator = RepetitionWithSeparator;
    var Alternation = (function (_super) {
        __extends(Alternation, _super);
        function Alternation(definition, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            _super.call(this, definition);
            this.occurrenceInParent = occurrenceInParent;
        }
        return Alternation;
    }(AbstractProduction));
    gast.Alternation = Alternation;
    var Terminal = (function () {
        function Terminal(terminalType, occurrenceInParent) {
            if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
            this.terminalType = terminalType;
            this.occurrenceInParent = occurrenceInParent;
            this.implicitOccurrenceIndex = false;
        }
        Terminal.prototype.accept = function (visitor) {
            visitor.visit(this);
        };
        return Terminal;
    }());
    gast.Terminal = Terminal;
    var GAstVisitor = (function () {
        function GAstVisitor() {
        }
        GAstVisitor.prototype.visit = function (node) {
            if (node instanceof NonTerminal) {
                return this.visitNonTerminal(node);
            }
            else if (node instanceof Flat) {
                return this.visitFlat(node);
            }
            else if (node instanceof Option) {
                return this.visitOption(node);
            }
            else if (node instanceof RepetitionMandatory) {
                return this.visitRepetitionMandatory(node);
            }
            else if (node instanceof RepetitionMandatoryWithSeparator) {
                return this.visitRepetitionMandatoryWithSeparator(node);
            }
            else if (node instanceof RepetitionWithSeparator) {
                return this.visitRepetitionWithSeparator(node);
            }
            else if (node instanceof Repetition) {
                return this.visitRepetition(node);
            }
            else if (node instanceof Alternation) {
                return this.visitAlternation(node);
            }
            else if (node instanceof Terminal) {
                return this.visitTerminal(node);
            }/* istanbul ignore else */ 
            else if (node instanceof Rule) {
                return this.visitRule(node);
            }
            else {
                /* istanbul ignore next */ throw Error("non exhaustive match");
            }
        };
        GAstVisitor.prototype.visitNonTerminal = function (node) { };
        GAstVisitor.prototype.visitFlat = function (node) { };
        GAstVisitor.prototype.visitOption = function (node) { };
        GAstVisitor.prototype.visitRepetition = function (node) { };
        GAstVisitor.prototype.visitRepetitionMandatory = function (node) { };
        GAstVisitor.prototype.visitRepetitionMandatoryWithSeparator = function (node) { };
        GAstVisitor.prototype.visitRepetitionWithSeparator = function (node) { };
        GAstVisitor.prototype.visitAlternation = function (node) { };
        GAstVisitor.prototype.visitTerminal = function (node) { };
        GAstVisitor.prototype.visitRule = function (node) { };
        return GAstVisitor;
    }());
    gast.GAstVisitor = GAstVisitor;
    function serializeGrammar(topRules) {
        return utils_1.map(topRules, serializeProduction);
    }
    gast.serializeGrammar = serializeGrammar;
    function serializeProduction(node) {
        function convertDefinition(definition) {
            return utils_1.map(definition, serializeProduction);
        }
        if (node instanceof NonTerminal) {
            return {
                type: "NonTerminal",
                name: node.nonTerminalName,
                occurrenceInParent: node.occurrenceInParent
            };
        }
        else if (node instanceof Flat) {
            return {
                type: "Flat",
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof Option) {
            return {
                type: "Option",
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof RepetitionMandatory) {
            return {
                type: "RepetitionMandatory",
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof RepetitionMandatoryWithSeparator) {
            return {
                type: "RepetitionMandatoryWithSeparator",
                separator: serializeProduction(new Terminal(node.separator)),
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof RepetitionWithSeparator) {
            return {
                type: "RepetitionWithSeparator",
                separator: serializeProduction(new Terminal(node.separator)),
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof Repetition) {
            return {
                type: "Repetition",
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof Alternation) {
            return {
                type: "Alternation",
                definition: convertDefinition(node.definition)
            };
        }
        else if (node instanceof Terminal) {
            var serializedTerminal = {
                type: "Terminal",
                name: tokens_public_1.tokenName(node.terminalType),
                label: tokens_public_1.tokenLabel(node.terminalType),
                occurrenceInParent: node.occurrenceInParent
            };
            if (node.terminalType.PATTERN) {
                serializedTerminal.pattern = node.terminalType.PATTERN.source;
            }
            return serializedTerminal;
        }/* istanbul ignore else */ 
        else if (node instanceof Rule) {
            return { type: "Rule", name: node.name, definition: convertDefinition(node.definition) };
        }
        else {
            /* istanbul ignore next */ throw Error("non exhaustive match");
        }
    }
    gast.serializeProduction = serializeProduction;
})(gast = exports.gast || (exports.gast = {}));
