#include "core.h" #include "vm.h" #include "ns.h" #include "utils/utf.h" #include "utils/talloc.h" #include "utils/file.h" #include "utils/time.h" #include "utils/interrupt.h" static B replPath; static Scope* gsc; static bool init = false; static void repl_init() { if (init) return; cbqn_init(); replPath = m_c8vec_0("."); gc_add(replPath); Body* body = m_nnsDesc(); B ns = m_nns(body); gsc = ptr_inc(c(NS, ns)->sc); gc_add(tag(gsc,OBJ_TAG)); ptr_dec(v(ns)); init = true; } static B gsc_exec_inline(B src, B path, B args) { Block* block = bqn_compSc(src, path, args, gsc, true); ptr_dec(gsc->body); // redirect new errors to the newly executed code; initial scope had 0 vars, so this is safe gsc->body = ptr_inc(block->bodies[0]); B r = execBlockInline(block, gsc); ptr_dec(block); return r; } static bool isCmd(char* s, char** e, const char* cmd) { while (*cmd) { if (*cmd==' ' && *s==0) { *e = s; return true; } if (*s!=*cmd || *s==0) return false; s++; cmd++; } *e = s; return true; } bool profiler_alloc(void); bool profiler_start(i64 hz); bool profiler_stop(void); void profiler_free(void); void profiler_displayResults(void); void clearImportCache(void); static B escape_parser; B simple_unescape(B x) { if (RARE(escape_parser.u==0)) { escape_parser = bqn_exec(utf8Decode0("{m←\"Expected surrounding quotes\" ⋄ m!2≤≠𝕩 ⋄ m!\"\"\"\"\"\"≡0‿¯1⊏𝕩 ⋄ s←¬e←<`'\\'=𝕩 ⋄ i‿o←\"\\\"\"nr\"⋈\"\\\"\"\"∾@+10‿13 ⋄ 1↓¯1↓{n←i⊐𝕩 ⋄ \"Unknown escape\"!∧´n≠≠i ⋄ n⊏o}⌾((s/»e)⊸/) s/𝕩}"), bi_N, bi_N); gc_add(escape_parser); } return c1(escape_parser, x); } i64 readInt(char** p) { char* c = *p; i64 am = 0; while (*c>='0' & *c<='9') { am = am*10 + (*c - '0'); c++; } *p = c; return am; } void cbqn_runLine0(char* ln, i64 read) { if (ln[0]==0 || read==0) return; B code; int output; // 0-no; 1-formatter; 2-internal i32 time = 0; i64 profile = -1; if (ln[0] == ')') { char* cmdS = ln+1; char* cmdE; if (isCmd(cmdS, &cmdE, "ex ")) { B path = utf8Decode0(cmdE); code = path_chars(path); output = 0; } else if (isCmd(cmdS, &cmdE, "r ")) { code = utf8Decode0(cmdE); output = 0; } else if (isCmd(cmdS, &cmdE, "escaped ")) { B u = simple_unescape(utf8Decode0(cmdE)); u64 len = utf8lenB(u); TALLOC(char, ascii, len+1); toUTF8(u, ascii); dec(u); ascii[len] = '\0'; cbqn_runLine0(ascii, len+1); TFREE(ascii); return; } else if (isCmd(cmdS, &cmdE, "t ") || isCmd(cmdS, &cmdE, "time ")) { code = utf8Decode0(cmdE); time = -1; output = 0; } else if (isCmd(cmdS, &cmdE, "profile ") || isCmd(cmdS, &cmdE, "profile@")) { char* cpos = cmdE; profile = '@'==*(cpos-1)? readInt(&cpos) : 5000; if (profile==0) { printf("Cannot profile with 0hz sampling frequency\n"); return; } if (profile>999999) { printf("Cannot profile with >999999hz frequency\n"); return; } code = utf8Decode0(cpos); output = 0; } else if (isCmd(cmdS, &cmdE, "t:") || isCmd(cmdS, &cmdE, "time:")) { char* repE = cmdE; i64 am = readInt(&repE); if (repE==cmdE) { printf("time command not given repetition count\n"); return; } if (am==0) { printf("repetition count was zero\n"); return; } code = utf8Decode0(repE); time = am; output = 0; } else if (isCmd(cmdS, &cmdE, "mem ")) { bool sizes = 0; bool types = 0; bool freed = 0; char c; while ((c=*(cmdE++)) != 0) { if (c=='t') types=1; if (c=='s') sizes=1; if (c=='f') freed=1; } heap_printInfo(sizes, types, freed); return; } else if (isCmd(cmdS, &cmdE, "erase ")) { char* name = cmdE; i64 len = strlen(name); ScopeExt* e = gsc->ext; if (e!=NULL) { i32 am = e->varAm; for (i32 i = 0; i < am; i++) { B c = e->vars[i+am]; if (IA(c) != len) continue; SGetU(c) bool ok = true; for (i32 j = 0; j < len; j++) ok&= o2cG(GetU(c, j))==name[j]; if (ok) { B val = e->vars[i]; e->vars[i] = bi_noVar; dec(val); #if ENABLE_GC if (!gc_depth) gc_forceGC(); #endif return; } } } printf("No such variable found\n"); return; } else if (isCmd(cmdS, &cmdE, "clearImportCache ")) { clearImportCache(); return; } else if (isCmd(cmdS, &cmdE, "vars")) { B r = listVars(gsc); if (q_N(r)) { printf("Couldn't list variables\n"); } else { usz ia = IA(r); B* rp = harr_ptr(r); for (usz i = 0; i < ia; i++) { if (i!=0) printf(", "); printRaw(rp[i]); } putchar('\n'); } return; } else if (isCmd(cmdS, &cmdE, "gc ")) { #if ENABLE_GC if (0==*cmdE) { if (gc_depth!=0) { printf("GC is disabled, but forcibly GCing anyway\n"); gc_enable(); gc_forceGC(); gc_disable(); } else { gc_forceGC(); } } else if (strcmp(cmdE,"on")==0) { if (gc_depth==0) printf("GC already on\n"); else if (gc_depth>1) printf("GC cannot be enabled\n"); else { gc_enable(); printf("GC enabled\n"); } } else if (strcmp(cmdE,"off")==0) { if (gc_depth>0) printf("GC already off\n"); else { gc_disable(); printf("GC disabled\n"); } } else printf("Unknown GC command\n"); #else printf("Macro ENABLE_GC was false at compile-time, cannot GC\n"); #endif return; } else if (isCmd(cmdS, &cmdE, "internalPrint ")) { code = utf8Decode0(cmdE); output = 2; } else if (isCmd(cmdS, &cmdE, "e ") || isCmd(cmdS, &cmdE, "explain ")) { B expl = bqn_explain(utf8Decode0(cmdE), replPath); HArr* expla = toHArr(expl); usz ia=PIA(expla); for(usz i=0; ia[i]); putchar('\n'); } dec(expl); return; } else { printf("Unknown REPL command\n"); return; } } else { code = utf8Decode0(ln); output = 1; } Block* block = bqn_compSc(code, incG(replPath), emptySVec(), gsc, true); ptr_dec(gsc->body); gsc->body = ptr_inc(block->bodies[0]); B res; if (time) { f64 t; if (time==-1) { u64 sns = nsTime(); res = execBlockInline(block, gsc); u64 ens = nsTime(); t = ens-sns; } else { u64 sns = nsTime(); for (i64 i = 0; i < time; i++) dec(execBlockInline(block, gsc)); u64 ens = nsTime(); t = (ens-sns)*1.0 / time; res = m_c32(0); } if (t<1e3) printf("%.5gns\n", t); else if (t<1e6) printf("%.4gus\n", t/1e3); else if (t<1e9) printf("%.4gms\n", t/1e6); else printf("%.5gs\n", t/1e9); } else if (profile>0) { if (CATCH) { profiler_stop(); profiler_free(); rethrow(); } if (profiler_alloc() && profiler_start(profile)) { res = execBlockInline(block, gsc); profiler_stop(); profiler_displayResults(); profiler_free(); } popCatch(); } else res = execBlockInline(block, gsc); ptr_dec(block); if (output) { if (output!=2 && FORMATTER) { B resFmt = bqn_fmt(res); printRaw(resFmt); dec(resFmt); putchar('\n'); } else { print(res); putchar('\n'); fflush(stdout); dec(res); } } else dec(res); } void cbqn_runLine(char* ln, i64 len) { if(CATCH) { cbqn_takeInterrupts(false); fprintf(stderr, "Error: "); printErrMsg(thrownMsg); fputc('\n', stderr); vm_pst(envCurr+1, envStart+envPrevHeight); freeThrown(); #ifdef HEAP_VERIFY heapVerify(); #endif gc_maybeGC(); return; } cbqn_takeInterrupts(true); cbqn_runLine0(ln, len); #ifdef HEAP_VERIFY heapVerify(); #endif gc_maybeGC(); cbqn_takeInterrupts(false); popCatch(); } #if WASM void cbqn_evalSrc(char* src, i64 len) { B code = utf8Decode(src, len); B res = bqn_exec(code, bi_N, bi_N); B resFmt = bqn_fmt(res); printRaw(resFmt); dec(resFmt); putchar('\n'); } #endif #if EMCC int main() { repl_init(); } #elif !CBQN_SHARED int main(int argc, char* argv[]) { repl_init(); bool startREPL = argc==1; bool silentREPL = false; bool execStdin = false; if (!startREPL) { i32 i = 1; while (i!=argc) { char* carg = argv[i]; if (carg[0]!='-') break; if (carg[1]==0) { execStdin=true; i++; break; } i++; if (carg[1]=='-') { if (!strcmp(carg, "--help")) { printf( "Usage: %s [options] [file.bqn [arguments]]\n" "Options:\n" "-f file: execute the contents of the file with all further arguments as •args\n" "-e code: execute the argument as BQN\n" "-p code: execute the argument as BQN and print its result pretty-printed\n" "-o code: execute the argument as BQN and print its raw result\n" "-M num : set maximum heap size to num megabytes\n" "-r : start the REPL after executing all arguments\n" "-s : start a silent REPL\n" , argv[0]); exit(0); } else { printf("%s: Unknown option: %s\n", argv[0], carg); exit(1); } } else { carg++; char c; while ((c = *carg++)) { switch(c) { default: fprintf(stderr, "%s: Unknown option: -%c\n", argv[0], c); exit(1); #define REQARG(X) if(*carg) { fprintf(stderr, "%s: -%s must end the option\n", argv[0], #X); exit(1); } if (i==argc) { fprintf(stderr, "%s: -%s requires an argument\n", argv[0], #X); exit(1); } case 'f': repl_init(); REQARG(f); goto execFile; case 'e': { repl_init(); REQARG(e); dec(gsc_exec_inline(utf8Decode0(argv[i++]), m_c8vec_0("(-e)"), emptySVec())); break; } case 'L': { repl_init(); break; } // just initialize. mostly for perf testing case 'p': { repl_init(); REQARG(p); B r = bqn_fmt(gsc_exec_inline(utf8Decode0(argv[i++]), m_c8vec_0("(-p)"), emptySVec())); printRaw(r); dec(r); printf("\n"); break; } case 'o': { repl_init(); REQARG(o); B r = gsc_exec_inline(utf8Decode0(argv[i++]), m_c8vec_0("(-o)"), emptySVec()); printRaw(r); dec(r); printf("\n"); break; } case 'M': { REQARG(M); char* str = argv[i++]; u64 am = 0; while (*str) { char c = *str++; if (c<'0' | c>'9') { printf("%s: -M: Argument not a number\n", argv[0]); exit(1); } if (am>1ULL<<48) { printf("%s: -M: Too large\n", argv[0]); exit(1); } am = am*10 + c-48; } mm_heapMax = am*1024*1024; break; } #ifdef PERF_TEST case 'R': { repl_init(); REQARG(R); B path = utf8Decode0(argv[i++]); B lines = path_lines(path); usz ia = IA(lines); SGet(lines) for (u64 i = 0; i < ia; i++) { dec(gsc_exec_inline(Get(lines, i), incG(replPath), emptySVec())); } break; } #endif case 'r': { startREPL=true; break; } case 's': { startREPL=true; silentREPL=true; break; } } } } } execFile: if (i!=argc || execStdin) { repl_init(); B src; if (!execStdin) src = utf8Decode0(argv[i++]); B args; if (i==argc) { args = emptySVec(); } else { M_HARR(ap, argc-i) for (usz j = 0; j < argc-i; j++) HARR_ADD(ap, j, utf8Decode0(argv[i+j])); args = HARR_FV(ap); } B execRes; if (execStdin) { execRes = gsc_exec_inline(utf8DecodeA(stream_bytes(stdin)), m_c8vec_0("(-)"), args); } else { execRes = bqn_execFile(src, args); } dec(execRes); #ifdef HEAP_VERIFY heapVerify(); #endif gc_forceGC(); } } if (startREPL) { repl_init(); while (true) { if (!silentREPL) { printf(" "); fflush(stdout); } char* ln = NULL; size_t gl = 0; i64 read = getline(&ln, &gl, stdin); if (read<=0 || ln[0]==0) { if(!silentREPL) putchar('\n'); break; } if (ln[read-1]==10) ln[--read] = 0; cbqn_runLine(ln, read); free(ln); } } #ifdef HEAP_VERIFY heapVerify(); #endif bqn_exit(0); #undef INIT } #endif