var _ns_ = "wisp.analyzer";
module.namespace = _ns_;
var symbol = (require("./ast")).symbol;
var isSymbol = (require("./ast")).isSymbol;
var isKeyword = (require("./ast")).isKeyword;
var meta = (require("./ast")).meta;
var name = (require("./ast")).name;
var namespace = (require("./ast")).namespace;
var isSeq = (require("./sequence")).isSeq;
var seq = (require("./sequence")).seq;
var conj = (require("./sequence")).conj;
var map = (require("./sequence")).map;
var isEvery = (require("./sequence")).isEvery;
var interleave = (require("./sequence")).interleave;
var isEmpty = (require("./sequence")).isEmpty;
var list_ = (require("./sequence")).list_;
var list = (require("./sequence")).list;
var first = (require("./sequence")).first;
var last = (require("./sequence")).last;
var rest = (require("./sequence")).rest;
var count = (require("./sequence")).count;
var isVector = (require("./runtime")).isVector;
var isDictionary = (require("./runtime")).isDictionary;
var isString = (require("./runtime")).isString;
var keys = (require("./runtime")).keys;
var vals = (require("./runtime")).vals;
var isEqual = (require("./runtime")).isEqual;
var isNil = (require("./runtime")).isNil;
var merge = (require("./runtime")).merge;
var split = (require("./string")).split;;

var getIn = function getIn(dictionary, keys, notFound) {
  return (function loop(target, sentinel, keys) {
    var recur = loop;
    while (recur === loop) {
      recur = isEmpty(keys) ?
      target :
      (function() {
        var result = (((target || 0) || 0)[first(keys)]) || sentinel;
        return result === sentinel ?
          notFound :
          (target = result, sentinel = sentinel, keys = rest(keys), loop);
      })();
    };
    return recur;
  })(dictionary, {}, keys);
};
exports.getIn = getIn;

var emptyEnv = function emptyEnv(ns) {
  "Utility function that creates empty namespaces";
  return {
    "ns": ns,
    "namespaces": {},
    "context": "statement",
    "locals": {}
  };
};
exports.emptyEnv = emptyEnv;

var localBinding = function localBinding(env, form) {
  return (((env || 0)["locals"]) || 0)[form];
};

var isCoreName = function isCoreName(env, sym) {
  return false;
};
exports.isCoreName = isCoreName;

var resolveNsAlias = function resolveNsAlias(env, name) {
  var sym = symbol(name);
  return (((((((env || 0)["ns"]) || 0)["requires"]) || 0) || 0)[sym]) || sym;
};
exports.resolveNsAlias = resolveNsAlias;

var resolveExistingVar = function resolveExistingVar(env, form) {
  return isEqual(namespace(form), "js") ?
    {
      "name": form,
      "ns": symbol(void(0), "js")
    } :
    (function() {
      var namespaces = (env || 0)["namespaces"];
      var s = "" + form;
      var binding = localBinding(env, form);
      return binding ?
        binding :
      namespace(form) ?
        (function() {
          var ns = namespace(form);
          var ns = isEqual("clojure.core", ns) ?
            "cljs.core" :
            ns;
          var fullNs = resolveNsAlias(env, ns);
          var id = symbol(name(form));
          return merge(getIn(namespaces, [fullNs, "defs", id]), {
            "name": symbol("" + fullNs, "" + (name(form))),
            "ns": fullNs
          });
        })() :
      "else" ?
        (function() {
          var fullNs = isCoreName(env, form) ?
            symbol(void(0), "cljs.core") :
            (((env || 0)["ns"]) || 0)["name"];
          return merge(getIn(namespaces, [fullNs, "defs", form]), {
            "name": symbol("" + fullNs, "" + form),
            "ns": fullNs
          });
        })() :
        void(0);
    })();
};
exports.resolveExistingVar = resolveExistingVar;

