#include #include #include #include #include "../core.h" #include "../utils/hash.h" #include "../utils/file.h" #include "../utils/wyhash.h" #include "../utils/mut.h" #include "../builtins.h" #include "../ns.h" #include "../nfns.h" B type_c1(B t, B x) { i32 r = -1; if (isArr(x)) r = 0; else if (isF64(x)) r = 1; else if (isC32(x)) r = 2; else if (isFun(x)) r = 3; else if (isMd1(x)) r = 4; else if (isMd2(x)) r = 5; else if (isNsp(x)) r = 6; if (RARE(r==-1)) { print(x); err(": getting type"); } decR(x); return m_i32(r); } B decp_c1(B t, B x) { if (!isVal(x)) return m_v2(m_i32(-1), x); if (isPrim(x)) return m_v2(m_i32(0), x); return TI(x,decompose)(x); } B primInd_c1(B t, B x) { if (!isVal(x)) return m_i32(rtLen); if (isPrim(x)) { B r = m_i32(v(x)->flags-1); dec(x); return r; } dec(x); return m_i32(rtLen); } B nsFmt(B x); #ifdef RT_WRAP B rtWrap_unwrap(B x); #endif B glyph_c1(B t, B x) { if (!isVal(x)) return m_str32(U"(•Glyph: not given a function)"); #ifdef RT_WRAP x = rtWrap_unwrap(x); #endif if (isPrim(x)) { B r = m_c32(U"+-×÷⋆√⌊⌈|¬∧∨<>≠=≤≥≡≢⊣⊢⥊∾≍↑↓↕«»⌽⍉/⍋⍒⊏⊑⊐⊒∊⍷⊔!˙˜˘¨⌜⁼´˝`∘○⊸⟜⌾⊘◶⎉⚇⍟⎊"[v(x)->flags-1]); dec(x); return r; } u8 ty = v(x)->type; if (ty==t_funBI) { B r = fromUTF8l(format_pf (c(Fun,x)->extra)); dec(x); return r; } if (ty==t_md1BI) { B r = fromUTF8l(format_pm1(c(Md1,x)->extra)); dec(x); return r; } if (ty==t_md2BI) { B r = fromUTF8l(format_pm2(c(Md2,x)->extra)); dec(x); return r; } if (ty==t_nfn) { B r = nfn_name(x); dec(x); return r; } if (ty==t_fun_block) { dec(x); return m_str8l("(function block)"); } if (ty==t_md1_block) { dec(x); return m_str8l("(1-modifier block)"); } if (ty==t_md2_block) { dec(x); return m_str8l("(2-modifier block)"); } if (ty==t_ns) return nsFmt(x); return m_str32(U"(•Glyph: given object with unexpected type)"); } B repr_c1(B t, B x) { if (isF64(x)) { NUM_FMT_BUF(buf, x.f); return fromUTF8(buf, strlen(buf)); } else { #if FORMATTER return bqn_repr(x); #else thrM("•Repr: Cannot represent non-numbers without FORMATTER defined"); #endif } } B fmt_c1(B t, B x) { #if FORMATTER return bqn_fmt(x); #else thrM("•Fmt isn't supported without FORMATTER defined"); #endif } B fill_c1(B t, B x) { B r = getFillE(x); dec(x); return r; } B fill_c2(B t, B w, B x) { // TODO not set fill for typed arrays if (isArr(x)) { B fill = asFill(w); if (noFill(fill)) return x; return withFill(x, fill); } dec(w); return x; } B grLen_both(i64 ria, B x) { usz ia = a(x)->ia; SGetU(x) for (usz i = 0; i < ia; i++) { i64 c = o2i64u(GetU(x, i)); if (c>ria) ria = c; } if (ria > (i64)(USZ_MAX-1)) thrOOM(); ria++; i32* rp; B r = m_i32arrv(&rp, ria); for (usz i = 0; i < ria; i++) rp[i] = 0; for (usz i = 0; i < ia; i++) { i64 n = o2i64u(GetU(x, i)); if (n>=0) rp[n]++; assert(n>=-1); } dec(x); return r; } B grLen_c1(B t, B x) { return grLen_both( -1, x); } // assumes valid arguments B grLen_c2(B t, B w, B x) { return grLen_both(o2i64u(w)-1, x); } // assumes valid arguments B grOrd_c2(B t, B w, B x) { // assumes valid arguments usz wia = a(w)->ia; usz xia = a(x)->ia; if (wia==0) { dec(w); dec(x); return emptyIVec(); } if (xia==0) { dec(w); return x; } SGetU(w) SGetU(x) TALLOC(usz, tmp, wia); tmp[0] = 0; for (usz i = 1; i < wia; i++) tmp[i] = tmp[i-1]+o2su(GetU(w,i-1)); usz ria = tmp[wia-1]+o2su(GetU(w,wia-1)); i32* rp; B r = m_i32arrv(&rp, ria); if (xia>=I32_MAX) thrM("⊔: Too large"); for (usz i = 0; i < xia; i++) { i64 c = o2i64(GetU(x,i)); if (c>=0) rp[tmp[c]++] = i; } dec(w); dec(x); TFREE(tmp); return r; } B asrt_c1(B t, B x) { if (LIKELY(isF64(x) && o2fu(x)==1)) return x; if (isF64(x)) thrM("Assertion error"); thr(x); } B asrt_c2(B t, B w, B x) { if (LIKELY(isF64(x) && o2fu(x)==1)) { dec(w); return x; } dec(x); thr(w); } B casrt_c2(B t, B w, B x) { if (LIKELY(isF64(x) && o2fu(x)==1)) { dec(w); return x; } unwindCompiler(); dec(x); if (isArr(w) && a(w)->ia==2) { B w0 = IGetU(w,0); if (isNum(w0)) { B s = IGet(w,1); AFMT("\n"); usz pos = o2s(w0); s = vm_fmtPoint(comp_currSrc, s, comp_currPath, pos, pos+1); dec(w); thr(s); } if (isArr(w0) && a(w0)->ia==2) { B s = IGet(w,1); SGetU(w0) AFMT("\n"); s = vm_fmtPoint(comp_currSrc, s, comp_currPath, o2s(GetU(w0,0)), o2s(GetU(w0,1))+1); dec(w); thr(s); } } thr(w); } B casrt_c1(B t, B x) { if (LIKELY(isF64(x) && o2fu(x)==1)) return x; casrt_c2(t, inc(x), x); UD; } B sys_c1(B t, B x); B out_c1(B t, B x) { if (isArr(x) && rnk(x)>1) thrF("•Out: Argument cannot have rank %i", rnk(x)); printRaw(x); putchar('\n'); return x; } B show_c1(B t, B x) { #if FORMATTER B fmt = bqn_fmt(inc(x)); printRaw(fmt); dec(fmt); #else print(x); #endif putchar('\n'); return x; } B bqn_c1(B t, B x) { if (isAtm(x) || rnk(x)!=1) thrM("•BQN: Argument must be a character vector"); if (a(x)->type!=t_c32arr && a(x)->type!=t_c32slice) { usz ia = a(x)->ia; SGetU(x) for (usz i = 0; i < ia; i++) if (!isC32(GetU(x,i))) thrM("•BQN: Argument must be a character vector"); } return bqn_exec(x, bi_N, bi_N); } B cmp_c2(B t, B w, B x) { B r = m_i32(compare(w, x)); dec(w); dec(x); return r; } B hash_c2(B t, B w, B x) { u64 secret[4]; make_secret(o2i64(w), secret); u64 rv = bqn_hash(x, secret); dec(x); i32* rp; B r = m_i32arrv(&rp, 4); rp[0] = (u16)(rv>>48); rp[1] = (u16)(rv>>32); rp[2] = (u16)(rv>>16); rp[3] = (u16)(rv ); return r; } B hash_c1(B t, B x) { u64 rv = bqn_hash(x, wy_secret); dec(x); i32* rp; B r = m_i32arrv(&rp, 4); rp[0] = (u16)(rv>>48); rp[1] = (u16)(rv>>32); rp[2] = (u16)(rv>>16); rp[3] = (u16)(rv ); return r; } static B rand_ns; static i32 rand_a, rand_b; static B rand_rangeName; static NFnDesc* rand_rangeDesc; static B rand_dealName; static NFnDesc* rand_dealDesc; static B rand_subsetName; static NFnDesc* rand_subsetDesc; #define RAND_START Scope* sc = c(NS,nfn_objU(t))->sc; \ u64 seed = sc->vars[rand_a].u | sc->vars[rand_b].u<<32; #define RAND_END sc->vars[rand_a].u = seed>>32; \ sc->vars[rand_a].u = seed&0xFFFFFFFF; B rand_range_c1(B t, B x) { i64 xv = o2i64(x); if (xv<0) thrM("(rand).Range: 𝕩 cannot be negative"); RAND_START; u64 rnd = wyrand(&seed); RAND_END; return xv? m_f64(wy2u0k(rnd, xv)) : m_f64(wy2u01(rnd)); } B rand_range_c2(B t, B w, B x) { usz am = o2s(w); i64 max = o2i64(x); RAND_START; B r; if (max<1) { if (max!=0) thrM("(rand).Range: 𝕩 cannot be negative"); f64* rp; r = m_f64arrv(&rp, am); for (usz i = 0; i < am; i++) rp[i] = wy2u01(wyrand(&seed)); } else if (max > I32_MAX) { if (max >= 1LL<<53) thrM("(rand).Range: 𝕩 must be less than 2⋆53"); f64* rp; r = m_f64arrv(&rp, am); for (usz i = 0; i < am; i++) rp[i] = wy2u0k(wyrand(&seed), max); } else { i32* rp; r = m_i32arrv(&rp, am); for (usz i = 0; i < am; i++) rp[i] = wy2u0k(wyrand(&seed), max); } RAND_END; return r; } B rand_deal_c1(B t, B x) { i32 xi = o2i(x); if (RARE(xi<0)) thrM("(rand).Deal: Argument cannot be negative"); if (xi==0) return emptyIVec(); RAND_START; i32* rp; B r = m_i32arrv(&rp, xi); for (i64 i = 0; i < xi; i++) rp[i] = i; for (i64 i = 0; i < xi; i++) { i32 j = wy2u0k(wyrand(&seed), xi-i) + i; i32 c = rp[j]; rp[j] = rp[i]; rp[i] = c; } RAND_END; return r; } B rand_deal_c2(B t, B w, B x) { i32 wi = o2i(w); i32 xi = o2i(x); if (RARE(wi<0)) thrM("(rand).Deal: 𝕨 cannot be negative"); if (RARE(xi<0)) thrM("(rand).Deal: 𝕩 cannot be negative"); if (RARE(wi>xi)) thrM("(rand).Deal: 𝕨 cannot exceed 𝕩"); if (wi==0) return emptyIVec(); B r; RAND_START; if (wi > xi/64) { // Dense shuffle TALLOC(i32,s,xi); for (i64 i = 0; i < xi; i++) s[i] = i; for (i64 i = 0; i < wi; i++) { i32 j = wy2u0k(wyrand(&seed), xi-i) + i; i32 c = s[j]; s[j] = s[i]; s[i] = c; } i32* rp; r = m_i32arrv(&rp, wi); memcpy(rp, s, wi*4); TFREE(s); } else { // Hash-based shuffle i32* rp; r = m_i32arrv(&rp, wi); i64 sz = 1; while (sz < wi*2) sz*= 2; TALLOC(i32, hash, 2*sz); i32* val = hash+1; for (i64 i = 0; i < 2*sz; i++) hash[i] = 0; for (i64 i = 0; i < wi; i++) rp[i] = i; u64 mask = 2*(sz-1); for (i64 i = 0; i < wi; i++) { u64 j = wy2u0k(wyrand(&seed), xi-i) + i; if (j<(u64)wi) { i32 c = rp[j]; rp[j] = rp[i]; rp[i] = c; } else { u64 p = 2*j; i32 prev = j; while (true) { p&= mask; i32 h = hash[p]; if (h==0) { hash[p] = j; break; } if ((u64)h==j) { prev = val[p]; break; } p+= 2; } val[p] = rp[i]; rp[i] = prev; } } TFREE(hash); } RAND_END; return r; } B rand_subset_c2(B t, B w, B x) { i32 wi = o2i(w); i32 xi = o2i(x); if (RARE(wi<0)) thrM("(rand).Subset: 𝕨 cannot be negative"); if (RARE(xi<0)) thrM("(rand).Subset: 𝕩 cannot be negative"); if (RARE(wi>xi)) thrM("(rand).Subset: 𝕨 cannot exceed 𝕩"); if (wi==0) return emptyIVec(); B r; if (wi==xi) { // Only one complete subset; will hang without this i32* rp; r = m_i32arrv(&rp, wi); for (i64 i = 0; i < wi; i++) rp[i] = i; return r; } RAND_START; if (wi > xi/8) { // Bit set (as bytes) TALLOC(u8, set, xi); bool invert = wi > xi/2; i32 wn = invert ? xi-wi : wi; for (i64 i = 0; i < xi; i++) set[i] = 0; for (i32 i = xi-wn; i < xi; i++) { i32 j = wy2u0k(wyrand(&seed), i+1); if (set[j]) j=i; set[j] = 1; } i32* rp; r = m_i32arrv(&rp, wi); if (!invert) { for (i64 i = 0; i < xi; i++) if ( set[i]) *rp++=i; } else { for (i64 i = 0; i < xi; i++) if (!set[i]) *rp++=i; } TFREE(set); } else { // Sorted "hash" set u64 sh = 0; for (u64 xt=xi/4; xt>=(u64)wi; xt>>=1) sh++; u64 sz = ((xi-1)>>sh)+1 + wi; TALLOC(i32, hash, sz); for (u64 i = 0; i < sz; i++) hash[i] = xi; for (i32 i = xi-wi; i < xi; i++) { i32 j = wy2u0k(wyrand(&seed), i+1); u64 p = (u64)j >> sh; while (true) { i32 h = hash[p]; if (LIKELY(j>sh; continue; } p++; } } i32* rp; r = m_i32arrv(&rp, wi); for (u64 i = 0; i < sz; i++) if (hash[i]!=xi) *rp++=hash[i]; TFREE(hash); } RAND_END; return r; } static NOINLINE void rand_init() { rand_ns = bqn_exec(m_str32(U"{a←𝕨⋄b←𝕩⋄range⇐0⋄deal⇐0⋄subset⇐0}"), emptyCVec(), emptySVec()); gc_add(rand_ns); rand_rangeName = m_str8l("range"); gc_add(rand_rangeName); rand_rangeDesc = registerNFn(m_str8l("(rand).Range"), rand_range_c1, rand_range_c2); rand_dealName = m_str8l("deal"); gc_add(rand_dealName); rand_dealDesc = registerNFn(m_str8l("(rand).Deal"), rand_deal_c1, rand_deal_c2); rand_subsetName = m_str8l("subset"); gc_add(rand_subsetName); rand_subsetDesc = registerNFn(m_str8l("(rand).Subset"), c1_bad, rand_subset_c2); B tmp = c2(rand_ns, m_f64(0), m_f64(0)); rand_a = ns_pos(tmp, m_str8l("a")); rand_b = ns_pos(tmp, m_str8l("b")); dec(tmp); } B makeRand_c1(B t, B x) { if (!isNum(x)) thrM("•MakeRand: 𝕩 must be a number"); if (rand_ns.u==0) rand_init(); B r = c2(rand_ns, b(x.u>>32), b(x.u&0xFFFFFFFF)); ns_set(r, rand_rangeName, m_nfn(rand_rangeDesc, incG(r))); ns_set(r, rand_dealName, m_nfn(rand_dealDesc, incG(r))); ns_set(r, rand_subsetName, m_nfn(rand_subsetDesc, incG(r))); return r; } static B randNS; B getRandNS() { if (randNS.u == 0) { #if RANDSEED==0 randNS = c1(bi_makeRand, m_f64(nsTime())); #else randNS = c1(bi_makeRand, m_f64(RANDSEED)); #endif gc_add(randNS); } return incG(randNS); } extern B replPath; // defined in main.c static NFnDesc* reBQNDesc; B reBQN_c1(B t, B x) { if (!isNsp(x)) thrM("•ReBQN: Argument must be a namespace"); B replStr = m_str8l("repl"); B repl = ns_getNU(x, replStr, false); dec(replStr); i32 replVal = q_N(repl) || eqStr(repl,U"none")? 0 : eqStr(repl,U"strict")? 1 : eqStr(repl,U"loose")? 2 : 3; if (replVal==3) thrM("•ReBQN: Invalid repl value"); dec(x); Block* initBlock = bqn_comp(m_str8l("\"(REPL initializer)\""), inc(replPath), m_f64(0)); B scVal; if (replVal==0) { scVal = bi_N; } else { Scope* sc = m_scope(initBlock->bodies[0], NULL, 0, 0, NULL); scVal = tag(sc,OBJ_TAG); } ptr_dec(initBlock); return m_nfn(reBQNDesc, m_v2(m_f64(replVal), scVal)); } B repl_c2(B t, B w, B x) { B o = nfn_objU(t); B* op = harr_ptr(o); i32 replMode = o2iu(op[0]); Scope* sc = c(Scope, op[1]); if (!isArr(w) || rnk(w)!=1 || a(w)->ia>3) thrM("REPL: 𝕨 must be a vector with at most 3 items"); usz ia = a(w)->ia; SGet(w) B path = ia>0? Get(w,0) : inc(replPath); B file = ia>1? Get(w,1) : emptyCVec(); B args = ia>2? Get(w,2) : emptySVec(); B fullpath = vec_join(vec_add(path, m_c32('/')), file); dec(w); B res; if (replMode>0) { Block* block = bqn_compSc(x, fullpath, args, sc, replMode==2); ptr_dec(sc->body); ptr_inc(block->bodies[0]); sc->body = block->bodies[0]; res = execBlockInline(block, sc); ptr_dec(block); } else { res = bqn_exec(x, fullpath, args); } return res; } B repl_c1(B t, B x) { return repl_c2(t, emptyHVec(), x); } static NFnDesc* fileAtDesc; B fileAt_c1(B d, B x) { return path_resolve(nfn_objU(d), x); } static NFnDesc* fCharsDesc; B fchars_c1(B d, B x) { return file_chars(path_resolve(nfn_objU(d), x)); } B fchars_c2(B d, B w, B x) { if (!isArr(x)) thrM("•FChars: Non-array 𝕩"); B p = path_resolve(nfn_objU(d), w); file_wChars(inc(p), x); dec(x); return p; } static NFnDesc* fBytesDesc; B fbytes_c1(B d, B x) { I8Arr* tf = file_bytes(path_resolve(nfn_objU(d), x)); usz ia = tf->ia; u8* p = (u8*)tf->a; u8* rp; B r = m_c8arrv(&rp, ia); for (i64 i = 0; i < ia; i++) rp[i] = p[i]; ptr_dec(tf); return r; } B fbytes_c2(B d, B w, B x) { if (!isArr(x)) thrM("•FBytes: Non-array 𝕩"); B p = path_resolve(nfn_objU(d), w); file_wBytes(inc(p), x); dec(x); return p; } static NFnDesc* fLinesDesc; B flines_c1(B d, B x) { return file_lines(path_resolve(nfn_objU(d), x)); } B flines_c2(B d, B w, B x) { if (!isArr(x)) thrM("•FLines: Non-array 𝕩"); B nl, s = emptyCVec(); usz ia = a(x)->ia; SGet(x) for (u64 i = 0; i < ia; i++) { nl = Get(x, i); if (!isArr(nl)) thrM("•FLines: Non-array element of 𝕩"); s = vec_join(s, nl); //if (windows) s = vec_add(s, m_c32('\r')); TODO figure out whether or not this is a thing that should be done s = vec_add(s, m_c32('\n')); } dec(x); B p = path_resolve(nfn_objU(d), w); file_wChars(inc(p), s); dec(s); return p; } static NFnDesc* importDesc; B import_c1(B d, B x) { return bqn_execFile(path_resolve(nfn_objU(d), x), emptySVec()); } B import_c2(B d, B w, B x) { return bqn_execFile(path_resolve(nfn_objU(d), x), w); } static NFnDesc* listDesc; B list_c1(B d, B x) { return file_list(path_resolve(nfn_objU(d), x)); } B unixTime_c1(B t, B x) { dec(x); return m_f64(time(NULL)); } B monoTime_c1(B t, B x) { dec(x); struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return m_f64(ts.tv_sec + ts.tv_nsec*1e-9); } B delay_c1(B t, B x) { f64 sf = o2f(x); if (sf<0 || sf>1ULL<<63) thrF("•Delay: Bad argument: %f", sf); struct timespec ts,ts0; u64 s = (u64)sf; ts.tv_sec = (u64)sf; ts.tv_nsec = (u64)((sf-s)*1e9); clock_gettime(CLOCK_MONOTONIC, &ts0); nanosleep(&ts, &ts); clock_gettime(CLOCK_MONOTONIC, &ts); return m_f64(ts.tv_sec-ts0.tv_sec+(ts.tv_nsec-ts0.tv_nsec)*1e-9); } B exit_c1(B t, B x) { bqn_exit(q_i32(x)? o2i(x) : 0); } B getLine_c1(B t, B x) { dec(x); char* ln = NULL; size_t gl = 0; i64 read = getline(&ln, &gl, stdin); if (read<=0 || ln[0]==0) { if (ln) free(ln); return m_c32(0); } B r = fromUTF8(ln, strlen(ln)-1); free(ln); return r; } B fromUtf8_c1(B t, B x) { if (!isArr(x)) thrM("•FromUTF8: Argument must be a character or number array"); usz ia = a(x)->ia; TALLOC(char, chrs, ia); SGetU(x) for (u64 i = 0; i < ia; i++) { B c = GetU(x,i); if (isC32(c)) { u32 v = o2cu(c); if (v>=256) thrF("•FromUTF8: Argument contained a character with codepoint %i", v); chrs[i] = v; } else { i32 v = o2i(c); if (v<=-128 | v>=256) thrF("•FromUTF8: Argument contained %i", v); chrs[i] = v&0xff; } } B r = fromUTF8(chrs, ia); dec(x); TFREE(chrs); return r; } extern char** environ; #if __has_include() #include B sh_c1(B t, B x) { if (isAtm(x) || rnk(x)>1) thrM("•SH: 𝕩 must be a vector of strings"); usz xia = a(x)->ia; if (xia==0) thrM("•SH: 𝕩 must have at least one item"); TALLOC(char*, argv, xia+1); SGetU(x) for (u64 i = 0; i < xia; i++) { B c = GetU(x, i); if (isAtm(c) || rnk(c)!=1) thrM("•SH: 𝕩 must be a vector of strings"); u64 len = utf8lenB(c); TALLOC(char, cstr, len+1); toUTF8(c, cstr); cstr[len] = 0; argv[i] = cstr; } argv[xia] = NULL; int p_out[2]; int p_err[2]; if (pipe(p_out) || pipe(p_err)) thrM("•SH: Failed to create process: Couldn't create pipes"); posix_spawn_file_actions_t a; posix_spawn_file_actions_init(&a); posix_spawn_file_actions_addclose(&a, p_out[0]); posix_spawn_file_actions_adddup2(&a, p_out[1], 1); posix_spawn_file_actions_addclose(&a, p_out[1]); posix_spawn_file_actions_addclose(&a, p_err[0]); posix_spawn_file_actions_adddup2(&a, p_err[1], 2); posix_spawn_file_actions_addclose(&a, p_err[1]); pid_t pid; if(posix_spawnp(&pid, argv[0], &a, NULL, argv, environ) != 0) thrF("•SH: Failed to create process: %S", strerror(errno)); for (u64 i = 0; i < xia; i++) TFREE(argv[i]); TFREE(argv); close(p_out[1]); B s_out = emptyCVec(); close(p_err[1]); B s_err = emptyCVec(); const u64 bufsz = 1024; TALLOC(char, buf, bufsz); struct pollfd ps[] = {{.fd=p_out[0], .events=POLLIN}, {.fd=p_err[0], .events=POLLIN}}; while (poll(&ps[0], 2, -1) > 0) { if (ps[0].revents & POLLIN) s_out = vec_join(s_out, fromUTF8(buf, read(p_out[0], &buf[0], bufsz))); else if (ps[1].revents & POLLIN) s_err = vec_join(s_err, fromUTF8(buf, read(p_err[0], &buf[0], bufsz))); else break; } TFREE(buf); int status; waitpid(pid, &status, 0); posix_spawn_file_actions_destroy(&a); dec(x); return m_v3(m_i32(WEXITSTATUS(status)), s_out, s_err); } #else B sh_c1(B t, B x) { thrM("•SH: CBQN was built without "); } #endif B getInternalNS(void); B getMathNS(void); static B file_nsGen; B sys_c1(B t, B x) { assert(isArr(x)); usz i = 0; HArr_p r = m_harrs(a(x)->ia, &i); SGetU(x) B fileNS = m_f64(0); B path = m_f64(0); #define REQ_PATH ({ if(!path.u) path = path_abs(path_dir(inc(comp_currPath))); path; }) for (; i < a(x)->ia; i++) { B c = GetU(x,i); if (eqStr(c, U"out")) r.a[i] = incG(bi_out); else if (eqStr(c, U"show")) r.a[i] = incG(bi_show); else if (eqStr(c, U"exit")) r.a[i] = incG(bi_exit); else if (eqStr(c, U"getline")) r.a[i] = incG(bi_getLine); else if (eqStr(c, U"file")) { if(!fileNS.u) { REQ_PATH; #define F(X) m_nfn(X##Desc, inc(path)) B arg = m_caB(6, (B[]){q_N(path)? m_c32(0) : inc(path), F(fileAt), F(list), F(fBytes), F(fChars), F(fLines)}); #undef F fileNS = c1(file_nsGen,arg); } r.a[i] = inc(fileNS); } else if (eqStr(c, U"internal")) r.a[i] = getInternalNS(); else if (eqStr(c, U"math")) r.a[i] = getMathNS(); else if (eqStr(c, U"type")) r.a[i] = incG(bi_type); else if (eqStr(c, U"sh")) r.a[i] = incG(bi_sh); else if (eqStr(c, U"decompose")) r.a[i] = incG(bi_decp); else if (eqStr(c, U"primind")) r.a[i] = incG(bi_primInd); else if (eqStr(c, U"bqn")) r.a[i] = incG(bi_bqn); else if (eqStr(c, U"cmp")) r.a[i] = incG(bi_cmp); else if (eqStr(c, U"unixtime")) r.a[i] = incG(bi_unixTime); else if (eqStr(c, U"monotime")) r.a[i] = incG(bi_monoTime); else if (eqStr(c, U"timed")) r.a[i] = incG(bi_timed); else if (eqStr(c, U"delay")) r.a[i] = incG(bi_delay); else if (eqStr(c, U"hash")) r.a[i] = incG(bi_hash); else if (eqStr(c, U"repr")) r.a[i] = incG(bi_repr); else if (eqStr(c, U"fmt")) r.a[i] = incG(bi_fmt); else if (eqStr(c, U"glyph")) r.a[i] = incG(bi_glyph); else if (eqStr(c, U"makerand")) r.a[i] = incG(bi_makeRand); else if (eqStr(c, U"rand")) r.a[i] = getRandNS(); else if (eqStr(c, U"rebqn")) r.a[i] = incG(bi_reBQN); else if (eqStr(c, U"fromutf8")) r.a[i] = incG(bi_fromUtf8); else if (eqStr(c, U"path")) r.a[i] = inc(REQ_PATH); else if (eqStr(c, U"fchars")) r.a[i] = m_nfn(fCharsDesc, inc(REQ_PATH)); else if (eqStr(c, U"fbytes")) r.a[i] = m_nfn(fBytesDesc, inc(REQ_PATH)); else if (eqStr(c, U"flines")) r.a[i] = m_nfn(fLinesDesc, inc(REQ_PATH)); else if (eqStr(c, U"import")) r.a[i] = m_nfn(importDesc, inc(REQ_PATH)); else if (eqStr(c, U"args")) { if(q_N(comp_currArgs)) thrM("No arguments present for •args"); r.a[i] = inc(comp_currArgs); } else { dec(x); thrF("Unknown system function •%R", c); } } #undef REQ_PATH dec(fileNS); dec(path); return harr_fcd(r, x); } void sysfn_init() { fCharsDesc = registerNFn(m_str8l("(file).Chars"), fchars_c1, fchars_c2); fileAtDesc = registerNFn(m_str8l("(file).At"), fileAt_c1, c2_bad); fLinesDesc = registerNFn(m_str8l("(file).Lines"), flines_c1, flines_c2); fBytesDesc = registerNFn(m_str8l("(file).Bytes"), fbytes_c1, fbytes_c2); importDesc = registerNFn(m_str32(U"•Import"), import_c1, import_c2); reBQNDesc = registerNFn(m_str8l("(REPL)"), repl_c1, repl_c2); listDesc = registerNFn(m_str32(U"•file.List"), list_c1, c2_bad); } void sysfnPost_init() { file_nsGen = bqn_exec(m_str32(U"{⟨path,At,List,Bytes,Chars,Lines⟩⇐𝕩}"), emptyCVec(), emptySVec()); gc_add(file_nsGen); }