shithub: femtolisp

Download patch

ref: adb702cdf82a2ac6ccadd8f786234086e7c2743a
parent: 08787a01cdd4103d1e5efa34b661b524651ec538
author: JeffBezanson <jeff.bezanson@gmail.com>
date: Wed Jul 29 00:20:28 EDT 2009

fixing a bug in optional args + rest args with no required arguments
adding some code for keyword argument processing


--- a/femtolisp/Makefile
+++ b/femtolisp/Makefile
@@ -12,7 +12,7 @@
 LIBS = $(LLT) -lm
 
 DEBUGFLAGS = -g -DDEBUG $(FLAGS)
-SHIPFLAGS = -O3 -DNDEBUG $(FLAGS)
+SHIPFLAGS = -O2 -DNDEBUG $(FLAGS)
 
 default: release test
 
--- a/femtolisp/compiler.lsp
+++ b/femtolisp/compiler.lsp
@@ -574,7 +574,7 @@
 
 	  ; emit argument checking prologue
 	  (if (not (null? opta))
-	      (begin (emit g 'optargs (if (null? atail) nreq (- nreq)) nargs)
+	      (begin (emit g 'optargs nreq (if (null? atail) nargs (- nargs)))
 		     (emit-optional-arg-inits g env opta vars nreq)))
 
 	  (cond ((not (null? let?))      (emit g 'let))
--- a/femtolisp/flisp.c
+++ b/femtolisp/flisp.c
@@ -790,6 +790,78 @@
     return v;
 }
 
+/*
+  argument layout on stack is
+  |--required args--|--opt args--|--kw args--|--rest args...
+ */
+static uint32_t process_keys(value_t kwtable,
+                             uint32_t nreq, uint32_t nkw, uint32_t nopt,
+                             uint32_t bp, uint32_t nargs, int va)
+{
+    uint32_t extr = nopt+nkw;
+    uint32_t ntot = nreq+extr;
+    value_t args[extr], v;
+    uint32_t i, a = 0, nrestargs;
+    value_t s1 = Stack[SP-1];
+    value_t s2 = Stack[SP-2];
+    value_t s4 = Stack[SP-4];
+    value_t s5 = Stack[SP-5];
+    if (nargs < nreq)
+        lerror(ArgError, "apply: too few arguments");
+    for (i=0; i < extr; i++) args[i] = UNBOUND;
+    for (i=nreq; i < nargs; i++) {
+        v = Stack[bp+i];
+        if (issymbol(v) && iskeyword((symbol_t*)ptr(v)))
+            break;
+        if (a >= nopt)
+            goto no_kw;
+        args[a++] = v;
+    }
+    if (i >= nargs) goto no_kw;
+    // now process keywords
+    uint32_t n = vector_size(kwtable)/2;
+    do {
+        i++;
+        if (i >= nargs)
+            lerrorf(ArgError, "keyword %s requires an argument",
+                    symbol_name(v));
+        value_t hv = fixnum(((symbol_t*)ptr(v))->hash);
+        uint32_t x = 2*(numval(hv) % n);
+        if (vector_elt(kwtable, x) == v) {
+            uint32_t idx = numval(vector_elt(kwtable, x+1));
+            assert(idx < nkw);
+            idx += (nreq+nopt);
+            if (args[idx] == UNBOUND) {
+                // if duplicate key, keep first value
+                args[idx] = Stack[bp+i];
+            }
+        }
+        else {
+            lerrorf(ArgError, "unsupported keyword %s", symbol_name(v));
+        }
+        i++;
+        if (i >= nargs) break;
+        v = Stack[bp+i];
+    } while (issymbol(v) && iskeyword((symbol_t*)ptr(v)));
+ no_kw:
+    nrestargs = nargs - i;
+    if (!va && nrestargs > 0)
+        lerror(ArgError, "apply: too many arguments");
+    nargs = ntot + nrestargs;
+    if (nrestargs)
+        memmove(&Stack[bp+ntot], &Stack[bp+i], nrestargs*sizeof(value_t));
+    memcpy(&Stack[bp+nreq], args, extr*sizeof(value_t));
+    SP = bp + nargs;
+    assert(SP < N_STACK-5);
+    PUSH(s5);
+    PUSH(s4);
+    PUSH(nargs);
+    PUSH(s2);
+    PUSH(s1);
+    curr_frame = SP;
+    return nargs;
+}
+
 #if _BYTE_ORDER == __BIG_ENDIAN
 #define GET_INT32(a)                            \
     ((((int32_t)a[0])<<0)  |                    \
@@ -935,16 +1007,13 @@
         OP(OP_OPTARGS)
             i = GET_INT32(ip); ip+=4;
             n = GET_INT32(ip); ip+=4;
-            if ((int32_t)i < 0) {
-                if (nargs < -i)
-                    lerror(ArgError, "apply: too few arguments");
-            }
-            else if (nargs < i) {
+            if (nargs < i)
                 lerror(ArgError, "apply: too few arguments");
+            if ((int32_t)n > 0) {
+                if (nargs > n)
+                    lerror(ArgError, "apply: too many arguments");
             }
-            else if (nargs > n) {
-                lerror(ArgError, "apply: too many arguments");
-            }
+            else n = -n;
             if (n > nargs) {
                 n -= nargs;
                 SP += n;
--- a/femtolisp/unittest.lsp
+++ b/femtolisp/unittest.lsp
@@ -123,6 +123,8 @@
 (assert (equal? ((lambda (a (b 2) (c 3)) (list a b c)) 1) '(1 2 3)))
 (assert (equal? ((lambda (a (b 2) (c 3)) (list a b c)) 1 8) '(1 8 3)))
 (assert (equal? ((lambda (a (b 2) (c 3)) (list a b c)) 1 8 9) '(1 8 9)))
+(assert (equal? ((lambda ((x 0) . r) (list x r))) '(0 ())))
+(assert (equal? ((lambda ((x 0) . r) (list x r)) 1 2 3) '(1 (2 3))))
 
 ; ok, a couple end-to-end tests as well
 (define (fib n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))