"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Join = void 0;
const NonTerminal_js_1 = require("../core/NonTerminal.js");
const Production_js_1 = require("../core/Production.js");
const Terminal_js_1 = require("../core/Terminal.js");
const Evaluator_js_1 = require("../Evaluator.js");
const IntRange_js_1 = require("../util/IntRange.js");
const Repeat_js_1 = require("./Repeat.js");
const Rule_js_1 = require("./Rule.js");
const Star_js_1 = require("./Star.js");
class Join extends Rule_js_1.Rule {
    constructor(tgt, entry, open, close, delimiter, cardinality) {
        super("join", tgt, entry);
        this.onlyKeepEntries = true;
        this.open = open;
        this.close = close;
        this.delimiter = delimiter;
        this.cardinality = cardinality;
        this.setEvaluator(Evaluator_js_1.Evaluator.ALL_CHILDREN_EVALUATOR);
    }
    getEntry() {
        return this.children[0];
    }
    getCardinality() {
        return this.cardinality;
    }
    setCardinality(cardinality) {
        this.cardinality = cardinality;
    }
    setOnlyKeepEntries(onlyKeepEntries) {
        this.onlyKeepEntries = onlyKeepEntries;
    }
    createBNF(grammar) {
        const first = this.children[0];
        const next = new NonTerminal_js_1.NonTerminal("next-" + NonTerminal_js_1.NonTerminal.makeRandomSymbol());
        const hasOpen = this.open !== undefined && !this.open.isEpsilon();
        const hasClose = this.close !== undefined && !this.close.isEpsilon();
        const hasDelimiter = this.delimiter !== undefined && !this.delimiter.isEpsilon();
        if ((hasOpen && !hasClose) || (!hasOpen && hasClose))
            throw new Error("Join must have either both open and close or neither");
        if (hasDelimiter) {
            const p = Rule_js_1.Rule.addProduction(grammar, this, next, this.delimiter, first);
            const that = this;
            p.onExtension((parent, ...children) => {
                const nthEntry = parent.getNthEntryInParent() + 1;
                children[0].setName("delimiter");
                children[1].setName(that.getNameForChild(nthEntry));
            });
            if (this.onlyKeepEntries)
                p.setAstBuilder((parent, ...children) => parent.addChildren(children[1]));
            else
                p.setAstBuilder(Production_js_1.AstBuilder.DEFAULT);
        }
        else {
            const p = Rule_js_1.Rule.addProduction(grammar, this, next, first);
            const that = this;
            p.onExtension((parent, ...children) => {
                const nthEntry = parent.getNthEntryInParent() + 1;
                children[0].setName(that.getNameForChild(nthEntry));
            });
            p.setAstBuilder((parent, ...children) => parent.addChildren(children[0]));
        }
        // Assume L -> first next
        const astBuilder = (parent, ...children) => {
            parent.addChildren(children[0]);
            for (let pn of children[1].getChildren())
                parent.addChildren(...pn.getChildren());
        };
        const repetition = new NonTerminal_js_1.NonTerminal("repetition:" + NonTerminal_js_1.NonTerminal.makeRandomSymbol());
        // + : L -> first next*
        if (this.cardinality.equals(IntRange_js_1.IntRange.PLUS)) {
            const star = new Star_js_1.Star(undefined, next);
            star.setParsedChildNames("next");
            star.createBNF(grammar);
            const p = Rule_js_1.Rule.addProduction(grammar, this, repetition, first, star.getTarget());
            const that = this;
            p.onExtension((_parent, ...children) => {
                children[0].setName(that.getNameForChild(0));
                children[1].setName("star");
            });
            p.setAstBuilder(astBuilder);
        }
        // L -> first L
        // L -> next L
        // L -> e
        // * : L -> first next*
        //     L -> epsilon
        else if (this.cardinality.equals(IntRange_js_1.IntRange.STAR)) {
            const star = new Star_js_1.Star(undefined, next);
            star.setParsedChildNames("next");
            star.createBNF(grammar);
            const p1 = Rule_js_1.Rule.addProduction(grammar, this, repetition, first, star.getTarget());
            const p2 = Rule_js_1.Rule.addProduction(grammar, this, repetition, Terminal_js_1.Terminal.EPSILON);
            p1.setAstBuilder(astBuilder);
            p2.setAstBuilder((_parent, ..._children) => { });
            const that = this;
            p1.onExtension((_parent, ...children) => {
                children[0].setName(that.getNameForChild(0));
                children[1].setName("star");
            });
        }
        // ? : L -> first
        //     L -> epsilon
        else if (this.cardinality.equals(IntRange_js_1.IntRange.OPTIONAL)) {
            const p1 = Rule_js_1.Rule.addProduction(grammar, this, repetition, first); // using default ASTBuilder
            const that = this;
            p1.onExtension((_parent, ...children) => {
                children[0].setName(that.getNameForChild(0));
            });
            const p2 = Rule_js_1.Rule.addProduction(grammar, this, repetition, Terminal_js_1.Terminal.EPSILON);
            p2.setAstBuilder((_parent, ..._children) => { });
        }
        // Dealing with a specific range
        else {
            const lower = this.cardinality.getLower();
            const upper = this.cardinality.getUpper();
            if (lower === 0 && upper === 0) {
                Rule_js_1.Rule.addProduction(grammar, this, repetition, Terminal_js_1.Terminal.EPSILON).setAstBuilder((_parent, ..._children) => { });
            }
            else if (lower === 1 && upper === 1) {
                const p = Rule_js_1.Rule.addProduction(grammar, this, repetition, first); // using default ASTBuilder
                const that = this;
                p.onExtension((_parent, ...children) => {
                    children[0].setName(that.getNameForChild(0));
                });
            }
            else {
                if (lower <= 0) {
                    const repeat = new Repeat_js_1.Repeat(undefined, next, 0, upper - 1);
                    repeat.setParsedChildNames("next");
                    repeat.createBNF(grammar);
                    const p = Rule_js_1.Rule.addProduction(grammar, this, repetition, first, repeat.getTarget());
                    const that = this;
                    p.setAstBuilder(astBuilder);
                    p.onExtension((_parent, ...children) => {
                        children[0].setName(that.getNameForChild(0));
                        children[1].setName("repeat");
                    });
                    Rule_js_1.Rule.addProduction(grammar, this, repetition, Terminal_js_1.Terminal.EPSILON).setAstBuilder((_parent, ..._children) => { });
                }
                else {
                    const repeat = new Repeat_js_1.Repeat(undefined, next, lower - 1, upper - 1);
                    repeat.setParsedChildNames("next");
                    repeat.createBNF(grammar);
                    const p = Rule_js_1.Rule.addProduction(grammar, this, repetition, first, repeat.getTarget());
                    const that = this;
                    p.setAstBuilder(astBuilder);
                    p.onExtension((_parent, ...children) => {
                        children[0].setName(that.getNameForChild(0));
                        children[1].setName("repeat");
                    });
                }
            }
        }
        if (!hasOpen && !hasClose) {
            const p = Rule_js_1.Rule.addProduction(grammar, this, this.tgt, repetition);
            p.onExtension((_parent, ...children) => {
                children[0].setName("repetition");
            });
            p.setAstBuilder((parent, ...children) => parent.addChildren(...children[0].getChildren()));
        }
        else {
            const p = Rule_js_1.Rule.addProduction(grammar, this, this.tgt, this.open, repetition, this.close);
            const that = this;
            p.onExtension((_parent, ...children) => {
                if (!that.onlyKeepEntries)
                    children[0].setName("open");
                children[1].setName("repetition");
                if (!that.onlyKeepEntries)
                    children[2].setName("close");
            });
            p.setAstBuilder((parent, ...children) => {
                if (!that.onlyKeepEntries)
                    parent.addChildren(children[0]);
                parent.addChildren(...children[1].getChildren());
                if (!that.onlyKeepEntries)
                    parent.addChildren(children[2]);
            });
        }
    }
}
exports.Join = Join;
//# sourceMappingURL=Join.js.map