shithub: femtolisp

Download patch

ref: 0864e3a7e23a6e3f6db9049abd3c70fd7e181606
parent: 33cfd0c3a7b0ace96e79dacacb9c69230a633312
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Mon Dec 9 15:05:25 EST 2024

aref: support multiple indices

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

--- a/compiler.lsp
+++ b/compiler.lsp
@@ -389,6 +389,9 @@
       (apply    (if (< nargs 2)
                     (argc-error b 2)
                     (emit g (if tail? 'tapply 'apply) nargs)))
+      (aref     (cond ((= nargs 2) (emit g 'aref0))
+                      ((> nargs 2) (emit g b (- nargs 2)))
+                      (else (argc-error b 2))))
       (else     (emit g b)))))
 
 (define (inlineable? form)
@@ -777,7 +780,7 @@
                   (set! i (+ i 1)))
 
                  ((loada seta loadc call tcall list + - * / vector
-                   argc vargc loadi8 apply tapply closure box shift)
+                   argc vargc loadi8 apply tapply closure box shift aref)
                   (print-inst inst i 1)
                   (princ (number->string (aref code i)))
                   (set! i (+ i 1)))
--- a/flisp.boot
+++ b/flisp.boot
@@ -1,9 +1,8 @@
 (*builtins* #(0 0 0 0 0 0 0 0 0 0 0 0 #fn("5000n10<:" #())
-	      #fn("5000n10=:" #()) 0 0 0 0 #fn("5000n10B:" #()) 0 0 0 0 #fn("6000n201G:" #())
-	      #fn("5000n10H:" #()) 0 0 0 #fn("6000n201L:" #()) 0 #fn("6000n201N:" #())
-	      0 #fn("6000n201P:" #()) #fn("6000n201Q:" #())
-	      #fn("5000n10R:" #()) #fn("5000n10S:" #())
-	      #fn("5000n10T:" #()) 0 #fn("5000n10V:" #())
+	      #fn("5000n10=:" #()) 0 0 0 0 #fn("5000n10B:" #()) 0 0 0 0 0 #fn("5000n10H:" #())
+	      0 0 0 #fn("6000n201L:" #()) 0 #fn("6000n201N:" #()) 0 #fn("6000n201P:" #())
+	      #fn("6000n201Q:" #()) #fn("5000n10R:" #())
+	      #fn("5000n10S:" #()) #fn("5000n10T:" #()) 0 #fn("5000n10V:" #())
 	      #fn("5000n10W:" #()) #fn("5000n10X:" #())
 	      #fn("5000n10Y:" #()) #fn("5000n10Z:" #())
 	      #fn("5000n10[:" #()) #fn("5000n10\\:" #())
@@ -15,7 +14,8 @@
 	      #fn("8000z0700}2:" #(div0))
 	      #fn("6000n201l:" #()) #fn("6000n201m:" #()) 0 #fn("8000z0700}2:" #(vector))
 	      #fn("7000n30182p:" #()) 0 0 0 0 0 0 0 0 0 0 0 #fn("9000n3012082>1|:" #(#fn("6000n1A061:" #())))
-	      0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
+	      0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #fn("8000z0700}2:" #(aref)) 0 0 0
+	      0)
 	    *empty-string* "" *runestring-type* (array rune) *string-type* (array
   byte)
 	    *syntax-environment* #table(when #fn(";000z1200211POe4:" #(if begin))  with-output-to #fn("<000z12021e1220e2e1e12315163:" #(#fn(nconc)
@@ -49,7 +49,7 @@
   length=) 1arg-lambda?)
 	    <= #fn("6000n210L;IB0470051;380470151S:" #(nan?) <=) >
 	    #fn("6000n210L:" #() >) >= #fn("6000n201L;IB0470051;380470151S:" #(nan?) >=)
