/* Generated by Seltzer 0.0.4. */
(function() {
    var path, packageJson, OPERATOR_MAP, RULES, DEFAULT_ROOT, CompilationContext;
    path = require("path");
    packageJson = require(path.join(__dirname, "..", "package.json"));
    OPERATOR_MAP = {
        and: "&&",
        or: "||",
        not: "!"
    };
    RULES = {
        program: function(ctx, node) {
            var body, scope, varDeclaration;
            ctx.pushScope();
            body = RULES[node.block.type](ctx, node.block);
            scope = ctx.popScope();
            varDeclaration = "";
            scope.variables.length > 0 && (varDeclaration = "var " + scope.variables.join(",") + ";");
            return "(function(){" + varDeclaration + body + "})();";
        },
        block: function(ctx, node) {
            var buf;
            buf = [];
            node.statements.forEach(function(statement) {
                buf.push(RULES[statement.type](ctx, statement));
                buf.push(";");
            });
            return "{" + buf.join("") + "}";
        },
        binary: function(ctx, node) {
            var op, buf, temp;
            op = node.op;
            hoist = !1;
            if (op === ":=") {
                node.left.type === "identifier" && ctx.scopes[ctx.scopes.length - 1].variables.push(node.left.value);
                hoist = !0;
                op = "=";
            }
            op = OPERATOR_MAP[op] || op;
            if (ctx.opts.extensions.DestructuringAssignment && op === "=" && node["left"]["type"] == "array") {
                buf = "";
                temp = null;
                if (node.right.type !== "identifier") {
                    temp = ctx.uniqueVar();
                    buf = temp + " = " + RULES[node.right.type](ctx, node.right) + ";";
                } else temp = node.right.value;
                node.left.params.forEach(function(p, i) {
                    if (p.type === "identifier") {
                        hoist && ctx.scopes[ctx.scopes.length - 1].variables.push(p.value);
                        buf += RULES[p.type](ctx, p) + " = " + temp + "[" + i + "];";
                    } else p.type === "array" && (buf += RULES.binary(ctx, {
                        type: "binary",
                        left: p,
                        op: hoist ? ":=" : "=",
                        right: {
                            type: "member",
                            left: {
                                type: "identifier",
                                value: temp
                            },
                            right: {
                                type: "literal",
                                tag: "number",
                                value: i
                            }
                        }
                    }));
                });
                return buf;
            }
            return "(" + RULES[node.left.type](ctx, node.left) + " " + op + " " + RULES[node.right.type](ctx, node.right) + ")";
        },
        unary: function(ctx, node) {
            var op;
            op = node.op;
            op = OPERATOR_MAP[op] || op;
            return "(" + op + " " + RULES[node.left.type](ctx, node.left) + ")";
        },
        identifier: function(ctx, node) {
            return node.value;
        },
        call: function(ctx, node) {
            return "(" + RULES[node.left.type](ctx, node.left) + "(" + node.params.map(function(x) {
                return "(" + RULES[x.type](ctx, x) + ")";
            }).join(",") + "))";
        },
        member: function(ctx, node) {
            var buf;
            if (ctx.opts.extensions.NegativeIndexing && (node.right.type !== "literal" || node.right.tag === "number" && node.right.value < 0)) return "__seltzer_xrt.member(" + RULES[node.left.type](ctx, node.left) + "," + RULES[node.right.type](ctx, node.right) + ")";
            if (ctx.opts.extensions.ArraySlices && node.right.type === "range") {
                buf = "__seltzer_xrt.slice(" + RULES[node.left.type](ctx, node.left) + ",";
                buf += (node.right.start === null ? "null" : RULES[node.right.start.type](ctx, node.right.start)) + ",";
                buf += (node.right.stop === null ? "null" : RULES[node.right.stop.type](ctx, node.right.stop)) + ",";
                buf += node.right.step === null ? "null" : RULES[node.right.step.type](ctx, node.right.step);
                buf += ")";
                return buf;
            }
            return "(" + RULES[node.left.type](ctx, node.left) + "[" + RULES[node.right.type](ctx, node.right) + "])";
        },
        range: function(ctx, node) {
            var buf;
            buf = "(new __seltzer_xrt.Range(";
            buf += (node["start"] == null ? "null" : RULES[node.start.type](ctx, node.start)) + ",";
            buf += (node["stop"] == null ? "null" : RULES[node.stop.type](ctx, node.stop)) + ",";
            buf += node["step"] == null ? "null" : RULES[node.step.type](ctx, node.step);
            buf += "))";
            return buf;
        },
        proto: function(ctx, node) {
            return node.value !== null ? "prototype." + RULES[node.value.type](ctx, node.value) : '"prototype"';
        },
        literal: function(ctx, node) {
            return node.value === "undefined" ? "(void 0)" : node.tag === "regex" && node["value"] == "//" ? "/(?:)/" : node.value;
        },
        object: function(ctx, node) {
            return "{" + node.props.map(function(x) {
                return RULES[x.key.type](ctx, x.key) + ":" + RULES[x.value.type](ctx, x.value);
            }).join(",") + "}";
        },
        ifStatement: function(ctx, node) {
            var buf;
            buf = "if(" + RULES[node.condition.type](ctx, node.condition) + ")" + RULES[node.truthy.type](ctx, node.truthy);
            node.falsy !== null && (buf += "else " + RULES[node.falsy.type](ctx, node.falsy));
            return buf;
        },
        simpleForStatement: function(ctx, node) {
            return "for(" + RULES[node.intializer.type](ctx, node.intializer) + ";" + RULES[node.condition.type](ctx, node.condition) + ";" + RULES[node.advance.type](ctx, node.advance) + ")" + RULES[node.block.type](ctx, node.block);
        },
        enhancedForStatement: function(ctx, node) {
            var buf, start, stop, step, temp;
            buf = "";
            if (node.collection.type === "range" && node.bind.type !== "array") {
                start = 1;
                if (node.collection.start !== null) switch (node.collection.start.type) {
                  case "identifier":
                  case "literal":
                    start = node.collection.start.value;
                    break;
                  default:
                    start = ctx.uniqueVar();
                    buf += start + "=" + RULES[node.collection.start.type](ctx, node.collection.start) + ";";
                }
                stop = null;
                if (node.collection.stop !== null) switch (node.collection.stop.type) {
                  case "identifier":
                  case "literal":
                    stop = node.collection.stop.value;
                    break;
                  default:
                    stop = ctx.uniqueVar();
                    buf += stop + "=" + RULES[node.collection.stop.type](ctx, node.collection.stop) + ";";
                }
                step = 1;
                if (node.collection.step !== null) switch (node.collection.step.type) {
                  case "identifier":
                  case "literal":
                    step = node.collection.step.value;
                    break;
                  default:
                    step = ctx.uniqueVar();
                    buf += step + "=" + RULES[node.collection.step.type](ctx, node.collection.step) + ";";
                }
                ctx.scopes[ctx.scopes.length - 1].variables.push(node.bind.value);
                buf += "for(";
                buf += node.bind.value + "=" + start + ";";
                stop !== null && (buf += node.bind.value + "<=" + stop);
                buf += ";";
                buf += node.bind.value + "+=" + step;
                buf += "){";
                buf += RULES[node.block.type](ctx, node.block);
                buf += "}";
            } else {
                temp = ctx.uniqueVar();
                buf += "for(" + temp + "=__seltzer_xrt.iter(" + RULES[node.collection.type](ctx, node.collection) + ");;)try{";
                buf += RULES.binary(ctx, {
                    type: "binary",
                    left: node.bind,
                    op: ":=",
                    right: {
                        type: "call",
                        left: {
                            type: "member",
                            left: {
                                type: "identifier",
                                value: temp
                            },
                            right: {
                                type: "literal",
                                tag: "string",
                                value: '"next"'
                            }
                        },
                        params: []
                    }
                });
                buf += ";";
                buf += RULES[node.block.type](ctx, node.block);
                buf += "}catch(e){if(e===__seltzer_xrt.StopIteration)break;throw e;}";
            }
            return buf;
        },
        "catch": function(ctx, node) {
            return "catch(" + RULES[node.bind.type](ctx, node.bind) + ")" + RULES[node.block.type](ctx, node.block);
        },
        tryStatement: function(ctx, node) {
            var buf;
            buf = "try{" + RULES[node.block.type](ctx, node.block) + "}";
            node.catcher && (buf += RULES[node.catcher.type](ctx, node.catcher));
            node.cleanup && (buf += "finally" + RULES[node.cleanup.type](ctx, node.cleanup));
            return buf;
        },
        allocation: function(ctx, node) {
            return "(new " + RULES[node.left.type](ctx, node.left) + "(" + node.params.map(function(x) {
                return "(" + RULES[x.type](ctx, x) + ")";
            }).join(",") + "))";
        },
        formalParameter: function(ctx, node) {
            return RULES[node.name.type](ctx, node.name);
        },
        defExpression: function(ctx, node) {
            var body, scope, varDeclaration, defaults;
            ctx.pushScope();
            body = RULES[node.block.type](ctx, node.block);
            scope = ctx.popScope();
            varDeclaration = "";
            scope.variables.length > 0 && (varDeclaration = "var " + scope.variables.join(",") + ";");
            defaults = "";
            ctx.opts.extensions.DefaultArguments && node.params.forEach(function(x) {
                x.value !== null && (defaults += "if(" + RULES[x.name.type](ctx, x.name) + "===(void 0))" + RULES[x.name.type](ctx, x.name) + "=" + RULES[x.value.type](ctx, x.value) + ";");
            });
            return "(function(" + node.params.map(function(x) {
                return RULES[x.type](ctx, x);
            }) + "){" + varDeclaration + defaults + body + "})";
        },
        whileStatement: function(ctx, node) {
            return "while(" + RULES[node.condition.type](ctx, node.condition) + ")" + RULES[node.block.type](ctx, node.block);
        },
        "case": function(ctx, node) {
            var buf;
            buf = "case " + RULES[node.value.type](ctx, node.value) + ":";
            node.block !== null && (buf += RULES[node.block.type](ctx, node.block));
            return buf;
        },
        "switch": function(ctx, node) {
            var buf;
            buf = "switch(" + RULES[node.expr.type](ctx, node.expr) + "){" + node.cases.map(function(x) {
                return RULES[x.type](ctx, x);
            }).join("");
            node.fallthru !== null && (buf += "default:" + RULES[node.fallthru.type](ctx, node.fallthru));
            buf += "}";
            return buf;
        },
        noop: function(ctx, node) {
            return "";
        },
        array: function(ctx, node) {
            return "[" + node.params.map(function(x) {
                return RULES[x.type](ctx, x);
            }) + "]";
        },
        conditional: function(ctx, node) {
            return "(" + RULES[node.condition.type](ctx, node.condition) + "?" + RULES[node.truthy.type](ctx, node.truthy) + ":" + RULES[node.falsy.type](ctx, node.falsy) + ")";
        },
        "return": function(ctx, node) {
            var buf;
            buf = "return";
            node.value && (buf += " " + RULES[node.value.type](ctx, node.value));
            return buf;
        },
        "throw": function(ctx, node) {
            var buf;
            buf = "throw";
            node.value && (buf += " " + RULES[node.value.type](ctx, node.value));
            return buf;
        },
        "continue": function(ctx, node) {
            return "continue";
        },
        "break": function(ctx, node) {
            return "break";
        }
    };
    DEFAULT_ROOT = RULES.program;
    CompilationContext = function(opts) {
        this.opts = opts;
        this.scopes = [];
        this.buf = [];
        this.uid = 0;
    };
    CompilationContext.prototype = {
        uniqueVar: function() {
            var name;
            name = "__seltzer_tmp_" + this.uid;
            this.scopes[this.scopes.length - 1].variables.push(name);
            this.uid += 1;
            return name;
        },
        pushScope: function() {
            this.scopes.push({
                variables: []
            });
        },
        popScope: function() {
            return this.scopes.pop();
        }
    };
    module.exports = {
        DEFAULT_ROOT: DEFAULT_ROOT,
        compile: function(ast, opts) {
            var header, root, ctx, program, jsp, pro, jsAst;
            opts === void 0 && (opts = {});
            header = "/* Generated by Seltzer " + packageJson.version + ".";
            Object.keys(opts.extensions).length > 0 && (header += " Compiled with extensions: " + Object.keys(opts.extensions).join(", "));
            header += " */\n";
            root = "root" in opts ? opts.root : DEFAULT_ROOT;
            ctx = new CompilationContext(opts);
            program = root(ctx, ast);
            if (opts.beautify) {
                jsp = require("uglify-js").parser;
                pro = require("uglify-js").uglify;
                jsAst = jsp.parse(program);
                jsAst = pro.ast_squeeze(jsAst, {
                    make_seqs: !1,
                    dead_code: !1
                });
                program = pro.gen_code(jsAst, {
                    beautify: !0
                });
            }
            return header + program + "\n";
        }
    };
})();
