shithub: femtolisp

Download patch

ref: 40b76da107627b529cabd18c6c03304a6226e4d5
parent: db405e1a4af5f93671d269d65fa716b8dbdcbdf0
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Dec 5 20:04:09 EST 2024

for-each: work with multiple lists

Fixes: https://todo.sr.ht/~ft/femtolisp/15

--- a/flisp.c
+++ b/flisp.c
@@ -2099,19 +2099,32 @@
 
 BUILTIN("for-each", for_each)
 {
-	argcount(nargs, 2);
+	if(nargs < 2)
+		argcount(nargs, 2);
 	intptr_t argSP = args-FL(stack);
 	assert(argSP >= 0 && argSP < FL(nstack));
-	if(FL(sp)+2 > FL(nstack))
+	if(FL(sp)+nargs > FL(nstack))
 		grow_stack();
-	FL(sp) += 2;
-	while(iscons(FL(stack)[argSP+1])){
-		FL(stack)[FL(sp)-2] = FL(stack)[argSP];
-		FL(stack)[FL(sp)-1] = car_(FL(stack)[argSP+1]);
-		_applyn(1);
-		FL(stack)[argSP+1] = cdr_(FL(stack)[argSP+1]);
+	FL(sp) += nargs;
+
+	for(uint32_t n = 0;; n++){
+		FL(stack)[FL(sp)-nargs] = FL(stack)[argSP];
+
+		uint32_t i, c;
+		for(i = c = 1; i < nargs; i++){
+			if(iscons(FL(stack)[argSP+i])){
+				FL(stack)[FL(sp)-nargs+i] = car_(FL(stack)[argSP+i]);
+				FL(stack)[argSP+i] = cdr_(FL(stack)[argSP+i]);
+				c++;
+			}
+			if(c != i+1 && c != 1)
+				lerrorf(FL(ArgError), "list %d is of different length", i-1);
+		}
+		if(c == 1)
+			break;
+		_applyn(nargs-1);
 	}
-	POPN(2);
+	POPN(nargs);
 	return FL(t);
 }
 
--- a/test/unittest.lsp
+++ b/test/unittest.lsp
@@ -393,6 +393,15 @@
 (assert (null? (caaar '())))
 (assert (null? (cdddr '())))
 
+;; for-each with multiple lists
+(define q '())
+(for-each (λ (x y z) (set! q (cons (+ x y z) q)))
+  '(1 2 3) '(4 5 6) '(7 8 9))
+(assert (equal? q '(18 15 12)))
+(assert-fail (eval '(for-each (λ (x y) (+ x y)) '(1) '(2 3))))
+(assert-fail (eval '(for-each (λ (x y) (+ x y)) '(1 2) '(3))))
+(assert-fail (eval '(for-each (λ (x y z) (+ x y z)) '(1 2) '(3) '(4 5))))
+
 ;; make many initialized tables large enough not to be stored in-line
 (for 1 100 (λ (i)
   (table eq?      2      eqv?     2