-	    Instructions #table(call.l 81  trycatch 75  largc 79  loadg.l 68  box 90  cadr 36  argc 62  setg 71  load0 21  vector? 45  fixnum? 41  loadc0 17  loada0 0  div0 59  keyargs 89  call 5  loada.l 69  brt.l 50  pair? 18  sub2 78  add2 29  loadc.l 70  loadc 9  builtin? 43  set-car! 47  brt 25  ret 10  loadi8 66  tapply 77  loada1 1  shift 46  boolean? 39  atom? 24  cdr 13  brne.l 83  / 58  loadf 31  equal? 52  apply 54  dup 11  loadt 20  jmp.l 48  null? 38  not 35  = 60  set-cdr! 30  eq? 33  * 57  load1 27  dummy_t 93  bound? 42  brf 3  function? 44  box.l 91  < 28  brnn.l 84  jmp 16  loadv 2  for 76  lvargc 80  dummy_eof 95  + 55  dummy_f 92  brne 19  compare 61  neg 37  loadv.l 67  number? 40  vargc 74  brn 85  brbound 88  vector 63  loadc1 22  setg.l 72  brf.l 49  aref 23  symbol? 34  aset! 64  car 12  cons 32  tcall.l 82  - 56  brn.l 86  optargs 87  closure 14  pop 4  eqv? 51  list 53  seta 15  seta.l 73  brnn 26  loadnil 65  loadg 7  loada 8  dummy_nil 94  tcall 6)
+	    Instructions #table(call.l 81  trycatch 75  largc 79  loadg.l 68  box 90  cadr 36  argc 62  setg 71  load0 21  vector? 45  fixnum? 41  loadc0 17  loada0 0  div0 59  keyargs 89  call 5  loada.l 69  brt.l 50  pair? 18  sub2 78  add2 29  loadc.l 70  loadc 9  builtin? 43  set-car! 47  brt 25  ret 10  loadi8 66  tapply 77  loada1 1  shift 46  aref0 23  boolean? 39  atom? 24  cdr 13  brne.l 83  / 58  loadf 31  equal? 52  apply 54  dup 11  loadt 20  jmp.l 48  null? 38  not 35  = 60  set-cdr! 30  eq? 33  * 57  load1 27  dummy_t 94  bound? 42  brf 3  function? 44  box.l 91  < 28  brnn.l 84  jmp 16  loadv 2  for 76  lvargc 80  dummy_eof 96  + 55  dummy_f 93  brne 19  compare 61  neg 37  loadv.l 67  number? 40  vargc 74  brn 85  brbound 88  vector 63  loadc1 22  setg.l 72  brf.l 49  aref 92  symbol? 34  aset! 64  car 12  cons 32  tcall.l 82  - 56  brn.l 86  optargs 87  closure 14  pop 4  eqv? 51  list 53  seta 15  seta.l 73  brnn 26  loadnil 65  loadg 7  loada 8  dummy_nil 95  tcall 6)
 	    __init_globals #fn("5000n020w1422w3474w5476w7478w9:" #("/"
 								   *directory-separator*
 								   "\n"
@@ -69,7 +69,7 @@
 	    __start #fn("7000n1705040=B3D00=w14Ow24730T51@C00w14Dw24745047550426E61:" #(__init_globals
   *argv* *interactive* __script __rcscript repl #fn(exit)) __start)
 	    abs #fn("6000n10EL3500U:0:" #() abs) any
-	    #fn("7000n21B;3D0401<51;I:047001=62:" #(any) any) arg-counts #table(bound? 1  function? 1  symbol? 1  aset! 3  car 1  cons 2  < 2  cadr 1  for 3  vector? 1  fixnum? 1  boolean? 1  atom? 1  cdr 1  div0 2  equal? 2  eqv? 2  pair? 1  compare 2  null? 1  not 1  number? 1  = 2  set-cdr! 2  eq? 2  builtin? 1  set-car! 2  aref 2)
+	    #fn("7000n21B;3D0401<51;I:047001=62:" #(any) any) arg-counts #table(bound? 1  function? 1  symbol? 1  aset! 3  car 1  cons 2  < 2  cadr 1  for 3  vector? 1  fixnum? 1  boolean? 1  cdr 1  atom? 1  div0 2  equal? 2  eqv? 2  pair? 1  compare 2  null? 1  not 1  number? 1  = 2  set-cdr! 2  eq? 2  builtin? 1  set-car! 2)
 	    argc-error #fn(";000n2702102211Kl37023@402465:" #(error "compile error: "
 							      " expects " " argument."
 							      " arguments.") argc-error)
