TypeScript: setup initial environment
[jackhill/mal.git] / js / core.js
index b5a52e0..01dad97 100644 (file)
@@ -6,7 +6,8 @@ if (typeof module === 'undefined') {
     var types = require('./types'),
         readline = require('./node_readline'),
         reader = require('./reader'),
-        printer = require('./printer');
+        printer = require('./printer'),
+        interop = require('./interop');
 }
 
 // Errors/Exceptions
@@ -39,7 +40,18 @@ function println() {
 }
 
 function slurp(f) {
-    return require('fs').readFileSync(f, 'utf-8');
+    if (typeof require !== 'undefined') {
+        return require('fs').readFileSync(f, 'utf-8');
+    } else {
+        var req = new XMLHttpRequest();
+        req.open("GET", f, false);
+        req.send();
+        if (req.status == 200) {
+            return req.responseText;
+        } else {
+            throw new Error("Failed to slurp file: " + f);
+        }
+    }
 }
 
 
@@ -84,17 +96,21 @@ function concat(lst) {
     return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));
 }
 
-function nth(lst, idx) { return lst[idx]; }
+function nth(lst, idx) {
+    if (idx < lst.length) { return lst[idx]; }
+    else                  { throw new Error("nth: index out of range"); }
+}
 
-function first(lst) { return lst[0]; }
+function first(lst) { return (lst === null) ? null : lst[0]; }
 
-function rest(lst) { return lst.slice(1); }
+function rest(lst) { return (lst == null) ? [] : lst.slice(1); }
 
 function empty_Q(lst) { return lst.length === 0; }
 
 function count(s) {
     if (Array.isArray(s)) { return s.length; }
-    else {                  return Object.keys(s).length; }
+    else if (s === null)  { return 0; }
+    else                  { return Object.keys(s).length; }
 }
 
 function conj(lst) {
@@ -107,6 +123,21 @@ function conj(lst) {
     }
 }
 
+function seq(obj) {
+    if (types._list_Q(obj)) {
+        return obj.length > 0 ? obj : null;
+    } else if (types._vector_Q(obj)) {
+        return obj.length > 0 ? Array.prototype.slice.call(obj, 0): null;
+    } else if (types._string_Q(obj)) {
+        return obj.length > 0 ? obj.split('') : null;
+    } else if (obj === null) {
+        return null;
+    } else {
+        throw new Error("seq: called on non-sequence");
+    }
+}
+
+
 function apply(f) {
     var args = Array.prototype.slice.call(arguments, 1);
     return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1]));
@@ -144,6 +175,17 @@ function swap_BANG(atm, f) {
     return atm.val;
 }
 
+function js_eval(str) {
+    return interop.js_to_mal(eval(str.toString()));
+}
+
+function js_method_call(object_method_str) {
+    var args = Array.prototype.slice.call(arguments, 1),
+        r = interop.resolve_js(object_method_str),
+        obj = r[0], f = r[1];
+    var res = f.apply(obj, args);
+    return interop.js_to_mal(res);
+}
 
 // types.ns is namespace of type functions
 var ns = {'type': types._obj_type,
@@ -152,8 +194,11 @@ var ns = {'type': types._obj_type,
           'nil?': types._nil_Q,
           'true?': types._true_Q,
           'false?': types._false_Q,
+          'string?': types._string_Q,
           'symbol': types._symbol,
           'symbol?': types._symbol_Q,
+          'keyword': types._keyword,
+          'keyword?': types._keyword_Q,
 
           'pr-str': pr_str,
           'str': str,
@@ -195,7 +240,9 @@ var ns = {'type': types._obj_type,
           'count': count,
           'apply': apply,
           'map': map,
+
           'conj': conj,
+          'seq': seq,
 
           'with-meta': with_meta,
           'meta': meta,
@@ -203,6 +250,10 @@ var ns = {'type': types._obj_type,
           'atom?': types._atom_Q,
           "deref": deref,
           "reset!": reset_BANG,
-          "swap!": swap_BANG};
+          "swap!": swap_BANG,
+
+          'js-eval': js_eval,
+          '.': js_method_call
+};
 
 exports.ns = core.ns = ns;