Update from master
[jackhill/mal.git] / php / core.php
index 4cb4667..78f107e 100644 (file)
@@ -97,11 +97,15 @@ function concat() {
 }
 
 function nth($seq, $idx) {
-    return $seq[$idx];
+    if ($idx < $seq->count()) {
+        return $seq[$idx];
+    } else {
+        throw new Exception("nth: index out of range");
+    }
 }
 
 function first($seq) {
-    if (count($seq) === 0) {
+    if ($seq === NULL || count($seq) === 0) {
         return NULL;
     } else {
         return $seq[0];
@@ -109,15 +113,32 @@ function first($seq) {
 }
 
 function rest($seq) {
-    $l = new ListClass();
-    $l->exchangeArray(array_slice($seq->getArrayCopy(), 1));
-    return $l;
+    if ($seq === NULL) {
+        return new ListClass();
+    } else {
+        $l = new ListClass();
+        $l->exchangeArray(array_slice($seq->getArrayCopy(), 1));
+        return $l;
+    }
 }
 
 function empty_Q($seq) { return $seq->count() === 0; }
 
 function scount($seq) { return ($seq === NULL ? 0 : $seq->count()); }
 
+function apply($f) {
+    $args = array_slice(func_get_args(), 1);
+    $last_arg = array_pop($args)->getArrayCopy();
+    return $f->apply(array_merge($args, $last_arg));
+}
+
+function map($f, $seq) {
+    $l = new ListClass();
+    # @ to surpress warning if $f throws an exception
+    @$l->exchangeArray(array_map($f, $seq->getArrayCopy()));
+    return $l;
+}
+
 function conj($src) {
     $args = array_slice(func_get_args(), 1);
     $tmp = $src->getArrayCopy();
@@ -132,17 +153,30 @@ function conj($src) {
     return $s;
 }
 
-function apply($f) {
-    $args = array_slice(func_get_args(), 1);
-    $last_arg = array_pop($args)->getArrayCopy();
-    return $f->apply(array_merge($args, $last_arg));
+function seq($src) {
+    if (_list_Q($src)) {
+        if (count($src) == 0) { return NULL; }
+        return $src;
+    } elseif (_vector_Q($src)) {
+        if (count($src) == 0) { return NULL; }
+        $tmp = $src->getArrayCopy();
+        $s = new ListClass();
+        $s->exchangeArray($tmp);
+        return $s;
+    } elseif (_string_Q($src)) {
+        if (strlen($src) == 0) { return NULL; }
+        $tmp = str_split($src);
+        $s = new ListClass();
+        $s->exchangeArray($tmp);
+        return $s;
+    } elseif (_nil_Q($src)) {
+        return NULL;
+    } else {
+        throw new Exception("seq: called on non-sequence");
+    }
+    return $s;
 }
 
-function map($f, $seq) {
-    $l = new ListClass();
-    $l->exchangeArray(array_map($f, $seq->getArrayCopy()));
-    return $l;
-}
 
 
 // Metadata functions
@@ -177,6 +211,8 @@ $core_ns = array(
     'false?'=> function ($a) { return _false_Q($a); },
     'symbol'=> function () { return call_user_func_array('_symbol', func_get_args()); },
     'symbol?'=> function ($a) { return _symbol_Q($a); },
+    'keyword'=> function () { return call_user_func_array('_keyword', func_get_args()); },
+    'keyword?'=> function ($a) { return _keyword_Q($a); },
 
     'string?'=> function ($a) { return _string_Q($a); },
     'pr-str'=> function () { return call_user_func_array('pr_str', func_get_args()); },
@@ -217,10 +253,12 @@ $core_ns = array(
     'rest'=>   function ($a) { return rest($a); },
     'empty?'=> function ($a) { return empty_Q($a); },
     'count'=>  function ($a) { return scount($a); },
-    'conj'=>   function () { return call_user_func_array('conj', func_get_args()); },
     'apply'=>  function () { return call_user_func_array('apply', func_get_args()); },
     'map'=>    function ($a, $b) { return map($a, $b); },
 
+    'conj'=>   function () { return call_user_func_array('conj', func_get_args()); },
+    'seq'=>    function ($a) { return seq($a); },
+
     'with-meta'=> function ($a, $b) { return with_meta($a, $b); },
     'meta'=>   function ($a) { return meta($a); },
     'atom'=>   function ($a) { return _atom($a); },