DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / js / core.js
1 // Node vs browser behavior
2 var core = {};
3 if (typeof module === 'undefined') {
4 var exports = core;
5 } else {
6 var types = require('./types'),
7 readline = require('./node_readline'),
8 reader = require('./reader'),
9 printer = require('./printer'),
10 interop = require('./interop');
11 }
12
13 // Errors/Exceptions
14 function mal_throw(exc) { throw exc; }
15
16
17 // String functions
18 function pr_str() {
19 return Array.prototype.map.call(arguments,function(exp) {
20 return printer._pr_str(exp, true);
21 }).join(" ");
22 }
23
24 function str() {
25 return Array.prototype.map.call(arguments,function(exp) {
26 return printer._pr_str(exp, false);
27 }).join("");
28 }
29
30 function prn() {
31 printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {
32 return printer._pr_str(exp, true);
33 }));
34 }
35
36 function println() {
37 printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {
38 return printer._pr_str(exp, false);
39 }));
40 }
41
42 function slurp(f) {
43 if (typeof require !== 'undefined') {
44 return require('fs').readFileSync(f, 'utf-8');
45 } else {
46 var req = new XMLHttpRequest();
47 req.open("GET", f, false);
48 req.send();
49 if (req.status == 200) {
50 return req.responseText;
51 } else {
52 throw new Error("Failed to slurp file: " + f);
53 }
54 }
55 }
56
57
58 // Number functions
59 function time_ms() { return new Date().getTime(); }
60
61
62 // Hash Map functions
63 function assoc(src_hm) {
64 var hm = types._clone(src_hm);
65 var args = [hm].concat(Array.prototype.slice.call(arguments, 1));
66 return types._assoc_BANG.apply(null, args);
67 }
68
69 function dissoc(src_hm) {
70 var hm = types._clone(src_hm);
71 var args = [hm].concat(Array.prototype.slice.call(arguments, 1));
72 return types._dissoc_BANG.apply(null, args);
73 }
74
75 function get(hm, key) {
76 if (hm != null && key in hm) {
77 return hm[key];
78 } else {
79 return null;
80 }
81 }
82
83 function contains_Q(hm, key) {
84 if (key in hm) { return true; } else { return false; }
85 }
86
87 function keys(hm) { return Object.keys(hm); }
88 function vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); }
89
90
91 // Sequence functions
92 function cons(a, b) { return [a].concat(b); }
93
94 function concat(lst) {
95 lst = lst || [];
96 return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));
97 }
98
99 function nth(lst, idx) {
100 if (idx < lst.length) { return lst[idx]; }
101 else { throw new Error("nth: index out of range"); }
102 }
103
104 function first(lst) { return (lst === null) ? null : lst[0]; }
105
106 function rest(lst) { return (lst == null) ? [] : lst.slice(1); }
107
108 function empty_Q(lst) { return lst.length === 0; }
109
110 function count(s) {
111 if (Array.isArray(s)) { return s.length; }
112 else if (s === null) { return 0; }
113 else { return Object.keys(s).length; }
114 }
115
116 function conj(lst) {
117 if (types._list_Q(lst)) {
118 return Array.prototype.slice.call(arguments, 1).reverse().concat(lst);
119 } else {
120 var v = lst.concat(Array.prototype.slice.call(arguments, 1));
121 v.__isvector__ = true;
122 return v;
123 }
124 }
125
126 function seq(obj) {
127 if (types._list_Q(obj)) {
128 return obj.length > 0 ? obj : null;
129 } else if (types._vector_Q(obj)) {
130 return obj.length > 0 ? Array.prototype.slice.call(obj, 0): null;
131 } else if (types._string_Q(obj)) {
132 return obj.length > 0 ? obj.split('') : null;
133 } else if (obj === null) {
134 return null;
135 } else {
136 throw new Error("seq: called on non-sequence");
137 }
138 }
139
140
141 function apply(f) {
142 var args = Array.prototype.slice.call(arguments, 1);
143 return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1]));
144 }
145
146 function map(f, lst) {
147 return lst.map(function(el){ return f(el); });
148 }
149
150
151 // Metadata functions
152 function with_meta(obj, m) {
153 var new_obj = types._clone(obj);
154 new_obj.__meta__ = m;
155 return new_obj;
156 }
157
158 function meta(obj) {
159 // TODO: support symbols and atoms
160 if ((!types._sequential_Q(obj)) &&
161 (!(types._hash_map_Q(obj))) &&
162 (!(types._function_Q(obj)))) {
163 throw new Error("attempt to get metadata from: " + types._obj_type(obj));
164 }
165 return obj.__meta__;
166 }
167
168
169 // Atom functions
170 function deref(atm) { return atm.val; }
171 function reset_BANG(atm, val) { return atm.val = val; }
172 function swap_BANG(atm, f) {
173 var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2));
174 atm.val = f.apply(f, args);
175 return atm.val;
176 }
177
178 function js_eval(str) {
179 return interop.js_to_mal(eval(str.toString()));
180 }
181
182 function js_method_call(object_method_str) {
183 var args = Array.prototype.slice.call(arguments, 1),
184 r = interop.resolve_js(object_method_str),
185 obj = r[0], f = r[1];
186 var res = f.apply(obj, args);
187 return interop.js_to_mal(res);
188 }
189
190 // types.ns is namespace of type functions
191 var ns = {'type': types._obj_type,
192 '=': types._equal_Q,
193 'throw': mal_throw,
194 'nil?': types._nil_Q,
195 'true?': types._true_Q,
196 'false?': types._false_Q,
197 'number?': types._number_Q,
198 'string?': types._string_Q,
199 'symbol': types._symbol,
200 'symbol?': types._symbol_Q,
201 'keyword': types._keyword,
202 'keyword?': types._keyword_Q,
203 'fn?': types._fn_Q,
204 'macro?': types._macro_Q,
205
206 'pr-str': pr_str,
207 'str': str,
208 'prn': prn,
209 'println': println,
210 'readline': readline.readline,
211 'read-string': reader.read_str,
212 'slurp': slurp,
213 '<' : function(a,b){return a<b;},
214 '<=' : function(a,b){return a<=b;},
215 '>' : function(a,b){return a>b;},
216 '>=' : function(a,b){return a>=b;},
217 '+' : function(a,b){return a+b;},
218 '-' : function(a,b){return a-b;},
219 '*' : function(a,b){return a*b;},
220 '/' : function(a,b){return a/b;},
221 "time-ms": time_ms,
222
223 'list': types._list,
224 'list?': types._list_Q,
225 'vector': types._vector,
226 'vector?': types._vector_Q,
227 'hash-map': types._hash_map,
228 'map?': types._hash_map_Q,
229 'assoc': assoc,
230 'dissoc': dissoc,
231 'get': get,
232 'contains?': contains_Q,
233 'keys': keys,
234 'vals': vals,
235
236 'sequential?': types._sequential_Q,
237 'cons': cons,
238 'concat': concat,
239 'nth': nth,
240 'first': first,
241 'rest': rest,
242 'empty?': empty_Q,
243 'count': count,
244 'apply': apply,
245 'map': map,
246
247 'conj': conj,
248 'seq': seq,
249
250 'with-meta': with_meta,
251 'meta': meta,
252 'atom': types._atom,
253 'atom?': types._atom_Q,
254 "deref": deref,
255 "reset!": reset_BANG,
256 "swap!": swap_BANG,
257
258 'js-eval': js_eval,
259 '.': js_method_call
260 };
261
262 exports.ns = core.ns = ns;