shithub: femtolisp

Download patch

ref: bda10587a8c0e776dbc7f2717bce95c16e259b8b
parent: 7f5c363205f4a6fd78a7a78fcb1bb00fbf0128d1
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sat Dec 7 20:06:25 EST 2024

for-each: work with vectors, arrays and hash tables

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

--- a/flisp.c
+++ b/flisp.c
@@ -2034,7 +2034,7 @@
 	uint32_t k = FL(sp);
 	PUSH(FL(Nil));
 	PUSH(FL(Nil));
-	for(;;){
+	for(bool first = true;;){
 		PUSH(FL(stack)[argSP]);
 		for(uint32_t i = 1; i < nargs; i++){
 			if(!iscons(FL(stack)[argSP+i])){
@@ -2049,34 +2049,72 @@
 		PUSH(v);
 		value_t c = mk_cons();
 		car_(c) = POP(); cdr_(c) = FL(Nil);
-		if(FL(stack)[k+1] == FL(Nil))
+		if(first)
 			FL(stack)[k+1] = c;
 		else
 			cdr_(FL(stack)[k]) = c;
 		FL(stack)[k] = c;
+		first = false;
 	}
 }
 
 BUILTIN("for-each", for_each)
 {
-	if(nargs < 2)
-		argcount(nargs, 2);
+	if(__unlikely(nargs < 2))
+		lerrorf(FL(ArgError), "too few arguments");
 	intptr_t argSP = args-FL(stack);
 	assert(argSP >= 0 && argSP < FL(nstack));
-	if(FL(sp)+nargs > FL(nstack))
+	if(FL(sp)+2*nargs > FL(nstack))
 		grow_stack();
-	for(;;){
+	for(size_t n = 0;; n++){
 		PUSH(FL(stack)[argSP]);
-		for(uint32_t i = 1; i < nargs; i++){
-			if(!iscons(FL(stack)[argSP+i])){
-				POPN(i);
-				return FL(t);
+		uint32_t pargs = 0;
+		for(uint32_t i = 1; i < nargs; i++, pargs++){
+			value_t v = FL(stack)[argSP+i];
+			if(iscons(v)){
+				PUSH(car_(v));
+				FL(stack)[argSP+i] = cdr_(v);
+				continue;
 			}
-			PUSH(car_(FL(stack)[argSP+i]));
-			FL(stack)[argSP+i] = cdr_(FL(stack)[argSP+i]);
+			if(isvector(v)){
+				size_t sz = vector_size(v);
+				if(n < sz){
+					PUSH(vector_elt(v, n));
+					continue;
+				}
+			}
+			if(isarray(v)){
+				size_t sz = cvalue_arraylen(v);
+				if(n < sz){
+					value_t a[2];
+					a[0] = v;
+					a[1] = fixnum(n);
+					PUSH(cvalue_array_aref(a));
+					continue;
+				}
+			}
+			if(ishashtable(v)){
+				htable_t *h = totable(v);
+				if(n == 0)
+					h->i = 0;
+				void **table = h->table;
+				for(; h->i < h->size; h->i += 2){
+					if(table[h->i+1] != HT_NOTFOUND)
+						break;
+				}
+				if(h->i < h->size){
+					PUSH((value_t)table[h->i]);
+					pargs++;
+					PUSH((value_t)table[h->i+1]);
+					h->i += 2;
+					continue;
+				}
+			}
+			POPN(pargs+1);
+			return FL(t);
 		}
-		_applyn(nargs-1);
-		POPN(nargs);
+		_applyn(pargs);
+		POPN(pargs+1);
 	}
 }
 
--- a/htable.h
+++ b/htable.h
@@ -6,6 +6,10 @@
 	size_t size;
 	void **table;
 	void *_space[HT_N_INLINE];
+
+	// this is to skip over non-items in for-each
+	// FIXME(sigrid): in a multithreaded environment this isn't enough
+	size_t i;
 }htable_t;
 
 // define this to be an invalid key/value
--- a/table.c
+++ b/table.c
@@ -66,7 +66,7 @@
 	print_traverse_htable,
 };
 
-static int
+bool
 ishashtable(value_t v)
 {
 	return iscvalue(v) && cv_class((cvalue_t*)ptr(v)) == FL(tabletype);
@@ -78,7 +78,7 @@
 	return ishashtable(args[0]) ? FL(t) : FL(f);
 }
 
-static htable_t *
+htable_t *
 totable(value_t v)
 {
 	if(!ishashtable(v))
--- a/table.h
+++ b/table.h
@@ -1,1 +1,3 @@
+bool ishashtable(value_t v);
+htable_t *totable(value_t v);
 void table_init(void);
--- a/test/unittest.lsp
+++ b/test/unittest.lsp
@@ -395,7 +395,7 @@
 
 ;; 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))
+(for-each (λ (x y k v) (set! q (cons (+ x y k v) q))) #(1 2 3) #vu8(4 5 6) (table 0 7 1 7 2 7))
 (assert (equal? q '(18 15 12)))
 (define q 0)
 (for-each (λ (x y) (set! q (+ x y q))) '(1) '(3 9))