var isSpecial = function isSpecial(op) {
  return (isEqual(op, symbol(void(0), "if"))) || (isEqual(op, symbol(void(0), "def"))) || (isEqual(op, symbol(void(0), "fn*"))) || (isEqual(op, symbol(void(0), "do"))) || (isEqual(op, symbol(void(0), "let*"))) || (isEqual(op, symbol(void(0), "loop*"))) || (isEqual(op, symbol(void(0), "letfn*"))) || (isEqual(op, symbol(void(0), "throw"))) || (isEqual(op, symbol(void(0), "try*"))) || (isEqual(op, symbol(void(0), "recur"))) || (isEqual(op, symbol(void(0), "new"))) || (isEqual(op, symbol(void(0), "set!"))) || (isEqual(op, symbol(void(0), "ns"))) || (isEqual(op, symbol(void(0), "deftype*"))) || (isEqual(op, symbol(void(0), "defrecord*"))) || (isEqual(op, symbol(void(0), "."))) || (isEqual(op, symbol(void(0), "js*"))) || (isEqual(op, symbol(void(0), "&"))) || (isEqual(op, symbol(void(0), "quote")));
};
exports.isSpecial = isSpecial;

var analyzeSeq = function analyzeSeq(env, form, name) {
  var env = conj(env, {
    "line": (((meta(form)) || 0)["line"]) || ((env || 0)["line"])
  });
  return (function() {
    var op = first(form);
    (function() {
      (!(typeof(__verbose__) === "undefined")) && __verbose__ ?
        console.log("Assert:", "(not (nil? op))") :
        void(0);
      return !(!(isNil(op))) ?
        (function() { throw new Error("" + "Assert failed: " + "Can't call nil" + "\n\nAssertion:\n\n" + "(not (nil? op))" + "\n\nActual:\n\n" + (isNil(op)) + "\n--------------\n", void(0)); })() :
        void(0);
    })();
    return (function() {
      var expansion = macroexpand(form);
      return isSpecial(op) ?
        parse(op, env, form, name) :
        parseInvoke(env, form);
    })();
  })();
};
exports.analyzeSeq = analyzeSeq;

var isMethodCall = function isMethodCall(form) {
  return isEqual(first(form), ".");
};

var isInstantiation = function isInstantiation(form) {
  return isEqual(last(form), ".");
};

var getNsExclude = function getNsExclude(env, sym) {
  return (((((env || 0)["ns"]) || 0)["excludes"]) || 0)[sym];
};

var getNsName = function getNsName(env) {
  return (((env || 0)["ns"]) || 0)["name"];
};

var getMacroUses = function getMacroUses(env, sym) {
  return (((((env || 0)["ns"]) || 0)["uses-macros"]) || 0)[sym];
};

var isMacroSym = function isMacroSym(env, sym) {
  var namespaces = (env || 0)["namespaces"];
  var local = localBinding(env, sym);
  var nsId = getNsName(env);
  return !(local || (((getNsExclude(env, sym)) || (getIn(namespaces, [nsId, "excludes", sym]))) && (!((getMacroUses(env, sym)) || (getIn(namespaces, [nsId, "uses-macros", sym]))))));
};
exports.isMacroSym = isMacroSym;

var getExpander = function getExpander(sym, env) {
  var op = (isMacroSym(env, sym)) && (resolveExistingVar(emptyEnv(), sym));
  return op && ((op || 0)["macro"]) ?
    eval("" + (munge((op || 0)["name"]))) :
    void(0);
};
exports.getExpander = getExpander;

var isSugar = function isSugar(op) {
  var id = "" + op;
  return (first(id) === ".") || (last(id) === ".");
};
exports.isSugar = isSugar;

var isMacro = function isMacro(op) {
  return false;
};
exports.isMacro = isMacro;

var desugar1 = function desugar1(form) {
  var id = "" + form;
  var params = rest(form);
  var metadata = meta(form);
  return isMethodCall(id) ?
    withMeta(list_(symbol(void(0), "."), first(param), symbol(subs(id, 1)), rest(params)), metadata) :
  isInstantiation(id) ?
    withMeta(list_(symbol(void(0), "new"), symbol(subs(opname, 0, dec(count(opname)))), params), metadata) :
  "else" ?
    form :
    void(0);
};
exports.desugar1 = desugar1;

var macroexpand1 = function macroexpand1(form) {
  var op = first(form);
  return isSpecial(op) ?
    form :
  isSugar(op) ?
    desugar1(form) :
  isMacro(op) ?
    getExpander(op).apply(getExpander(op), form) :
  "else" ?
    form :
    void(0);
};
exports.macroexpand1 = macroexpand1;

var macroexpand = function macroexpand(form) {
  return (function loop(form, expansion) {
    var recur = loop;
    while (recur === loop) {
      recur = form === expansion ?
      form :
      (form = expansion, expansion = macroexpand1(expansion), loop);
    };
    return recur;
  })(form, macroexpand1(form));
};
exports.macroexpand = macroexpand;

