diff --git a/.gitignore b/.gitignore index 59df3a8b..14a69ef7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ main.s BQN src/runtime +src/runtime0 src/runtime1 src/compiler src/interp diff --git a/genRuntime b/genRuntime index 0417e032..0797896e 100755 --- a/genRuntime +++ b/genRuntime @@ -3,6 +3,7 @@ if [[ $# -ne 1 ]]; then echo "Usage: ./genRuntime path/to/mlochbaum/BQN" exit fi +./cc.bqn $1 r0 > src/runtime0 ./cc.bqn $1 r1 > src/runtime1 ./cc.bqn $1 c > src/compiler ./cc.bqn $1 f > src/formatter diff --git a/src/fillarr.c b/src/fillarr.c index 68b0e669..51a2da83 100644 --- a/src/fillarr.c +++ b/src/fillarr.c @@ -23,6 +23,10 @@ B asFill(B x) { // consumes return bi_noFill; } B getFill(B x) { // consumes; can return bi_noFill + bool defZero = true; + #ifdef CATCH_ERRORS + defZero = false; + #endif if (isArr(x)) { u8 t = v(x)->type; if (t==t_fillarr ) { B r = inc(c(FillArr,x )->fill); dec(x); return r; } @@ -31,13 +35,12 @@ B getFill(B x) { // consumes; can return bi_noFill if (t==t_i32arr || t==t_i32slice) { dec(x); return m_f64(0 ); } if (t==t_f64arr || t==t_f64slice) { dec(x); return m_f64(0 ); } dec(x); - return bi_noFill; + return defZero? m_f64(0) : bi_noFill; } if (isF64(x)|isI32(x)) return m_i32(0); if (isC32(x)) return m_c32(' '); dec(x); - if (isMd(x) || isFun(x)) return bi_noFill; - return bi_noFill; + return defZero? m_f64(0) : bi_noFill; } bool noFill(B x) { return x.u == bi_noFill.u; } @@ -112,6 +115,19 @@ void validateFill(B x) { assert(' '==(u32)x.u); } } + +B fill_both(B w, B x) { // doesn't consume + B fw = getFill(inc(w)); + if (noFill(fw)) return bi_noFill; + B fx = getFill(inc(x)); + if (!equal(fw, fx)) { + dec(fw); dec(fx); + return bi_noFill; + } + dec(fw); + return fx; +} + B withFill(B x, B fill) { // consumes both assert(isArr(x)); #ifdef DEBUG @@ -141,3 +157,7 @@ B withFill(B x, B fill) { // consumes both dec(x); return r; } +B qWithFill(B x, B fill) { // consumes both + if (noFill(fill)) return x; + return withFill(x, fill); +} diff --git a/src/h.h b/src/h.h index 61c0416d..5e6d294d 100644 --- a/src/h.h +++ b/src/h.h @@ -101,7 +101,7 @@ char* format_type(u8 u) { /*arith.c*/ F(add,"+") F(sub,"-") F(mul,"×") F(div,"÷") F(pow,"⋆") F(floor,"⌊") F(ceil,"⌈") F(stile,"|") F(eq,"=") \ /*arith.c*/ F(ne,"≠") F(le,"≤") F(ge,"≥") F(lt,"<") F(gt,">") F(and,"∧") F(or,"∨") F(not,"¬") F(log,"⋆⁼") \ /*fns.c*/ F(ud,"↕") F(fne,"≢") F(feq,"≡") F(ltack,"⊣") F(rtack,"⊢") F(fmtF,"•FmtF") F(fmtN,"•FmtN") \ - /*sfns.c*/ F(shape,"⥊") F(pick,"⊑") F(pair,"{𝕨‿𝕩}") F(select,"⊏") F(slash,"/") F(join,"∾") F(take,"↑") F(drop,"↓") \ + /*sfns.c*/ F(shape,"⥊") F(pick,"⊑") F(pair,"{𝕨‿𝕩}") F(select,"⊏") F(slash,"/") F(join,"∾") F(shiftb,"»") F(take,"↑") F(drop,"↓") \ /*derv.c*/ F(fork,"(fork)") F(atop,"(atop)") F(md1d,"(derived 1-modifier)") F(md2d,"(derived 2-modifier)") \ /*sysfn.c*/ F(type,"•Type") F(decp,"•Decompose") F(primInd,"•PrimInd") F(glyph,"•Glyph") F(fill,"•FillFn") \ /*sysfn.c*/ F(grLen,"•GroupLen") F(grOrd,"•groupOrd") F(asrt,"!") F(sys,"•getsys") F(internal,"•Internal") F(show,"•Show") F(out,"•Out") @@ -216,7 +216,7 @@ bool equal(B w, B x); // doesn't consume void arr_print(B x); // doesn't consume u8 fillElType(B x); // doesn't consume bool eqShape(B w, B x); // doesn't consume -usz arr_csz(B x); // doesn't consume +usz arr_csz(B x); // doesn't consume bool eqShPrefix(usz* w, usz* x, ur len); B m_v1(B a ); // consumes all @@ -337,10 +337,11 @@ usz o2s (B x) { if (x.f!=(f64)(usz)x.f) thrM("Expected integer"); return (usz) i64 o2i64 (B x) { if (x.f!=(f64)(i64)x.f) thrM("Expected integer"); return (i64)x.f; } u64 o2u64 (B x) { if (x.f!=(f64)(u64)x.f) thrM("Expected integer"); return (u64)x.f; } f64 o2f (B x) { if (!isNum(x)) thrM("Expected integer"); return x.f; } -i32 o2iu (B x) { return isI32(x)? (i32)(u32)x.u : (i32)x.f; } u32 o2c (B x) { if (!isC32(x)) thrM("Expected character"); return (u32)x.u; } +i32 o2iu (B x) { return isI32(x)? (i32)(u32)x.u : (i32)x.f; } i32 o2cu (B x) { return (u32)x.u; } usz o2su (B x) { return (usz)x.f; } +i64 o2fu (B x) { return x.f; } i64 o2i64u(B x) { return (i64)x.f; } bool q_i32(B x) { return isI32(x) || isF64(x)&(x.f==(i32)x.f); } bool q_f64(B x) { return isF64(x) || isI32(x); } diff --git a/src/main.c b/src/main.c index bce56f94..267fe1e1 100644 --- a/src/main.c +++ b/src/main.c @@ -10,8 +10,9 @@ // #define USE_VALGRIND // whether to mark freed memory for valgrind // #define DONT_FREE // don't actually ever free objects, such that they can be printed after being freed for debugging // #define OBJ_COUNTER // store a unique allocation number with each object for easier analysis +// #define ALL_R0 // use all of r0.bqn for runtime_0 +// #define ALL_R1 // use all of r1.bqn for runtime #define FAKE_RUNTIME false // whether to disable the self-hosted runtime -// #define ALL_RUNTIME // don't use custom native runtime parts // #define LOG_GC // log GC stats // #define FORMATTER // use self-hosted formatter for output @@ -102,18 +103,18 @@ int main() { // fake runtime B fruntime[] = { - /* +-×÷⋆√⌊⌈|¬ */ bi_add , bi_sub , bi_mul , bi_div , bi_pow , bi_N , bi_floor, bi_ceil, bi_stile, bi_not, - /* ∧∨<>≠=≤≥≡≢ */ bi_and , bi_or , bi_lt , bi_gt , bi_ne , bi_eq , bi_le , bi_ge , bi_feq , bi_fne, - /* ⊣⊢⥊∾≍↑↓↕«» */ bi_ltack, bi_rtack , bi_shape, bi_join , bi_N , bi_take , bi_drop , bi_ud , bi_N , bi_N, - /* ⌽⍉/⍋⍒⊏⊑⊐⊒∊ */ bi_N , bi_N , bi_slash, bi_N , bi_N , bi_select, bi_pick , bi_N , bi_N , bi_N, - /* ⍷⊔!˙˜˘¨⌜⁼´ */ bi_N , bi_N , bi_asrt , bi_const, bi_swap , bi_N , bi_each , bi_tbl , bi_N , bi_fold, - /* ˝`∘○⊸⟜⌾⊘◶⎉ */ bi_N , bi_scan , bi_atop , bi_over , bi_before, bi_after , bi_N , bi_val , bi_cond , bi_N, + /* +-×÷⋆√⌊⌈|¬ */ bi_add , bi_sub , bi_mul , bi_div , bi_pow , bi_N , bi_floor, bi_ceil, bi_stile , bi_not, + /* ∧∨<>≠=≤≥≡≢ */ bi_and , bi_or , bi_lt , bi_gt , bi_ne , bi_eq , bi_le , bi_ge , bi_feq , bi_fne, + /* ⊣⊢⥊∾≍↑↓↕«» */ bi_ltack, bi_rtack , bi_shape, bi_join , bi_N , bi_take , bi_drop , bi_ud , bi_N , bi_shiftb, + /* ⌽⍉/⍋⍒⊏⊑⊐⊒∊ */ bi_N , bi_N , bi_slash, bi_N , bi_N , bi_select, bi_pick , bi_N , bi_N , bi_N, + /* ⍷⊔!˙˜˘¨⌜⁼´ */ bi_N , bi_N , bi_asrt , bi_const, bi_swap , bi_N , bi_each , bi_tbl , bi_N , bi_fold, + /* ˝`∘○⊸⟜⌾⊘◶⎉ */ bi_N , bi_scan , bi_atop , bi_over , bi_before, bi_after , bi_N , bi_val , bi_cond , bi_N, /* ⚇⍟⎊ */ bi_N , bi_repeat, bi_catch }; bool rtComplete[] = { /* +-×÷⋆√⌊⌈|¬ */ 1,1,1,1,1,0,1,1,1,1, /* ∧∨<>≠=≤≥≡≢ */ 1,1,1,1,1,1,1,1,1,1, - /* ⊣⊢⥊∾≍↑↓↕«» */ 1,1,0,0,0,0,0,0,0,0, + /* ⊣⊢⥊∾≍↑↓↕«» */ 1,1,0,1,0,0,0,0,0,1, /* ⌽⍉/⍋⍒⊏⊑⊐⊒∊ */ 0,0,1,0,0,1,0,0,0,0, /* ⍷⊔!˙˜˘¨⌜⁼´ */ 0,0,1,1,1,0,1,1,0,1, /* ˝`∘○⊸⟜⌾⊘◶⎉ */ 0,1,1,1,1,1,0,1,0,0, @@ -124,11 +125,25 @@ int main() { B frtObj = m_caB(rtLen, fruntime); B provide[] = {bi_type,bi_fill,bi_log,bi_grLen,bi_grOrd,bi_asrt,bi_add,bi_sub,bi_mul,bi_div,bi_pow,bi_floor,bi_eq,bi_le,bi_fne,bi_shape,bi_pick,bi_ud,bi_tbl,bi_scan,bi_fillBy,bi_val,bi_catch}; - B runtime_0[] = {bi_floor, bi_ceil, bi_stile, bi_lt, bi_gt, bi_ne, bi_ge, bi_rtack, bi_ltack, bi_join, bi_take, bi_drop, bi_select, bi_const, bi_swap, bi_each, bi_fold, bi_atop, bi_over, bi_before, bi_after, bi_cond, bi_repeat}; + + #ifndef ALL_R0 + B runtime_0[] = {bi_floor,bi_ceil,bi_stile,bi_lt,bi_gt,bi_ne,bi_ge,bi_rtack,bi_ltack,bi_join,bi_take,bi_drop,bi_select,bi_const,bi_swap,bi_each,bi_fold,bi_atop,bi_over,bi_before,bi_after,bi_cond,bi_repeat}; + #else + Block* runtime0_b = compile( + #include "runtime0" + ); + B r0r = m_funBlock(runtime0_b, 0); ptr_dec(runtime0_b); + B* runtime_0 = toHArr(r0r)->a; + #endif Block* runtime_b = compile( #include "runtime1" ); + + #ifdef ALL_R0 + dec(r0r); + #endif + B rtRes = m_funBlock(runtime_b, 0); ptr_dec(runtime_b); B rtObjRaw = TI(rtRes).get(rtRes,0); B rtFinish = TI(rtRes).get(rtRes,1); @@ -144,9 +159,10 @@ int main() { rt_undo = rtObjGet(rtObjRaw, 48); gc_add(rt_undo); rt_select = rtObjGet(rtObjRaw, 35); gc_add(rt_select); rt_slash = rtObjGet(rtObjRaw, 32); gc_add(rt_slash); + rt_join = rtObjGet(rtObjRaw, 23); gc_add(rt_join); for (usz i = 0; i < runtimeLen; i++) { - #ifdef ALL_RUNTIME + #ifdef ALL_R1 B r = rtObjGet(rtObjRaw, i); #else B r = rtComplete[i]? inc(fruntime[i]) : rtObjGet(rtObjRaw, i); diff --git a/src/md1.c b/src/md1.c index d0edf020..ca54c4a0 100644 --- a/src/md1.c +++ b/src/md1.c @@ -1,7 +1,8 @@ #include "h.h" B tbl_c1(B d, B x) { B f = c(Md1D,d)->f; - return eachm(f, x); + // return eachm(f, x); + return withFill(eachm(f, x), m_f64(0)); } B tbl_c2(B d, B w, B x) { B f = c(Md1D,d)->f; if (isAtm(w)) w = m_hunit(w); @@ -27,14 +28,17 @@ B tbl_c2(B d, B w, B x) { B f = c(Md1D,d)->f; memcpy(rsh+wr, a(x)->sh, xr*sizeof(usz)); } dec(w); dec(x); - return r.b; + // return r.b; + return withFill(r.b, m_f64(0)); } B each_c1(B d, B x) { B f = c(Md1D,d)->f; - return eachm(f, x); + // return eachm(f, x); + return withFill(eachm(f, x), m_f64(0)); } B each_c2(B d, B w, B x) { B f = c(Md1D,d)->f; - return eachd(f, w, x); + // return eachd(f, w, x); + return withFill(eachd(f, w, x), m_f64(0)); } @@ -63,7 +67,7 @@ B scan_c1(B d, B x) { B f = c(Md1D,d)->f; B scan_c2(B d, B w, B x) { B f = c(Md1D,d)->f; if (!isArr(x) || rnk(x)==0) thrM("`: 𝕩 cannot have rank 0"); ur xr = rnk(x); usz* xsh = a(x)->sh; usz ia = a(x)->ia; - + B wf = getFill(inc(w)); bool reuse = (v(x)->type==t_harr && reusable(x)) | !ia; usz i = 0; HArr_p r = reuse? harr_parts(x) : m_harrs(a(x)->ia, &i); @@ -84,7 +88,7 @@ B scan_c2(B d, B w, B x) { B f = c(Md1D,d)->f; B pr = r.a[0] = c2(f, w, xget(x,0)); i++; for (; i < ia; i++) r.a[i] = pr = c2(f, inc(pr), xget(x,i)); } - return reuse? x : harr_fcd(r, x); + return withFill(reuse? x : harr_fcd(r, x), wf); } B fold_c1(B d, B x) { B f = c(Md1D,d)->f; diff --git a/src/mut.c b/src/mut.c index bf003969..47fa3afe 100644 --- a/src/mut.c +++ b/src/mut.c @@ -27,17 +27,26 @@ void mut_to(Mut* m, u8 n) { } } -B mut_fv(Mut* m) { - assert(m->type!=el_MAX); +B mut_fv(Mut* m) { assert(m->type!=el_MAX); m->val->sh = &m->val->ia; B r = tag(m->val, ARR_TAG); srnk(r, 1); return r; } -B mut_fp(Mut* m) { - assert(m->type!=el_MAX); +B mut_fp(Mut* m) { assert(m->type!=el_MAX); return tag(m->val, ARR_TAG); } +B mut_fc(Mut* m, B x) { assert(m->type!=el_MAX); + B r = tag(m->val, ARR_TAG); + arr_shCopy(r, x); + return r; +} +B mut_fcd(Mut* m, B x) { assert(m->type!=el_MAX); + B r = tag(m->val, ARR_TAG); + arr_shCopy(r, x); + dec(x); + return r; +} u8 el_or(u8 a, u8 b) { #define M(X) if(b==X) return b>X?b:X; @@ -57,6 +66,44 @@ void mut_pfree(Mut* m, usz n) { // free the first n elements else mm_free((Value*) m->val); } +// doesn't consume x; fills m[ms…ms+l] with x +void mut_fill(Mut* m, usz ms, B x, usz l) { + again: + #define AGAIN(T) { mut_to(m, T); goto again; } + switch(m->type) { + case el_MAX: AGAIN(isF64(x)? (q_i32(x)? el_i32 : el_f64) : (isC32(x)? el_c32 : el_B)); + + case el_i32: { + if (!q_i32(x)) AGAIN(isF64(x)? el_f64 : el_B); + i32* p = ((I32Arr*)m->val)->a+ms; + i32 v = o2iu(x); + for (usz i = 0; i < l; i++) p[i] = v; + return; + } + case el_c32: { + if (!isC32(x)) AGAIN(el_B); + u32* p = ((C32Arr*)m->val)->a+ms; + u32 v = o2cu(x); + for (usz i = 0; i < l; i++) p[i] = v; + return; + } + case el_f64: { + if (!isF64(x)) AGAIN(el_B); + f64* p = ((F64Arr*)m->val)->a+ms; + f64 v = o2fu(x); + for (usz i = 0; i < l; i++) p[i] = v; + return; + } + case el_B: { + B* p = ((HArr*)m->val)->a+ms; + for (usz i = 0; i < l; i++) p[i] = x; + if (isVal(x)) for (usz i = 0; i < l; i++) inc(x); + return; + } + } + #undef AGAIN +} + // expects x to be an array, each position must be written to precisely once // doesn't consume x void mut_copy(Mut* m, usz ms, B x, usz xs, usz l) { diff --git a/src/sfns.c b/src/sfns.c index 8b63c86b..a417b20f 100644 --- a/src/sfns.c +++ b/src/sfns.c @@ -365,24 +365,82 @@ B drop_c2(B t, B w, B x) { i64 v = o2i64(w); usz ia = a(x)->ia; return v<0? slicev(x, 0, v+ia) : slicev(x, v, ia-v); } + +B rt_join; +B join_c1(B t, B x) { + return c1(rt_join, x); +} B join_c2(B t, B w, B x) { - if (!isArr(w)|!isArr(x) || rnk(w)!=1 | rnk(x)!=1) thrM("∾: NYI non-vector args"); - usz wia = a(w)->ia; - usz xia = a(x)->ia; + B f = fill_both(w, x); + if (!isArr(w)) w = m_hunit(w); ur wr = rnk(w); usz wia = a(w)->ia; usz* wsh = a(w)->sh; + if (!isArr(x)) x = m_hunit(x); ur xr = rnk(x); usz xia = a(x)->ia; usz* xsh = a(x)->sh; + ur c = wr>xr?wr:xr; + if (c==0) { + HArr_p r = m_harrUv(2); + r.a[0] = TI(w).get(w,0); dec(w); + r.a[1] = TI(x).get(x,0); dec(x); + return r.b; + } + if (c-wr > 1 || c-xr > 1) thrM("∾: Argument ranks must differ by 1 or less"); MAKE_MUT(r, wia+xia); mut_to(r, el_or(TI(w).elType, TI(x).elType)); mut_copy(r, 0, w, 0, wia); mut_copy(r, wia, x, 0, xia); - dec(x); dec(w); - return mut_fv(r); + B rb = mut_fp(r); + usz* sh = arr_shAllocR(rb, c); + if (sh) { + for (i32 i = 1; i < c; i++) { + usz s = xsh[i+xr-c]; + if (wsh[i+wr-c] != s) { mut_pfree(r, wia+xia); thrM("∾: Lengths not matchable"); } + sh[i] = s; + } + sh[0] = (wr==c? wsh[0] : 1) + (xr==c? xsh[0] : 1); + } + dec(w); dec(x); + return qWithFill(rb, f); +} + +B shiftb_c1(B t, B x) { + if (!isArr(x) || rnk(x)==0) thrM("»: Argument cannot be a scalar"); + usz ia = a(x)->ia; + if (ia==0) return x; + B xf = getFill(inc(x)); + if (noFill(xf)) thrM("»: Argument didn't have a fill"); + usz csz = arr_csz(x); + + MAKE_MUT(r, ia); + mut_copy(r, csz, x, 0, ia-csz); + mut_fill(r, 0, xf, csz); + return qWithFill(mut_fcd(r, x), xf); +} + +void shift_check(B w, B x) { + ur wr = rnk(w); usz* wsh = a(w)->sh; + ur xr = rnk(x); usz* xsh = a(x)->sh; + if (wr+1!=xr & wr!=xr) thrM("shift: =𝕨 must be =𝕩 or ¯1+=𝕩"); + for (i32 i = 1; i < xr; i++) if (wsh[i+wr-xr] != xsh[i]) thrM("shift: Lengths not matchable"); +} +B shiftb_c2(B t, B w, B x) { + // return c2(rt_shiftb, w, x); + if (!isArr(x) || rnk(x)==0) thrM("»: 𝕩 cannot be a scalar"); + if (!isArr(w)) w = m_hunit(w); usz wia = a(w)->ia; + if (!isArr(x)) x = m_hunit(x); usz xia = a(x)->ia; + B f = fill_both(w, x); + shift_check(w, x); + MAKE_MUT(r, xia); + int mid = wiac2 = N##_c2 ;c(Fun,bi_##N)->c1 = N##_c1 ; c(Fun,bi_##N)->extra=pf_##N; c(BFn,bi_##N)->ident=bi_N; gc_add(bi_##N); #define bd(N) bi_##N = mm_alloc(sizeof(BFn), t_funBI, ftag(FUN_TAG)); c(Fun,bi_##N)->c2 = N##_c2 ;c(Fun,bi_##N)->c1 = c1_invalid; c(Fun,bi_##N)->extra=pf_##N; c(BFn,bi_##N)->ident=bi_N; gc_add(bi_##N); #define bm(N) bi_##N = mm_alloc(sizeof(BFn), t_funBI, ftag(FUN_TAG)); c(Fun,bi_##N)->c2 = c2_invalid;c(Fun,bi_##N)->c1 = N##_c1 ; c(Fun,bi_##N)->extra=pf_##N; c(BFn,bi_##N)->ident=bi_N; gc_add(bi_##N); -B bi_shape, bi_pick, bi_pair, bi_select, bi_slash, bi_join, bi_take, bi_drop; -static inline void sfns_init() { ba(shape) ba(pick) ba(pair) ba(select) ba(slash) bd(join) bd(take) bd(drop) +B bi_shape, bi_pick, bi_pair, bi_select, bi_slash, bi_join, bi_shiftb, /*bi_shifta,*/ bi_take, bi_drop; +static inline void sfns_init() { ba(shape) ba(pick) ba(pair) ba(select) ba(slash) ba(join) ba(shiftb) /*ba(shifta)*/ bd(take) bd(drop) } #undef ba diff --git a/src/sysfn.c b/src/sysfn.c index b648b16c..caffa54e 100644 --- a/src/sysfn.c +++ b/src/sysfn.c @@ -34,7 +34,7 @@ B glyph_c1(B t, B x) { B fill_c1(B t, B x) { B f = getFill(x); - if (noFill(f)) return m_f64(0); // thrM("No fill found"); + if (noFill(f)) thrM("No fill found"); return f; } B fill_c2(B t, B w, B x) { // TODO not set fill for typed arrays diff --git a/src/vm.c b/src/vm.c index 26cd3749..90d4566c 100644 --- a/src/vm.c +++ b/src/vm.c @@ -332,8 +332,9 @@ B evalBC(Body* b, Scope* sc) { // doesn't consume case ARRO: case ARRM: { i32 sz = *bc++; HArr_p r = m_harrUv(sz); - for (i32 i = 0; i < sz; i++) r.a[sz-i-1] = POP; - ADD(r.b); + bool allNum = true; + for (i32 i = 0; i < sz; i++) if (!isNum(r.a[sz-i-1] = POP)) allNum = false; + ADD(allNum && sz? withFill(r.b, m_f64(0)) : r.b); break; } case DFND: {