ref: 1905d7b14ebe2c8020b05d1a490bf0b6c6dbdb38
parent: b0802304ffe9bbfbb514c19fef9d721501604e7b
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sun Apr 13 22:53:54 EDT 2025
defstruct: properly typed structures and printing/reading Use #S(..) syntax (as in CL). Use extra bits in vector size to define the "type" of the vector.
--- a/boot/sl.boot
+++ b/boot/sl.boot
@@ -78,7 +78,7 @@
#fn(get) *properties* :kind *doc-extra* filter #fn("n10<20Q:" #(:doc-fmt))
#fn("n10<20Q:" #(:doc-see)) princ foldl #fn("n20=161:") newline "See also:" #fn("n1A<0=700=21522263:" #(getprop
*formals-list* " ")) "Members:" #fn("n1A<070021522263:" #(getprop *formals-list* " ")) void
- "no help for " #fn(str) " " "" " (undefined)")) defstruct #fn("O10005000*///W1000J7071?14W2000J60q?24W3000J60D?34W4000J60q?44W5000J60D?54z6IIb;228;230>1_5142224?<5147586518=<8==268?5127288?528<8?512912:5285;3E0485DC<02902:52@408583;3\\0483H;3M0483DQ;3:04292;052;J504838BP;J5048382;J704171Q;3404084;J:042<02=52I222>18F8E848508?>7?H5148>3G07?02@8>2A8HPe15252@30q42B2Ce18D3\x8d02D8D2Ee22F8C2Ee22G2H2I8Fe2e22J2K2EEe32I0e2e3e32L2M2Ee22N360K@30E12OC708@@80r28@i2Me3e4e3@30qe18E3b02D8E2B1e12I8Fe2e12P12OC708A@;07Q2Rq8A535153e3@30qe12P7S2T8;8B8A8G18D8F0>88@525164:" #(#(:constructor
+ "no help for " #fn(str) " " "" " (undefined)")) defstruct #fn("O10005000*///W1000J7071?14W2000J60q?24W3000J60D?34W4000J60q?44W5000J60D?54z6IIb;228;230>1_5142224?<5147586518=<8==268?5127288?528<8?51171Q85;3E0485DC<02902:52@408583;3\\0483H;3M0483DQ;3:04292;052;J504838BP;J5048382;J704171Q;3404084;J:042<02=52I222>18F8E848508?>7?H5148>3G07?02@8>2A8HPe15252@30q42B2Ce18D3\x9902D8D2Ee22F2G2H2Ee22I2J0e2e2e32K2L2I8Fe2e22M2N2EEe32I0e2e3e32O2P2Ee22Q360K@30E8C3;0r28@i2@408@Me3e4e3@30qe18E3\x8602C2D8E2B1e12R8C;37042Se1512I8Fe2e12R8C3>07T2Uq8A53@408A5154e32V2I0e22I2We28E<e4e3@30qe12R7X2Y8;8B8A8G8C8D8F0>88@525164:" #(#(:constructor
2 :predicate 4 NIL NIL :type 0 :named 1 :conc-name 3 NIL NIL) vec #0#
#fn("n17005121220A>28552485:" #(cddr #fn(for-each)
#fn("n17002152340q:722324A<25F2605661:" #(member (:read-only)
@@ -88,11 +88,12 @@
error "invalid slot name: " #fn(list*) #fn(sym) #\:))) tokw) separate-doc-from-body #fn(length)
#fn(map) #fn("n10B3500<:0:") #fn(sym) #\? "make-" #fn(str) "-" #fn("n12002152853=0220E8553@300853<02208552@40232486252627e1A78C60q@7029Ae2F360q@702:Fe292360q@802;92e2933;02<93e2@30q94S;J80494DQS;39042=94e29596P578764:" #(#fn(str-find)
"\n\n" #fn(str-sub) "" #fn(str) "\n\n " #fn(append) defstruct vec :type :named :constructor
- :conc-name :predicate) fmt) sym-set-doc #fn(append) :doc-fmt #fn(nconc) begin def s and or not
- quote eq? aref = length ,named list #fn(copy-list) foldr #fn("n2202105201PP:" #(#fn(sym) ":"))
- map-int #fn("n1A<70F052517092052219386529422C600@90r20i2KM23872425Ie2e3953?0269524e2e2@30q272825e2292496360K@30E88Me37:2;85523O02<2=2>2?86e22@2?97e22Ae6e2@G02B2496360K@30E88M25e4e4e4:" #(list-ref
- #fn(sym) list def s v assert if void? aref member :read-only error str "slot " quote " in struct "
- " is :read-only" aset!)))) bcode:ctable #fn("n1200Ke3:" #(aref)) with-output-to #fn("z12021e1220e2e1e12315163:" #(#fn(nconc)
+ :conc-name :predicate) fmt) sym-set-doc #fn(append) :doc-fmt #fn(nconc) begin def s and equal?
+ typeof quote struct or not eq? aref = length ,named
+ #fn(copy-list) '%struct% foldr #fn("n2202105201PP:" #(#fn(sym) ":")) putprop constructor map-int
+ #fn("n1A<70F05251709205221938652943<0r20i2KM@30022872324Ie2e3953?0259523e2e2@30q262724e2282396360K@30E88Me3792:85523O02;2<2=2>86e22?2>97e22@e6e2@G02A2396360K@30E88M24e4e4e4:" #(list-ref
+ #fn(sym) def s v assert if void? aref member :read-only error str "slot " quote " in struct " " is :read-only"
+ aset!)))) bcode:ctable #fn("n1200Ke3:" #(aref)) with-output-to #fn("z12021e1220e2e1e12315163:" #(#fn(nconc)
with-bindings *io-out* #fn(copy-list))) catch #fn("n22012122e123242522e2262722e22829e2e3262:22e20e3e42;22e22<22e2e4e3e3:" #(trycatch
λ #:g429 if and cons? eq? car quote thrown-value cadr caddr raise)) let* #fn("z10H3E02021e1qe12215153e1:2021e173051e1e1220=B3H02024e10=e12215153e1@301515375051e2:" #(#fn(nconc)
λ #fn(copy-list) caar let* cadar)) letrec #fn("z1202021e12273052e122240522515154e1227605262:" #(#fn(nconc)
@@ -112,8 +113,8 @@
<= #fn("z1Ib6862086>1_486<^10162:" #(#fn("n21S;JL041<0L2;J5040V340q:A<1<1=62:")) <=) >
#fn("z1Ib6862086>1_486<^10162:" #(#fn("n21S;JE041<0L2;3;04A<1<1=62:")) >) >= #fn("z1Ib6862086>1_486<^10162:" #(#fn("n21S;JL0401<L2;J5040V340q:A<1<1=62:")) >=)
Instructions #table(call.l #u8(81) trycatch #u8(75) loadg.l #u8(68) aref2 #u8(23) box #u8(50) cadr #u8(36) argc #u8(62) setg #u8(71) load0 #u8(21) nan? #u8(38) fixnum? #u8(41) loadc0 #u8(17) loada0 #u8(0) div0 #u8(59) keyargs #u8(31) call #u8(5) loada.l #u8(69) num? #u8(40) sub2 #u8(78) add2 #u8(29) loadc.l #u8(70) loadc #u8(9) builtin? #u8(43) set-car! #u8(47) vargc.l #u8(80) vec #u8(63) ret #u8(10) loadi8 #u8(66) tapply #u8(77) loadvoid #u8(25) loada1 #u8(1) shift #u8(46) atom? #u8(24) cdr #u8(13) brne.l #u8(83) / #u8(58) equal? #u8(52) apply #u8(54) dup #u8(11) loadt #u8(20) jmp.l #u8(48) = #u8(60) not #u8(35) set-cdr! #u8(30) fn? #u8(44) eq? #u8(33) * #u8(57) load1 #u8(27) bound? #u8(42) box.l #u8(86) < #u8(28) brnn.l #u8(84) jmp #u8(16) loadv #u8(2) for #u8(76) dummy_eof #u8(88) + #u8(55) brne #u8(19) argc.l #u8(79) compare #u8(61) brn #u8(3) neg #u8(37) loadv.l #u8(67) vargc #u8(74) brbound #u8(39) loadc1 #u8(22) setg.l #u8(72) cons? #u8(18) aref #u8(85) sym? #u8(34) aset! #u8(64) car #u8(12) cons #u8(32) tcall.l #u8(82) - #u8(56) brn.l #u8(49) optargs #u8(87) closure #u8(14) vec? #u8(45) pop #u8(4) eqv? #u8(51) list #u8(53) seta #u8(15) seta.l #u8(73) brnn #u8(26) loadnil #u8(65) loadg #u8(7) loada #u8(8) tcall #u8(6))
- __finish #fn("n120210>17262:" #(#fn(for-each)
- #fn("n10A61:") *exit-hooks*) __finish)
+ S #fn("z170021521}2:" #(getprop constructor) S) __finish
+ #fn("n120210>17262:" #(#fn(for-each) #fn("n10A61:") *exit-hooks*) __finish)
__init_globals #fn("n07021d37022@402384w4^147025d;350426;J50427w8429w:4qw;47<w=47>w?47@wA:" #(*os-name*
"macos" #fn("n0702161:" #(princ "\e[0m\e[1m#;> \e[0m"))
#fn("n0702161:" #(princ "#;> ")) *prompt* "dos" "\\" "/" *directory-separator* "\n" *linefeed*
--- a/src/cvalues.c
+++ b/src/cvalues.c
@@ -650,8 +650,10 @@
sl_v v = args[0];
argcount(nargs, 1);
switch(tag(v)){
- case TAG_CONS: return sl_conssym;
- case TAG_FIXNUM: return sl_fixnumsym;
+ case TAG_CONS:
+ return sl_conssym;
+ case TAG_FIXNUM:
+ return sl_fixnumsym;
case TAG_UNBOXED:
if(isubnum(v))
return unboxedtypesyms[ubnumtype(v)];
@@ -658,8 +660,12 @@
if(isrune(v))
return sl_runesym;
abort();
- case TAG_SYM: return sl_symsym;
- case TAG_VEC:return sl_vecsym;
+ case TAG_SYM:
+ return sl_symsym;
+ case TAG_VEC:
+ if(isstruct(v))
+ return mk_list2(sl_structsym, vec_elt(v, 0));
+ return sl_vecsym;
case TAG_FN:
if(v == sl_t)
return sl_booleansym;
--- a/src/print.c
+++ b/src/print.c
@@ -491,7 +491,10 @@
if(!sl.print_princ && print_circle_prefix(f, v))
break;
if(isvec(v)){
- outs(f, "#(");
+ if(isstruct(v))
+ outs(f, "#S(");
+ else
+ outs(f, "#(");
int newindent = sl.hpos, est;
int i, sz = vec_size(v);
for(i = 0; i < sz; i++){
--- a/src/read.c
+++ b/src/read.c
@@ -437,7 +437,7 @@
}
take(ctx);
if(i > 0)
- vec_setsize(v, i);
+ vec_setsize(v, i, 0);
return POP();
}
--- a/src/sl.c
+++ b/src/sl.c
@@ -20,7 +20,7 @@
sl_v sl_vtabsym, sl_pagesym, sl_returnsym, sl_escsym, sl_spacesym, sl_deletesym;
sl_v sl_errio, sl_errparse, sl_errtype, sl_errarg, sl_errmem, sl_errconst;
sl_v sl_errdiv0, sl_errbounds, sl_err, sl_errkey, sl_errunbound, sl_erroom;
-sl_v sl_emptyvec, sl_emptystr;
+sl_v sl_emptyvec, sl_emptystr, sl_vecstructsym, sl_structsym;
sl_v sl_printwidthsym, sl_printreadablysym, sl_printprettysym, sl_printlengthsym;
sl_v sl_printlevelsym;
@@ -363,7 +363,7 @@
return sl_emptyvec;
sl_v *c = alloc_words(n+1);
sl_v v = tagptr(c, TAG_VEC);
- vec_setsize(v, n);
+ vec_setsize(v, n, 0);
if(init){
for(usize i = 0; i < n; i++)
vec_elt(v, i) = sl_void;
@@ -439,7 +439,7 @@
forward(v, nc);
}else{
nc = tagptr(alloc_words(sz+1), TAG_VEC);
- vec_setsize(nc, sz);
+ vec_setsize(nc, sz, 0);
a = vec_elt(v, 0);
forward(v, nc);
if(sz > 0){
@@ -1334,6 +1334,8 @@
sl_spacesym = mk_csym("space");
sl_deletesym = mk_csym("delete");
sl_newlinesym = mk_csym("newline");
+ sl_vecstructsym = mk_csym("%struct%");
+ sl_structsym = mk_csym("struct");
sl_builtinssym = mk_csym("*builtins*");
set(sl_printprettysym = mk_csym("*print-pretty*"), sl_t);
@@ -1349,7 +1351,7 @@
}
sl_emptyvec = tagptr(alloc_words(1), TAG_VEC);
- vec_setsize(sl_emptyvec, 0);
+ vec_setsize(sl_emptyvec, 0, 0);
cvalues_init();
--- a/src/sl.h
+++ b/src/sl.h
@@ -153,10 +153,17 @@
*(sl_v*)ptr(v) = (sl_v)(to) | FWD_BIT; \
}while(0)
+enum {
+ VEC_VEC,
+ VEC_STRUCT,
+};
#define vec_elt(v, i) (((sl_v*)ptr(v))[1+(i)])
#define vec_size(v) uintval(vec_elt((v), -1))
-#define vec_setsize(v, n) do{ vec_elt((v), -1) = fixnum(n); }while(0)
+#define vec_setsize(v, n, type) do{ vec_elt((v), -1) = fixnum(n) | (type); }while(0)
+#define vec_type(v) (vec_elt((v), -1) & ((1<<TAG_BITS)-1))
+#define isstruct(v) (vec_type(v) == VEC_STRUCT)
#define vec_grow_amt(x) ((x)<8 ? 5 : 6*((x)>>3))
+
// functions ending in _ are unsafe, faster versions
#define car_(v) (((sl_cons*)ptr(v))->car)
#define cdr_(v) (((sl_cons*)ptr(v))->cdr)
@@ -446,7 +453,7 @@
extern sl_v sl_iosym, sl_rdsym, sl_wrsym, sl_apsym, sl_crsym, sl_truncsym;
extern sl_v sl_s8sym, sl_u8sym, sl_s16sym, sl_u16sym, sl_s32sym, sl_u32sym;
extern sl_v sl_s64sym, sl_u64sym, sl_p32sym, sl_p64sym, sl_ptrsym, sl_bignumsym;
-extern sl_v sl_utf8sym, sl_runesym, sl_floatsym, sl_doublesym;
+extern sl_v sl_utf8sym, sl_runesym, sl_floatsym, sl_doublesym, sl_vecstructsym, sl_structsym;
extern sl_type *sl_bignumtype, *sl_builtintype;
extern sl_type *sl_s8type, *sl_u8type;
--- a/src/system.sl
+++ b/src/system.sl
@@ -1044,6 +1044,9 @@
;;; structs
+(def (S struct . rest)
+ (apply (getprop struct 'constructor) rest))
+
(defmacro (defstruct name (:type vec)
(:named NIL)
(:constructor T)
@@ -1124,8 +1127,8 @@
; slots, but with default values added (if not set)
; and keywords for names
[slots-kw (tokw slots)]
- ; struct's underlying type's predicate (either vec? or list?)
- [type? (sym type #\?)]
+ ; underlying type, either a vector or list
+ [isvec (eq? type vec)]
; struct's predicate name
[is? (and predicate
(if (eq? predicate T)
@@ -1166,25 +1169,28 @@
`(begin
; predicate
,(when is? `(def (,is? s)
- (and [,type? s]
+ (and [equal? (typeof s) '(struct ,name)]
[or (not ',named) (eq? (aref s 0) ',name)]
- [= (length s) ,(+ (if ',named 1 0) (if (eq? type 'list)
- num-slots
- (* 2 num-slots)))])))
+ [= (length s) ,(+ (if ',named 1 0) (if isvec
+ (* 2 num-slots)
+ num-slots))])))
; constructor
- ,(when constructor `(def ,constructor
- (,type
- ',named
- ,@(if (eq? type 'list)
- slots-car
- (foldr (λ (s z) (cons (sym ":" s) (cons s z)))
- NIL
- slots-car)))))
+ ,(when constructor
+ `(begin (def ,constructor
+ (,type
+ ,@(and isvec (list ''%struct%))
+ ',named
+ ,@(if isvec
+ (foldr (λ (s z) (cons (sym ":" s) (cons s z)))
+ NIL
+ slots-car)
+ slots-car)))
+ (putprop ',name 'constructor ,(car constructor))))
; accessor per slot
,@(map-int (λ (i) [let* {[opts (slot-opts (list-ref slots-kw i))]
[fld (list-ref slots-car i)]
[fun (sym access fld)]
- [iv (if (eq? type 'list) i (+ (* 2 i) 1))]}
+ [iv (if isvec (+ (* 2 i) 1) i)]}
`(def (,fun s (v #.(void)))
,(when is? `(assert (,is? s)))
(if (void? v)
--- a/src/vm.h
+++ b/src/vm.h
@@ -815,9 +815,23 @@
n = *ip++;
LABEL(apply_vec):;
sl.sp = sp;
- sl_v v = alloc_vec(n, 0);
- memcpy(&vec_elt(v, 0), sp-n, n*sizeof(sl_v));
+ int type = VEC_VEC;
sp -= n;
+ if(*sp == sl_vecstructsym){
+ if(n < 2){
+ *ipd = (uintptr)ip;
+ arity_error(n, 2);
+ }
+ sp++;
+ n--;
+ type = VEC_STRUCT;
+ }
+ sl_v v = alloc_vec(n, 0);
+ memcpy(&vec_elt(v, 0), sp, n*sizeof(sl_v));
+ if(type != VEC_VEC){
+ sp--;
+ vec_setsize(v, vec_size(v), type);
+ }
*sp++ = v;
NEXT_OP;
}
@@ -866,7 +880,7 @@
}
OP(OP_VECP)
- sp[-1] = isvec(sp[-1]) ? sl_t : sl_nil;
+ sp[-1] = isvec(sp[-1]) && !isstruct(sp[-1]) ? sl_t : sl_nil;
NEXT_OP;
OP(OP_TRYCATCH) {