soy
Version:
experimental lisp which supports both s-expressions and smalltalk esque syntax
1,268 lines (1,167 loc) • 38.9 kB
JavaScript
(function() {
var CompileEnv, Env, InPort, None, Procedure, StringIO, Symbol, SyntaxError, add_globals, all, arity, atom, compile, cons, current_dir, demand, desugar, dict_keys, dict_to_string, dict_values, eof_object, expand, expand_quasiquote, fs, gensymid, getVar, global_env, isQuote, is_key_value_pair, is_pair, isa, k, load, macro_table, parse, quotes, read, specialForms, string_encode, string_escape, sym, symbol_table, to_string, type, unbox, unzip, v, zip, _and, _append, _begin, _colon, _comma, _commaat, _compile, _cons, _define, _defmacro, _dict, _enum_at, _eval, _hash, _hat, _if, _key, _key_value_pair, _lambda, _list, _load, _percent, _period, _pipe, _quasiquote, _quote, _ref, _ref2, _semicolon, _set, _tilda, _unquote, _unquotesplicing,
__indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = Array.prototype.slice;
fs = require('fs');
None = void 0;
current_dir = './';
Symbol = (function() {
function Symbol(str) {
this.str = str;
}
return Symbol;
})();
symbol_table = {};
sym = function(s) {
if (s instanceof Symbol) return s;
if (!symbol_table[s]) symbol_table[s] = new Symbol(s);
return symbol_table[s];
};
specialForms = "load enum-at compile key dict cons append list quote if set! define lambda key-value-pair begin defmacro | ; : . , ~ & % ^ # ,@".split(' ');
_ref = specialForms.map(sym), _load = _ref[0], _enum_at = _ref[1], _compile = _ref[2], _key = _ref[3], _dict = _ref[4], _cons = _ref[5], _append = _ref[6], _list = _ref[7], _quote = _ref[8], _if = _ref[9], _set = _ref[10], _define = _ref[11], _lambda = _ref[12], _key_value_pair = _ref[13], _begin = _ref[14], _defmacro = _ref[15], _pipe = _ref[16], _semicolon = _ref[17], _colon = _ref[18], _period = _ref[19], _comma = _ref[20], _tilda = _ref[21], _and = _ref[22], _percent = _ref[23], _hat = _ref[24], _hash = _ref[25], _commaat = _ref[26];
_ref2 = "quasiquote unquote unquote-splicing".split(' ').map(sym), _quasiquote = _ref2[0], _unquote = _ref2[1], _unquotesplicing = _ref2[2];
quotes = {
"'": _quote,
"`": _quasiquote,
",": _unquote,
",@": _unquotesplicing
};
isQuote = function(token) {};
eof_object = new Symbol('#<eof-object>');
InPort = (function() {
InPort.prototype.tokenizer = /\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;|[^\s('"`,;)]*)(.*)/;
function InPort(file) {
this.file = file;
this.line = '';
}
InPort.prototype.next_token = function() {
var oldLine, parts, specialChar, token, _, _i, _len, _ref3, _ref4;
while (true) {
if (this.line === '') this.line = this.file.readline();
if (this.line === '' || this.line === void 0) return eof_object;
oldLine = this.line;
_ref3 = this.line.match(this.tokenizer), _ = _ref3[0], token = _ref3[1], this.line = _ref3[2];
if (oldLine === this.line) return eof_object;
if (token[0] === '"') return token;
if (("" + (parseFloat(token))) === token) return token;
if (__indexOf.call(token, "{") >= 0 && token !== "{") {
if (token[0] === "{") {
this.line = token.slice(1) + this.line;
token = "{";
} else {
parts = token.split("{");
token = parts.shift();
this.line = "{" + parts.join(" { ") + " " + this.line;
}
}
if (__indexOf.call(token, "}") >= 0 && token !== "}") {
if (token[-1] === "}") {
token = token.slice(0, -1);
this.line += " } ";
} else {
parts = token.split("}");
token = parts.shift();
this.line = " } " + parts.join(" } ") + " " + this.line;
}
}
if (__indexOf.call(token, "[") >= 0 && token !== "[") {
if (token[0] === "[") {
this.line = token.slice(1) + this.line;
token = "[";
} else {
parts = token.split("[");
token = parts.shift();
this.line = "[" + parts.join(" [ ") + " " + this.line;
}
}
if (__indexOf.call(token, "]") >= 0 && token !== "]") {
if (token[-1] === "]") {
token = token.slice(0, -1);
this.line += " ] ";
} else {
parts = token.split("]");
token = parts.shift();
this.line = " ] " + parts.join(" ] ") + " " + this.line;
}
}
if (token === "[") {
token = "(";
this.line = "squarelambda " + this.line;
}
if (token === "]") token = ")";
if (token === "{") {
token = "(";
this.line = "dict " + this.line;
}
if (token === "}") token = ")";
_ref4 = ['|', ';', ':', '.', ',@', ',', '~', '&', '%', '^'];
for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
specialChar = _ref4[_i];
if (String(Number(token)) === token) break;
if (token === specialChar) break;
if (__indexOf.call(token, specialChar) >= 0 && token !== specialChar) {
parts = token.split(specialChar);
token = parts.shift();
this.line = specialChar + " " + parts.join(" " + specialChar + " ") + " " + this.line;
break;
}
}
if (token !== '') return token;
}
};
return InPort;
})();
StringIO = (function() {
function StringIO(string) {
this.index = 0;
this.lines = string.trim().split("\n");
}
StringIO.prototype.readline = function() {
var line;
line = this.lines[this.index++];
if (line.trim().length === 0 && this.index < this.lines.length) {
return this.readline();
} else {
return line;
}
};
return StringIO;
})();
Env = (function() {
function Env(parms, args, outer) {
if (parms == null) parms = [];
if (args == null) args = [];
if (outer == null) outer = false;
this.values = {};
this.outer = outer;
if (isa(parms, "Symbol")) {
this.values[to_string(parms)] = args;
} else {
if (args.length !== parms.length) {
throw "Expected " + (to_string(parms)) + ", given " + (to_string(args));
}
if (parms.length > 0) this.values = zip(parms, args);
}
}
Env.prototype.toString = function() {
var key, val;
return to_string((function() {
var _ref3, _results;
_ref3 = this.values;
_results = [];
for (key in _ref3) {
val = _ref3[key];
_results.push(push([key, "" + (to_string(val))]));
}
return _results;
}).call(this));
};
Env.prototype.update = function(values) {
this.values = values;
return this;
};
Env.prototype.find = function(key, couldBeNew) {
if (couldBeNew == null) couldBeNew = false;
if (isa(key, "Symbol")) key = to_string(key);
if (this.values[key] != null) return this;
if (!this.outer && !couldBeNew) throw "Could not find " + key;
if (this.outer) {
try {
return this.outer.find(key);
} catch (e) {
if (couldBeNew) {
return this;
} else {
throw e;
}
}
}
return this;
};
Env.prototype.at = function(key) {
if (isa(key, "Symbol")) key = to_string(key);
if (this.values[key] != null) return this.values[key];
throw "Could not find " + key + " in " + (to_string(dict_keys(this.values)));
};
Env.prototype.setAt = function(key, val, couldBeNew) {
if (couldBeNew == null) couldBeNew = false;
if (isa(key, "Symbol")) key = to_string(key);
return this.find(key, couldBeNew).values[key] = val;
};
return Env;
})();
Procedure = (function() {
function Procedure(parms, exp, env) {
this.parms = parms;
this.exp = exp;
this.env = env;
}
Procedure.prototype.toString = function() {
return to_string([_lambda, this.parms, this.exp]);
};
Procedure.prototype.applyProc = function(args) {
return _eval(this.exp, new Env(this.parms, args, this.env));
};
Procedure.prototype.apply = function(ctx, args) {
return this.applyProc(args);
};
Procedure.prototype.arity = function() {
return this.parms.length;
};
return Procedure;
})();
SyntaxError = function(msg) {
return msg;
};
parse = function(inport) {
if (type(inport) === "string") inport = new InPort(new StringIO(inport));
return read(inport);
};
read = function(inport) {
var read_ahead, token1;
read_ahead = function(token) {
var L;
if (token === '(') {
L = [];
while (true) {
token = inport.next_token();
if (token === ')') {
return L;
} else {
L.push(read_ahead(token));
}
}
} else if (token === ')') {
throw SyntaxError('unexpected )');
} else if (quotes[token]) {
return [quotes[token], read(inport)];
} else if (token === eof_object) {
throw SyntaxError('unexpected EOF in list');
} else {
return atom(token);
}
};
token1 = inport.next_token();
if (token1 === eof_object) {
return eof_object;
} else {
return read_ahead(token1);
}
};
string_escape = function(string) {
return string;
};
string_encode = function(string) {
return "\"" + string + "\"";
};
type = (function() {
var classToType, name, _i, _len, _ref3;
classToType = {};
_ref3 = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
name = _ref3[_i];
classToType["[object " + name + "]"] = name.toLowerCase();
}
return function(obj) {
var strType;
strType = Object.prototype.toString.call(obj);
return classToType[strType] || "object";
};
})();
isa = function(x, testType) {
return testType === "Symbol" && x instanceof Symbol || testType === "Func" && (x instanceof Procedure || type(x) === "function") || testType === "Procedure" && x instanceof Procedure || testType === "Boolean" && type(x) === "boolean" || testType === "String" && type(x) === "string" || testType === "Number" && type(x) === "number" || testType === "List" && type(x) === "array" || testType === "Object" && type(x) === "object";
};
atom = function(token) {
if (token === '#t') return true;
if (token === '#f') return false;
if (token[0] === '"') return string_escape(token.slice(1, -1));
if (String(Number(token)) === token) return Number(token);
return sym(token);
};
dict_keys = function(x) {
var k, keys, v;
keys = [];
for (k in x) {
v = x[k];
keys.push(k);
}
return keys;
};
dict_values = function(x) {
var k, v, vals;
vals = [];
for (k in x) {
v = x[k];
vals.push(v);
}
return vals;
};
dict_to_string = function(x, haveSeen) {
var k, v;
if (haveSeen == null) haveSeen = [];
return "{" + (((function() {
var _results;
_results = [];
for (k in x) {
v = x[k];
_results.push("" + (to_string(k)) + ": " + (isa(v, "Object") ? (__indexOf.call(haveSeen, v) >= 0 ? "<CIRCULAR>" : dict_to_string(v, haveSeen.concat([v]))) : to_string(v)));
}
return _results;
})()).join(" ")) + "}";
};
to_string = function(x) {
if (x === true) return "#t";
if (x === false) return "#f";
if (isa(x, "Symbol")) return x.str;
if (isa(x, "String")) return string_encode(x);
if (isa(x, "List")) return "(" + (x.map(to_string).join(' ')) + ")";
if (isa(x, "Number")) return Number(x);
if (isa(x, "Procedure")) return x.toString();
if (isa(x, "Object")) return dict_to_string(x);
return String(x);
};
unbox = function(item) {
if (item.length === 1 && isa(item[0], 'List')) item = item[0];
return item;
};
demand = function(x, predicate, msg) {
if (msg == null) msg = 'wrong length';
if (!predicate) throw "" + (to_string(x)) + ": " + msg;
};
all = function(pred, items) {
var item, _i, _len;
for (_i = 0, _len = items.length; _i < _len; _i++) {
item = items[_i];
if (!pred(item)) return false;
}
return true;
};
is_pair = function(x) {
return isa(x, "List") && x.length > 0;
};
is_key_value_pair = function(x) {
if (isa(x, "Object") && x.key && x.value) {
return true;
} else {
return false;
}
};
cons = function(x, y) {
return [x].concat(y);
};
macro_table = {};
macro_table['let'] = {
applyProc: function(args) {
var atom, body, pos, vals, vars, _len;
vars = [];
vals = [];
for (pos = 0, _len = args.length; pos < _len; pos++) {
atom = args[pos];
if (!(is_pair(atom)) || !(atom[0] === _key_value_pair)) break;
vars.push(atom[1]);
vals.push(atom[2]);
}
body = args.slice(pos);
if (!(is_pair(body[0])) && body.length === 1) {
body = body[0];
} else {
body = unbox(body);
}
return [[_lambda, vars, body]].concat(vals);
}
};
macro_table['let*'] = {
applyProc: function(args) {
if (is_pair(args[0]) && args[0][0] === _key_value_pair) {
return [[_lambda, [args[0][1]], macro_table['let*'].applyProc(args.slice(1))], args[0][2]];
} else {
return args;
}
}
};
gensymid = 0;
add_globals = function(env) {
return env.update({
'gensym': function() {
return sym("__SGENSYM__" + (gensymid++));
},
'enum-at': function(d, dkey) {
dkey = to_string(dkey);
if (isa(dkey, "Number")) dkey = parseInt(dkey);
if (isa(dkey, "Number") && dkey < 0) dkey = d.length + dkey;
return d[dkey];
},
'+': function(x, y) {
return Number(x) + Number(y);
},
'-': function(x, y) {
return x - y;
},
'*': function(x, y) {
return x * y;
},
'/': function(x, y) {
return x / y;
},
'and': function() {
var arg, args, result, _i, _len;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
result = true;
for (_i = 0, _len = args.length; _i < _len; _i++) {
arg = args[_i];
if (!arg) result = false;
}
return result;
},
'or': function() {
var arg, args, result, _i, _len;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
result = false;
for (_i = 0, _len = args.length; _i < _len; _i++) {
arg = args[_i];
if (arg) result = true;
}
return result;
},
'set-dict-prop': function(o, k, v) {
o[k] = v;
return o;
},
'string->symbol': function(x) {
return sym(x);
},
'string-encode': function(x) {
if (isa(x, "Symbol")) {
return x.str;
} else {
return JSON.stringify(x);
}
},
'key': function(x) {
return x;
},
'not': function(x) {
return !x;
},
'>': function(x, y) {
return x > y;
},
'<': function(x, y) {
return x < y;
},
'>=': function(x, y) {
return x >= y;
},
'<=': function(x, y) {
return x <= y;
},
'=': function(x, y) {
return x === y;
},
'sqrt': function(x) {
return Math.sqrt(x);
},
'abs': function(x) {
return Math.abs(x);
},
'equal?': function(x, y) {
return x === y;
},
'eq?': function(x, y) {
return x === y;
},
'length': function(x) {
return x.length;
},
'cons': function(x, y) {
return cons(x, y);
},
'car': function(x) {
return x[0];
},
'cdr': function(x) {
if ((x.slice != null) && x.length) {
return x.slice(1);
} else {
}
},
'append': function(x, y) {
return x.concat(y);
},
'list': function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
return args;
},
'list?': function(x) {
return isa(x, "List");
},
'key-value-pair': function(k, v) {
return {
key: k,
value: v
};
},
'key-value-pair?': function(x) {
return is_key_value_pair(x);
},
'dict': function() {
var args, d, kv, _i, _len;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
d = {};
for (_i = 0, _len = args.length; _i < _len; _i++) {
kv = args[_i];
d[kv.key] = kv.value;
}
return d;
},
'atom?': function(x) {
return !(is_pair(x)) && (!(x.length === 0));
},
'null?': function(x) {
return x.length === 0;
},
'symbol?': function(x) {
return isa(x, "Symbol");
},
'boolean?': function(x) {
return isa(x, "Boolean");
},
'pair?': function(x) {
return is_pair(x);
},
'port?': function(x) {
return isa(x, "File");
},
'map': function(fn, arr) {
var item, _i, _len, _results;
_results = [];
for (_i = 0, _len = arr.length; _i < _len; _i++) {
item = arr[_i];
_results.push(fn.apply({}, [item]));
}
return _results;
},
'filter': function(fn, arr) {
var item, _i, _len, _results;
_results = [];
for (_i = 0, _len = arr.length; _i < _len; _i++) {
item = arr[_i];
if (fn.apply({}, [item])) _results.push(item);
}
return _results;
},
'join': function(tok, arr) {
return arr.join(tok);
},
'map-dict': function(fn, dict) {
var key, result, val;
result = [];
for (key in dict) {
val = dict[key];
result.push(fn.apply({}, [key, val]));
}
return result;
},
'str': function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
return args.join("");
},
'apply': function(func, args) {
return func.apply({}, args);
},
'eval': function(x) {
return _eval(expand(x));
},
'load': function(x) {
return load(x);
},
'compile': function(lang, x) {
return compile(lang, expand(x));
},
'compile-file': function(file) {
return compile(parse(new InPort(new FileIn(file))));
},
'call/cc': function(x) {
return callcc(x);
},
'open-input-file': function(f) {
return new FileIn(f);
},
'close-input-port': function(p) {
return p.close();
},
'open-output-file': function(f) {
return new FileOut(f);
},
'close-output-port': function(p) {
return p.close();
},
'eof-object?': function(x) {
return x === _eof_object;
},
'read-char': function() {
return readchar();
},
'read': function(x) {
return parse(x);
},
'value': function(x) {
return x;
},
'print': function(x) {
return console.log(to_string(x));
},
'file-write': function(name, content) {
return require("fs").writeFileSync(name, content, "UTF-8");
},
'display': function(x, port) {
return port.pr(isa(x, "String") ? x : to_string(x));
},
'require': function(f) {
return require(to_string(f));
},
'file-contents': function(f) {
return require("fs").readFileSync(f, "UTF-8");
}
});
};
global_env = add_globals(new Env());
arity = function(x) {
if (isa(x, "Procedure")) {
return x.arity();
} else {
return x.length;
}
};
_eval = function(x, env) {
var alt, applyTerm, conseq, dkey, dkey1, dkey2, exp, exps, key, lst, obj, proc, result, str, term, test, val, vars, _, _i, _j, _k, _l, _len, _len2, _len3, _len4, _len5, _m, _ref3;
if (env == null) env = false;
env || (env = global_env);
while (true) {
if (isa(x, "Symbol")) {
return env.find(x).at(x);
} else if (!isa(x, "List")) {
return x;
} else if (x[0] === _quote) {
_ = x[0], exp = x[1];
return exp;
} else if (x[0] === _if) {
_ = x[0], test = x[1], conseq = x[2], alt = x[3];
return _eval((_eval(test, env) ? conseq : alt), env);
} else if (x[0] === _set) {
_ = x[0], key = x[1], exp = x[2];
env.find(key, true).setAt(key, _eval(exp, env));
return None;
} else if (x[0] === _define) {
_ = x[0], key = x[1], exp = x[2];
env.setAt(key, _eval(exp, env), true);
return None;
} else if (x[0] === _lambda) {
_ = x[0], vars = x[1], exp = x[2];
return new Procedure(vars, exp, env);
} else if (x[0] === _begin) {
val = false;
_ref3 = x.slice(1);
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
exp = _ref3[_i];
val = _eval(exp, env);
}
return val;
} else {
exps = (function() {
var _j, _len2, _results;
_results = [];
for (_j = 0, _len2 = x.length; _j < _len2; _j++) {
exp = x[_j];
_results.push(_eval(exp, env));
}
return _results;
})();
proc = exps.shift();
if (isa(proc, "Procedure")) {
x = proc.exp;
env = new Env(proc.parms, exps, proc.env);
} else if (proc.apply) {
return proc.apply({}, exps);
} else {
if (isa(proc, "Boolean")) {
if (proc) {
return exps[0];
} else {
if (exps[1] != null) {
return exps[1];
} else {
return false;
}
}
}
if (isa(proc, "Number")) {
result = proc;
for (_j = 0, _len2 = exps.length; _j < _len2; _j++) {
term = exps[_j];
if (applyTerm) {
result = applyTerm.apply({}, [result, term]);
applyTerm = false;
} else if (isa(term, "Number")) {
result *= term;
} else {
if (isa(term, "Func")) {
if (arity(term) === 1) {
result = term.apply({}, [result]);
} else {
applyTerm = term;
}
} else {
throw new Error("encountered " + term + " when evaluating application of Number expected Number of Func");
}
}
}
if (applyTerm) {
result = (function(r, t) {
return function(y) {
return t.apply({}, [r, y]);
};
})(result, applyTerm);
applyTerm = false;
}
return result;
}
if (exps.length && isa(exps[0], "Func")) {
return exps[0].apply({}, [proc].concat(exps.slice(1)));
}
if (isa(proc, "Object") && isa(exps[0], "Object")) {
result = proc;
for (_k = 0, _len3 = exps.length; _k < _len3; _k++) {
obj = exps[_k];
if (isa(obj, "Object")) {
result = _eval([sym("extends"), result, obj]);
} else {
throw "Only objects may be extended to each other";
}
}
return result;
}
if (isa(proc, "List") && isa(exps[0], "List")) {
result = proc;
for (_l = 0, _len4 = exps.length; _l < _len4; _l++) {
lst = exps[_l];
if (isa(lst, "List")) {
result = result.concat(lst);
} else {
throw "only lists may be appended to lists";
}
}
return result;
}
if (isa(proc, "String") && isa(exps[0], "String")) {
result = proc;
for (_m = 0, _len5 = exps.length; _m < _len5; _m++) {
str = exps[_m];
if (isa(str, "String")) {
result += str;
} else {
throw "only strings may be concat to strings";
}
}
return result;
}
if (exps.length === 1) {
dkey = to_string(exps.shift());
if (isa(dkey, "Number")) dkey = parseInt(dkey);
if (isa(dkey, "Number") && dkey < 0) dkey = proc.length + dkey;
return proc[dkey];
}
if (exps.length === 2) {
dkey1 = to_string(exps.shift());
dkey2 = to_string(exps.shift());
if (isa(dkey1, "Number")) dkey1 = parseInt(dkey1);
if (isa(dkey2, "Number")) dkey2 = parseInt(dkey2);
return proc.slice(dkey1, dkey2 + 1 || 9e9);
}
throw (to_string(proc)) + " can not be used as function";
}
}
}
};
desugar = function(x) {
var body, pos, token, _len, _len2, _len3, _len4, _len5, _len6;
for (pos = 0, _len = x.length; pos < _len; pos++) {
token = x[pos];
if (isa(token, 'List')) x[pos] = desugar(token);
}
if (x[0] === sym("squarelambda")) {
if (!(__indexOf.call(x, _pipe) >= 0)) {
return desugar([_lambda, _pipe].concat(x.slice(1)));
} else {
return desugar([_lambda].concat(x.slice(1)));
}
}
for (pos = 0, _len2 = x.length; pos < _len2; pos++) {
token = x[pos];
if (token === _semicolon) {
return desugar([x.slice(0, (pos - 1) + 1 || 9e9)].concat(x.slice(pos + 1)));
}
}
for (pos = 0, _len3 = x.length; pos < _len3; pos++) {
token = x[pos];
if (token === _period) {
if (pos === 0) {
if (!x[1]) throw "Missing param for dot expression ";
return desugar([[_key, x[1]]].concat(x.slice(2)));
} else {
return desugar((pos > 1 ? x.slice(0, (pos - 2) + 1 || 9e9) : []).concat([[_enum_at, x[pos - 1], [_key, x[pos + 1]]]]).concat(x.slice(pos + 2)));
}
}
}
for (pos = 0, _len4 = x.length; pos < _len4; pos++) {
token = x[pos];
if (token === _comma) {
return desugar([x.slice(0, (pos - 1) + 1 || 9e9)].concat(x.slice(pos + 1)));
}
}
for (pos = 0, _len5 = x.length; pos < _len5; pos++) {
token = x[pos];
if (token === _pipe) {
body = x.slice(pos + 1);
if (!(is_pair(body[0])) && body.length === 1) {
body = body[0];
} else {
body = unbox(body);
}
return desugar([x[0], unbox(x.slice(1, (pos - 1) + 1 || 9e9)), body]);
}
}
for (pos = 0, _len6 = x.length; pos < _len6; pos++) {
token = x[pos];
if (token === _colon) {
return (pos > 1 ? x.slice(0, (pos - 2) + 1 || 9e9) : []).concat([[_key_value_pair, x[pos - 1], x[pos + 1]]]).concat(desugar(x.slice(pos + 2)));
}
}
return x;
};
load = function(filename, wrap) {
var fileContent, loadDir, oldDir, _ref3;
if (wrap == null) wrap = true;
if (typeof window !== "undefined" && window !== null) {
fileContent = document.getElementById("soy-script-" + filename).textContent;
} else {
filename = ((_ref3 = filename[0]) === '.' || _ref3 === '/' ? '' : current_dir) + filename;
if (filename.slice(-4) !== ".soy") filename += ".soy";
loadDir = filename.split('/').slice(0, -1).join('/') + '/';
oldDir = current_dir;
current_dir = loadDir;
fileContent = require("fs").readFileSync(filename, "UTF-8").trim();
current_dir = oldDir;
}
return parse(wrap ? "(begin " + fileContent + ")" : fileContent);
};
expand = function(x, toplevel) {
var arg, args, atom, body, bodyExp, def, exp, f, item, lam, leaf, macroed, newBody, pos, proc, result, v, vars, xi, _i, _len, _len2, _len3, _ref3, _ref4;
if (toplevel == null) toplevel = false;
if (isa(x, "List")) demand(x, x.length > 0);
if (!isa(x, "List")) {
return x;
} else if (x[0] === _quote) {
demand(x, x.length === 2);
return x;
} else if (x[0] === _load) {
return expand(desugar(load(expand(x[1]))));
} else if (x[0] === _key) {
return [_quote, x[1]];
} else if (x[0] === _key_value_pair) {
return [_key_value_pair, (isa(x[1], "Symbol") ? x[1].str : expand(x[1])), expand(x[2])];
} else if (x[0] === _dict) {
_ref3 = x.slice(1);
for (pos = 0, _len = _ref3.length; pos < _len; pos++) {
atom = _ref3[pos];
if (!(isa(atom, "List")) || (isa(atom, "List") && atom[0] !== _key_value_pair)) {
x[0] = _list;
}
x[pos + 1] = expand(atom);
}
return x;
} else if (x[0] === _if) {
if (x.length === 3) x.push(None);
demand(x, x.length === 4);
return (function() {
var _i, _len2, _results;
_results = [];
for (_i = 0, _len2 = x.length; _i < _len2; _i++) {
item = x[_i];
_results.push(expand(item));
}
return _results;
})();
} else if (x[0] === _set) {
demand(x, x.length === 3);
v = x[1];
demand(x, isa(v, "Symbol"), "Can set! only a symbol");
return [_set, v, expand(x[2])];
} else if (x[0] === _define || x[0] === _defmacro) {
demand(x, x.length >= 3);
def = x[0], v = x[1], body = 3 <= x.length ? __slice.call(x, 2) : [];
if (isa(v, "List")) {
_ref4 = v[0], f = _ref4[0], args = 2 <= _ref4.length ? __slice.call(_ref4, 1) : [];
return expand([_define, f, [_lambda, args, body]]);
} else {
demand(x, x.length === 3);
demand(x, isa(v, "Symbol"), "can define only a symbol");
exp = expand(x[2]);
if (def === _defmacro) {
proc = _eval(exp);
demand(x, isa(proc, "Procedure"), "macro must be a procedure");
macro_table[to_string(v)] = proc;
return None;
} else {
return [_define, v, exp];
}
}
} else if (x[0] === _begin) {
if (x.length === 1) {
return None;
} else {
result = [];
for (_i = 0, _len2 = x.length; _i < _len2; _i++) {
xi = x[_i];
result.push(expand(xi, toplevel));
}
return result;
}
} else if (x[0] === _lambda) {
demand(x, x.length >= 3);
lam = x[0], vars = x[1], body = 3 <= x.length ? __slice.call(x, 2) : [];
demand(x, (isa(vars, "List") && all((function(v) {
return isa(v, "Symbol");
}), vars)) || isa(vars, "Symbol"), "illegal lambda argument list");
bodyExp = expand(body.length === 1 ? body[0] : [_begin].concat(body));
if (__indexOf.call(vars, _and) >= 0) {
if (vars[vars.length - 2] !== _and) throw "& must be before last arg";
newBody = [];
for (pos = 0, _len3 = vars.length; pos < _len3; pos++) {
arg = vars[pos];
if (arg === _and) break;
newBody.push([_key_value_pair, arg, [sym("_s_args"), pos]]);
}
newBody.push([_key_value_pair, vars[pos + 1], [sym("_s_args"), pos, -1]]);
newBody.push(bodyExp);
vars = sym("_s_args");
bodyExp = macro_table["let"].applyProc(newBody);
}
return [_lambda, vars, bodyExp];
} else if (x[0] === _quasiquote) {
demand(x, x.length === 2);
return expand(expand_quasiquote(x[1]));
} else if (isa(x[0], "Symbol") && macro_table[to_string(x[0])]) {
macroed = macro_table[to_string(x[0])].applyProc(x.slice(1));
return expand(macroed, toplevel);
} else {
return (function() {
var _j, _len4, _results;
_results = [];
for (_j = 0, _len4 = x.length; _j < _len4; _j++) {
leaf = x[_j];
_results.push(expand(leaf));
}
return _results;
})();
}
};
expand_quasiquote = function(x) {
if (!is_pair(x)) return [_quote, x];
demand(x, x[0] !== _unquotesplicing, "can't splice here");
if (x[0] === _unquote) {
demand(x, x.length === 2);
return x[1];
} else if (is_pair(x[0]) && x[0][0] === _unquotesplicing) {
demand(x[0], x[0].length === 2);
return [_append, x[0][1], expand_quasiquote(x.slice(1))];
} else {
if (x[0] === _quasiquote) {
return expand_quasiquote(expand_quasiquote(x.slice(1))[1]);
} else {
return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x.slice(1))];
}
}
};
zip = function(a, b) {
var i, result, x, _fn, _len;
result = {};
_fn = function(x, i) {
return result[to_string(x)] = b[i];
};
for (i = 0, _len = a.length; i < _len; i++) {
x = a[i];
_fn(x, i);
}
return result;
};
unzip = function(arr) {
var a, b, x, _i, _len;
a = [];
b = [];
for (_i = 0, _len = arr.length; _i < _len; _i++) {
x = arr[_i];
a.push(x[0]);
b.push(x[1]);
}
return [a, b];
};
getVar = function(sym) {
var c, newVar, ord, _i, _len, _ref3;
newVar = '';
_ref3 = (sym.str ? sym.str.split("") : sym.split(""));
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
c = _ref3[_i];
ord = (c + '').charCodeAt(0);
if ((ord >= 65 && ord <= 90) || (ord >= 97 && ord <= 122)) {
newVar += c;
} else {
newVar += "_" + ord + "_";
}
}
return newVar;
};
CompileEnv = (function() {
function CompileEnv(parent, defs) {
if (parent == null) parent = None;
if (defs == null) defs = [];
this._defs = {};
this._uses = [];
if (defs.length) this.defs(defs);
if (parent === None) {
this._sets = dict_keys(global_env.values);
} else {
parent.child = this;
}
}
CompileEnv.prototype.defs = function(vname) {
var v, _i, _len, _ref3;
_ref3 = (type(vname) === "array" ? vname : [v]);
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
v = _ref3[_i];
this._defs[to_string(v)] = true;
}
return this;
};
CompileEnv.prototype.uses = function(vname) {
var v, _i, _len, _ref3;
_ref3 = (type(vname) === "array" ? vname : [v]);
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
v = _ref3[_i];
if (!this._defs[to_string(v)]) this._uses[to_string(v)] = true;
}
return this;
};
CompileEnv.prototype.removeUse = function(vname) {
if (this._uses[vname]) delete this._uses[vname];
return this;
};
CompileEnv.prototype.getLexicalUses = function() {
if (this.child) this.uses(this.child.getLexicalUses());
return dict_keys(this._uses);
};
return CompileEnv;
})();
compile = function(targetLang, x, env) {
var alt, cexp, conseq, exp, exps, last, test, v, val, vars, _, _i, _len, _ref3;
if (env == null) env = false;
env || (env = new CompileEnv);
if (isa(x, "Symbol")) {
env.uses(x);
return getVar(x);
} else if (!(isa(x, "List"))) {
return to_string(x);
} else if (x[0] === _quote) {
_ = x[0], exp = x[1];
if (exp instanceof Symbol) {
return exp.str;
} else {
return to_string(x);
}
} else if (x[0] === _if) {
_ = x[0], test = x[1], conseq = x[2], alt = x[3];
return "(" + (compile(targetLang, test, env)) + ") ? (" + (compile(targetLang, conseq, env)) + ") : (" + (compile(targetLang, alt, env)) + ")";
} else if (x[0] === _set) {
_ = x[0], v = x[1], exp = x[2];
env.uses(v);
return "" + (getVar(v)) + " = " + (compile(targetLang, exp, env)) + ";";
} else if (x[0] === _define) {
_ = x[0], v = x[1], exp = x[2];
env.defs(v);
return "var " + (getVar(v)) + " = " + (compile(targetLang, exp, env)) + ";";
} else if (x[0] === _lambda) {
_ = x[0], vars = x[1], exp = x[2];
cexp = compile(targetLang, [_begin, exp], new CompileEnv(env, vars));
console.log("VARS", vars);
return "function(" + (((function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = vars.length; _i < _len; _i++) {
v = vars[_i];
_results.push(getVar(v));
}
return _results;
})()).join(",")) + "){" + cexp + "}";
} else if (x[0] === _enum_at) {
return "" + (compile(targetLang, x[1], env)) + "." + (compile(targetLang, x[2], env));
} else if (x[0] === _begin) {
val = [];
last = x.pop();
_ref3 = x.slice(1);
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
exp = _ref3[_i];
val.push(compile(targetLang, exp, env));
}
if (isa(last, "List")) {
cexp = compile(targetLang, last, new CompileEnv(env));
last = "new Soy.Bounce(function() {return " + cexp + "})";
} else {
last = compile(targetLang, last, env);
}
return "" + (val.join("" + '\n')) + "; return " + last + ";";
} else {
exps = (function() {
var _j, _len2, _results;
_results = [];
for (_j = 0, _len2 = x.length; _j < _len2; _j++) {
exp = x[_j];
_results.push(compile(targetLang, exp, env));
}
return _results;
})();
if (exps[0] === 'apply') {
env.removeUse('apply');
exps.shift();
}
if (exps[0].indexOf(".") !== -1) {
return "" + exps[0] + "(" + (exps.slice(1).join(',')) + ")";
} else {
return "Soy.apply(" + exps[0] + ", [" + (exps.slice(1).join(',')) + "])";
}
}
};
_eval(expand(desugar(load(typeof window !== "undefined" && window !== null ? "radicle" : __dirname + "/radicle.soy"), true)));
exports.setCurrentDir = function(d) {
return current_dir = d;
};
exports.topLevel = global_env;
exports.load = load;
exports.parse = parse;
exports.read = read;
exports.desugar = desugar;
exports.expand = expand;
exports.eval = _eval;
exports.to_string = to_string;
exports.compile = compile;
exports.readEval = function(frm) {
return _eval(expand(desugar(parse(frm))));
};
if (typeof window !== "undefined" && window !== null) {
for (k in global_env) {
v = global_env[k];
window[getVar(k)] = v;
}
}
}).call(this);