@@ -94,7 +94,7 @@
   length= cons 'unquote any splice-form? lastcdr #fn(map)
   #fn("7000n1700A62:" #(bq-bracket1)) #fn(nconc) list* #fn("=000n20J;02071151P:0B3n00<22CW020731AEl3700=@C07425e2760=AK~52e252P:F<0=770<A521P62:2071760A521P51P:" #(nconc
   reverse! unquote nreconc list 'unquote bq-process bq-bracket))) bq-process)
-	    builtin->instruction #fn("8000n120A0O63:" #(#fn(get)) #(#table(#.cadr cadr  #.aset! aset!  #.+ +  #.- -  #.equal? equal?  #.eq? eq?  #.builtin? builtin?  #.not not  #.pair? pair?  #.aref aref  #.cdr cdr  #./ /  #.div0 div0  #.set-car! set-car!  #.vector vector  #.set-cdr! set-cdr!  #.< <  #.for for  #.cons cons  #.apply apply  #.eqv? eqv?  #.vector? vector?  #.list list  #.car car  #.bound? bound?  #.function? function?  #.null? null?  #.symbol? symbol?  #.compare compare  #.boolean? boolean?  #.fixnum? fixnum?  #.atom? atom?  #.= =  #.number? number?  #.* *)))
+	    builtin->instruction #fn("8000n120A0O63:" #(#fn(get)) #(#table(#.cadr cadr  #.aset! aset!  #.+ +  #.- -  #.equal? equal?  #.eq? eq?  #.builtin? builtin?  #.not not  #.pair? pair?  #.cdr cdr  #./ /  #.div0 div0  #.set-car! set-car!  #.vector vector  #.set-cdr! set-cdr!  #.< <  #.for for  #.cons cons  #.apply apply  #.eqv? eqv?  #.vector? vector?  #.list list  #.aref aref  #.car car  #.bound? bound?  #.function? function?  #.null? null?  #.symbol? symbol?  #.compare compare  #.boolean? boolean?  #.fixnum? fixnum?  #.atom? atom?  #.= =  #.number? number?  #.* *)))
 	    caaaar #fn("5000n10<<<<:" #() caaaar) caaadr
 	    #fn("5000n10T<<:" #() caaadr) caaar #fn("5000n10<<<:" #() caaar)
 	    caadar #fn("5000n10<T<:" #() caadar) caaddr
@@ -131,9 +131,9 @@
   bcode:stack)) #fn(length)) compile-arglist)
 	    compile-begin #fn("9000n483H3?0700182715064:83=H3>070018283<64:7001O83<5447202352474018283=64:" #(compile-in
   void emit pop compile-begin) compile-begin)
-	    compile-builtin-call #fn("<000n7207185O538;3I07283=8;52I=073858;52@30D4858<24CK086El3:07502662:750858663:8<27C[086El3:07502862:86r2l3:07502962:750858663:8<2:Cj086El3:07385K62:86Kl3:07502;62:86r2l3:07502<62:750858663:8<2=CK086El3:07502>62:750858663:8<2?CK086El3:07385K62:750858663:8<2@CM086El3<07502A2B63:750858663:8<2CCW086r2L3;07385r262:750823702D@402C8663:7508562:" #(#fn(get)
+	    compile-builtin-call #fn("=000n7207185O538;3I07283=8;52I=073858;52@30D4858<24CK086El3:07502662:750858663:8<27C[086El3:07502862:86r2l3:07502962:750858663:8<2:Cj086El3:07385K62:86Kl3:07502;62:86r2l3:07502<62:750858663:8<2=CK086El3:07502>62:750858663:8<2?CK086El3:07385K62:750858663:8<2@CM086El3<07502A2B63:750858663:8<2CCW086r2L3;07385r262:750823702D@402C8663:8<2ECc086r2l3:07502F62:7G86r2523?07508586r2~63:7385r262:7508562:" #(#fn(get)
   arg-counts length= argc-error list emit loadnil + load0 add2 - neg sub2 *
