"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("../models/command");
const util_1 = require("../util");
const astRule_1 = require("./astRule");
exports.RULE_GRAMMAR = `
    RuleItem
        = RuleLoop
        | RuleGroup // Do this before Rule because we need to look for a "+" on the following Rule
        | Rule

    Rule = t_DEBUGGER? (RuleModifierLeft* RuleBracket)+ "->" (RuleModifier? RuleBracket)* RuleCommand* MessageCommand? lineTerminator+

    RuleBracket
        = EllipsisRuleBracket
        | NormalRuleBracket

    NormalRuleBracket = "[" NonemptyListOf<RuleBracketNeighbor,
    "|"> t_AGAIN? "]" t_DEBUGGER? // t_AGAIN is a HACK. It should be in the list of commands but it's not.
    EllipsisRuleBracket = "[" NonemptyListOf<RuleBracketNeighbor, "|"> t_ELLIPSIS "|" NonemptyListOf<RuleBracketNeighbor, "|"> "]" t_DEBUGGER?

    RuleBracketNeighbor
        = HackTileNameIsSFX1 // to parse '... -> [ SFX1 ]' (they should be commands)
        | HackTileNameIsSFX2 // to parse '... -> [ tilename SFX1 ]'
        | RuleBracketNoEllipsisNeighbor

    RuleBracketNoEllipsisNeighbor = TileWithModifier* t_DEBUGGER?

    TileWithModifier = t_DEBUGGER? tileModifier* lookupRuleVariableName

    // Force-check that there is whitespace after the cellLayerModifier so things
    // like "STATIONARYZ" or "NOZ" are not parsed as a modifier
    // (they are a variable that happens to begin with the same text as a modifier)
    tileModifier = space* tileModifierInner space+

    tileModifierInner
        = t_NO
        | t_LEFT
        | t_RIGHT
        | t_UP
        | t_DOWN
        | t_RANDOMDIR
        | t_RANDOM
        | t_STATIONARY
        | t_MOVING
        | t_ACTION
        | t_VERTICAL
        | t_HORIZONTAL
        | t_PERPENDICULAR
        | t_PARALLEL
        | t_ORTHOGONAL
        | t_ARROW_ANY // This can be a "v" so it needs to go at the end (behind t_VERTICAL)

    RuleModifier
        = t_RANDOM
        | t_UP
        | t_DOWN
        | t_LEFT
        | t_RIGHT
        | t_VERTICAL
        | t_HORIZONTAL
        | t_ORTHOGONAL

    RuleModifierLeft
        = RuleModifier // Sometimes people write "RIGHT LATE [..." instead of "LATE RIGHT [..."
        | t_LATE
        | t_RIGID

    RuleCommand
        = t_AGAIN
        | t_CANCEL
        | t_CHECKPOINT
        | t_RESTART
        | t_WIN
        | t_SFX

    MessageCommand = t_MESSAGE words*

    RuleLoop =
        t_DEBUGGER?
        t_STARTLOOP lineTerminator+
        RuleItem+
        t_ENDLOOP lineTerminator+

    RuleGroup =
        t_DEBUGGER?
        t_RANDOM?
        Rule
        (t_GROUP_RULE_PLUS Rule)+

    HackTileNameIsSFX1 = t_SFX t_DEBUGGER?
    HackTileNameIsSFX2 = lookupRuleVariableName t_SFX t_DEBUGGER?
`;
function getRuleSemantics(lookup) {
    const cacheTilesWithModifiers = new Map();
    const cacheNeighbors = new Map();
    const cacheBrackets = new Map();
    return {
        RuleItem(_1) {
            return _1.parse();
        },
        RuleLoop(debugFlag, _startloop, _whitespace1, rules, _endloop, _whitespace2) {
            return new astRule_1.ASTRuleLoop(this.source, rules.parse(), debugFlag.parse()[0]);
        },
        RuleGroup(debugFlag, randomFlag, firstRule, _plusses, followingRules) {
            return new astRule_1.ASTRuleGroup(this.source, !!randomFlag.parse()[0], [firstRule.parse()].concat(followingRules.parse()), debugFlag.parse()[0]);
        },
        Rule(debugFlag, modifiers, conditions, _arrow, _unusuedModifer, actions, commands, optionalMessageCommand, _whitespace) {
            const modifiers2 = util_1._flatten(modifiers.parse());
            const commands2 = commands.parse().filter((c) => !!c); // remove nulls (like an invalid sound effect... e.g. "Fish Friend")
            const optionalMessageCommand2 = optionalMessageCommand.parse()[0];
            if (optionalMessageCommand2) {
                commands2.push(optionalMessageCommand2);
            }
            return new astRule_1.ASTRule(this.source, modifiers2, conditions.parse(), actions.parse(), commands2, debugFlag.parse()[0]);
        },
        NormalRuleBracket(_openBracket, neighbors, hackAgain, _closeBracket, debugFlag) {
            const b = new astRule_1.ASTRuleBracket(this.source, neighbors.parse(), hackAgain.parse(), debugFlag.parse()[0]);
            const key = b.toKey();
            if (!cacheBrackets.has(key)) {
                cacheBrackets.set(key, b);
            }
            else {
                // console.log(`Prevented creating a duplicate bracket: ${key}`)
            }
            return cacheBrackets.get(key);
        },
        EllipsisRuleBracket(_openBracket, beforeEllipsisNeighbors, _ellipsis, _pipe, afterEllipsisNeighbors, _closeBracket, debugFlag) {
            // The before ellipsis neightbor contains an additional empty neighbor that we need to remove
            const before = beforeEllipsisNeighbors.parse();
            const extraEmptyNeighbor = before.pop();
            if (extraEmptyNeighbor && extraEmptyNeighbor.tilesWithModifier.length === 0) {
                // yep, that's the extra one we should remove
            }
            else {
                throw new Error(`BUG: Invariant broken`);
            }
            const b = new astRule_1.ASTRuleBracketEllipsis(this.source, before, afterEllipsisNeighbors.parse(), debugFlag.parse()[0]);
            const key = b.toKey();
            if (!cacheBrackets.has(key)) {
                cacheBrackets.set(key, b);
            }
            else {
                // console.log(`Prevented creating a duplicate bracket: ${key}`)
            }
            return cacheBrackets.get(key);
        },
        RuleBracketNeighbor(_1) {
            return _1.parse();
        },
        RuleBracketNoEllipsisNeighbor(tileWithModifiers, debugFlag) {
            const n = new astRule_1.ASTRuleBracketNeighbor(this.source, tileWithModifiers.parse(), debugFlag.parse()[0]);
            const key = n.toKey();
            if (!cacheNeighbors.has(key)) {
                cacheNeighbors.set(key, n);
            }
            else {
                // console.log('Prevented creating a duplicate neighbor')
            }
            return cacheNeighbors.get(key);
        },
        TileWithModifier(debugFlag, optionalModifier, tile) {
            const t = new astRule_1.ASTTileWithModifier(this.source, optionalModifier.parse()[0], tile.parse(), debugFlag.parse()[0]);
            const key = t.toKey();
            if (!cacheTilesWithModifiers.has(key)) {
                cacheTilesWithModifiers.set(key, t);
            }
            else {
                // console.log('Prevented creating a duplicate tile')
            }
            return cacheTilesWithModifiers.get(key);
        },
        tileModifier(_whitespace1, tileModifier, _whitespace2) {
            return tileModifier.parse();
        },
        MessageCommand(_message, message) {
            return new command_1.MessageCommand(this.source, message.parse()[0]);
        },
        RuleCommand(type) {
            const type2 = type.parse();
            switch (type2) {
                case command_1.COMMAND_TYPE.AGAIN:
                    return new command_1.AgainCommand(this.source);
                case command_1.COMMAND_TYPE.CANCEL:
                    return new command_1.CancelCommand(this.source);
                case command_1.COMMAND_TYPE.CHECKPOINT:
                    return new command_1.CheckpointCommand(this.source);
                case command_1.COMMAND_TYPE.RESTART:
                    return new command_1.RestartCommand(this.source);
                case command_1.COMMAND_TYPE.WIN:
                    return new command_1.WinCommand(this.source);
                case 'SFX0':
                case 'SFX1':
                case 'SFX2':
                case 'SFX3':
                case 'SFX4':
                case 'SFX5':
                case 'SFX6':
                case 'SFX7':
                case 'SFX8':
                case 'SFX9':
                case 'SFX10':
                    const sound = lookup.lookupSoundEffect(type2);
                    if (!sound) {
                        // console.warn(this.source.getLineAndColumnMessage())
                        console.warn(`Sound not found "${type2}"`); // tslint:disable-line:no-console
                        return null; // this will get filtered out when we create the Rule
                    }
                    return new command_1.SoundCommand(this.source, sound);
                default:
                    throw new Error(`BUG: Fallthrough. Did not match "${type2}"`);
            }
        },
        HackTileNameIsSFX1(sfx, debugFlag) {
            return new astRule_1.ASTRuleBracketNeighborHack(this.source, { sfx: sfx.parse() }, debugFlag.parse()[0]);
        },
        HackTileNameIsSFX2(tile, sfx, debugFlag) {
            return new astRule_1.ASTRuleBracketNeighborHack(this.source, { tile: tile.parse(), sfx: sfx.parse() }, debugFlag.parse()[0]);
        }
    };
}
exports.getRuleSemantics = getRuleSemantics;
//# sourceMappingURL=data:application/json;base64, 