var analyzeSymbol = function analyzeSymbol(env, symbol) {
  var result = {
    "env": env,
    "form": symbol
  };
  var locals = (env || 0)["locals"];
  var local = (locals || 0)[symbol];
  return conj(result, {
    "op": "var",
    "info": local ?
      local :
      resolveExistingVar(env, symbol)
  });
};
exports.analyzeSymbol = analyzeSymbol;

var _readerNsName_ = symbol("clojure.reader", "reader");

var analyzeKeyword = function analyzeKeyword(env, form) {
  return {
    "op": "constant",
    "env": env,
    "form": isEqual(namespace(form), name(_readerNsName_)) ?
      keyword(name((((env || 0)["ns"]) || 0)["name"]), name(form)) :
      form
  };
};
exports.analyzeKeyword = analyzeKeyword;

var isSimpleKey = function isSimpleKey(x) {
  return (isString(x)) || (isKeyword(x));
};
exports.isSimpleKey = isSimpleKey;

var analyzeDictionary = function analyzeDictionary(env, form, name) {
  var exprEnv = conj(env, {
    "context": "expr"
  });
  var names = keys(form);
  var isSimpleKeys = isEvery(isSimpleKey, names);
  var ks = disallowingRecur(vec(map(function($1) {
    return analyze(exprEnv, $1, name);
  }, names)));
  var vs = disallowingRecur(vec(map(function($1) {
    return analyze(exprEnv, $1, name);
  }, vals(form))));
  return analyzeWrapMeta({
    "op": "map",
    "env": env,
    "form": form,
    "keys": ks,
    "vals": vs,
    "simple-keys?": isSimpleKeys,
    "children": vec(interleave(ks, vs))
  }, name);
};
exports.analyzeDictionary = analyzeDictionary;

var analyzeVector = function analyzeVector(env, form, name) {
  var exprEnv = conj(env, {
    "context": "expr"
  });
  var items = disallowingRecur(vec(map(function($1) {
    return analyze(exprEnv, $1, name);
  }, form)));
  return analyzeWrapMeta({
    "op": "vector",
    "env": env,
    "form": form,
    "items": items,
    "children": items
  }, name);
};
exports.analyzeVector = analyzeVector;

var analyzeWrapMeta = function analyzeWrapMeta(expr, name) {
  var form = (expr || 0)["form"];
  var metadata = meta(form);
  var env = (expr || 0)["env"];
  var expr = metadata ?
    assocIn(expr, ["env", "context"], "expr") :
    void(0);
  var metaExpr = metadata ?
    analyzeMap(env, metadata, name) :
    void(0);
  return metadata ?
    {
      "op": "meta",
      "env": env,
      "form": form,
      "meta": metaExpr,
      "expr": expr,
      "children": [metaExpr, expr]
    } :
    expr;
};
exports.analyzeWrapMeta = analyzeWrapMeta;

var analyzeMap = function analyzeMap(env, form, name) {
  var exprEnv = conj(env, {
    "context": "expr"
  });
  var isSimpleKeys = isEvery(function($1) {
    return (isString($1)) || (isKeyword($1));
  }, keys(form));
  var ks = disallowingRecur(vec(map(function($1) {
    return analyze(exprEnv, $1, name);
  }, keys(form))));
  var vs = disallowingRecur(vec(map(function($1) {
    return analyze(exprEnv, $1, name);
  }, vals(form))));
  return analyzeWrapMeta({
    "op": "map",
    "env": env,
    "form": form,
    "keys": ks,
    "vals": vs,
    "simple-keys?": isSimpleKeys,
    "children": vec(interleave(ks, vs))
  }, name);
};
exports.analyzeMap = analyzeMap;

var analyze = function analyze(env, form, name) {
  switch (arguments.length) {
    case 2:
      return analyze(env, form, void(0));
    case 3:
      return isSymbol(form) ?
        analyzeSymbol(env, form) :
      isKeyword(form) ?
        analyzeKeyword(env, form) :
      (isSeq(form)) && (!(isEmpty(form))) ?
        analyzeSeq(env, form, name) :
      isDictionary(form) ?
        analyzeDictionary(env, form, name) :
      isVector(form) ?
        analyzeVector(env, form, name) :
      "else" ?
        {
          "op": "constant",
          "env": env,
          "form": form
        } :
        void(0);

    default:
      (function() { throw Error("Invalid arity"); })()
  };
  return void(0);
};
exports.analyze = analyze