-  load1 / vector loadv #() apply tapply) compile-builtin-call)
+  load1 / vector loadv #() apply tapply aref aref0 >) compile-builtin-call)
 	    compile-f #fn("8000n2702101>22262:" #(call-with-values #fn("7000n070AF62:" #(compile-f-))
 						  #fn("5000n20:" #())) compile-f)
 	    compile-f- #fn("O000n270501T711T517215173741T52711518;J7025@408;87H360E@802687518=268:51~73778:528:\x85\xa208?JL07886298>88J708=@508=U54@r07:867;2<7=2<7>8?527?268?5151535152478862@8>268?5188J708=@508=U5547A8608:898>55@30D47B8=2C523I0788688J702D@402E8=53@W088\x85?078862F8=53@E08:J?078862G8=53@30O47H0897I7J1518952537K868@<52486r4268951r4Mp47L868@D7J15154478862M5247N2O7P7Q8651517R86518<537S865162:" #(make-code-emitter
@@ -196,7 +196,7 @@
   Instructions > #fn("6000n1702161:" #(princ "\t"))
   #fn(memq) (loadv.l loadg.l setg.l) ref-int32-LE (loadv loadg setg)
   (loada seta loadc call tcall list + - * / vector argc vargc loadi8 apply
-   tapply closure box shift) princ #fn(number->string)
+   tapply closure box shift aref) princ #fn(number->string)
   (loada.l seta.l loadc.l largc lvargc call.l tcall.l box.l) (optargs keyargs)
   keyargs " " brbound (jmp brf brt brne brnn brn) "@" hex5 ref-int16-LE (jmp.l
   brf.l brt.l brne.l brnn.l brn.l)) disassemble)
--- a/flisp.c
+++ b/flisp.c
@@ -921,6 +921,7 @@
 		GOTO_OP_OFFSET(OP_LOADT),
 		GOTO_OP_OFFSET(OP_LOAD0),
 		GOTO_OP_OFFSET(OP_LOADC1),
+		GOTO_OP_OFFSET(OP_AREF0),
 		GOTO_OP_OFFSET(OP_AREF),
 		GOTO_OP_OFFSET(OP_ATOMP),
 		GOTO_OP_OFFSET(OP_BRT),
@@ -1250,7 +1251,7 @@
 			PUSH(vector_elt(FL(stack)[bp+nargs], 1));
 			NEXT_OP;
 
-		OP(OP_AREF)
+		OP(OP_AREF0)
 			FL(stack)[ipd] = (uintptr_t)ip;
 			v = FL(stack)[FL(sp)-2];
 			if(isvector(v)){
@@ -1266,6 +1267,27 @@
 			}
 			POPN(1);
 			FL(stack)[FL(sp)-1] = v;
+			NEXT_OP;
+
+		OP(OP_AREF)
+			FL(stack)[ipd] = (uintptr_t)ip;
+			n = 1 + *ip++;
+			v = FL(stack)[FL(sp)-n-1];
+			for(i = n; i > 0; i--){
+				if(isvector(v)){
+					e = FL(stack)[FL(sp)-i];
+					isz = tosize(e);
+					if(__unlikely(isz >= vector_size(v)))
+						bounds_error(v, e);
+					v = vector_elt(v, isz);
+				}else if(__likely(isarray(v))){
+					v = cvalue_array_aref(&FL(stack)[FL(sp)-i]);
+				}else{
+					type_error("sequence", v);
+				}
+			}
+			POPN(n+1);
+			PUSH(v);
 			NEXT_OP;
 
 		OP(OP_ATOMP)
--- a/gen.lsp
+++ b/gen.lsp
@@ -23,7 +23,7 @@
     OP_LOADT          loadt     #f      0
     OP_LOAD0          load0     #f      0
     OP_LOADC1         loadc1    #f      0
-    OP_AREF           aref      2       (λ (x y) (aref x y))
+    OP_AREF0          aref0     #f      0
     OP_ATOMP          atom?     1       (λ (x) (atom? x))
     OP_BRT            brt       #f      0
     OP_BRNN           brnn      #f      0
@@ -92,6 +92,7 @@
     OP_KEYARGS        keyargs   #f      0
     OP_BOX            box       #f      0
     OP_BOXL           box.l     #f      0
+    OP_AREF           aref      -2      (λ rest (apply aref rest))
     OP_BOOL_CONST_F   dummy_f   #f      0
     OP_BOOL_CONST_T   dummy_t   #f      0
     OP_THE_EMPTY_LIST dummy_nil #f      0
--- a/maxstack.inc
+++ b/maxstack.inc
@@ -30,8 +30,13 @@
 		case OP_CONS: case OP_SETCAR: case OP_SETCDR:
 		case OP_EQ: case OP_EQV: case OP_EQUAL: case OP_ADD2: case OP_SUB2:
 		case OP_IDIV: case OP_NUMEQ: case OP_LT: case OP_COMPARE:
-		case OP_AREF: case OP_TRYCATCH:
+		case OP_AREF0: case OP_TRYCATCH:
 			sp--;
+			break;
+
+		case OP_AREF:
+			n = 1 + *ip++;
+			sp -= n;
 			break;
 
 		case OP_ARGC: case OP_SETG: case OP_SETA: case OP_BOX:
--- a/opcodes.h
+++ b/opcodes.h
@@ -22,7 +22,7 @@
 	OP_LOADT,
 	OP_LOAD0,
 	OP_LOADC1,
-	OP_AREF,
+	OP_AREF0,
 	OP_ATOMP,
 	OP_BRT,
 	OP_BRNN,
@@ -91,6 +91,7 @@
 	OP_KEYARGS,
 	OP_BOX,
 	OP_BOXL,
+	OP_AREF,
 	OP_BOOL_CONST_F,
 	OP_BOOL_CONST_T,
 	OP_THE_EMPTY_LIST,
@@ -129,8 +130,8 @@
 	[OP_PAIRP] = {"pair?", 1},
 	[OP_MUL] = {"*", ANYARGS},
 	[OP_FOR] = {"for", 3},
-	[OP_AREF] = {"aref", 2},
 	[OP_ADD] = {"+", ANYARGS},
+	[OP_AREF] = {"aref", -2},
 	[OP_DIV] = {"/", -1},
 	[OP_VECTOR] = {"vector", ANYARGS},
 	[OP_EQUAL] = {"equal?", 2},
--- a/test/unittest.lsp
+++ b/test/unittest.lsp
@@ -411,6 +411,15 @@
 (assert (equal? (map (λ (x y) (+ x y)) '(1 2) '(3)) '(4)))
 (assert (equal? (map (λ (x y z) (+ x y z)) '(1 2) '(3) '(4 5)) '(8)))
 
+;; aref with multiple indices
+(define a #(#(0 1 2) #(3 #(4 5 6) 7)))
+(assert (equal? 0 (aref a 0 0)))
+(assert (equal? 2 (aref a 0 2)))
+(assert (equal? 3 (aref a 1 0)))
+(assert (equal? 7 (aref a 1 2)))
+(assert (equal? 5 (aref a 1 1 1)))
+(assert-fail (aref a 1 1 3) bounds-error)
+
 ;; make many initialized tables large enough not to be stored in-line
 (for 1 100 (λ (i)
   (table eq?      2      eqv?     2