#include #include #include #include #include #include #include #include i64 pit_lex_file(pit_lexer *ret, char *path) { FILE *f = fopen(path, "r"); if (f == NULL) { return -1; } fseek(f, 0, SEEK_END); i64 len = ftell(f); fseek(f, 0, SEEK_SET); char *buf = calloc((size_t) len, sizeof(char)); if ((size_t) len != fread(buf, sizeof(char), (size_t) len, f)) { free(buf); fclose(f); return -1; } fclose(f); pit_lex_bytes(ret, buf, len); return 0; } static pit_dump_condition pit_dump_to_file_callback(u8 *buf, i64 len, void *data) { if (fwrite(buf, sizeof(u8), (size_t) len, (FILE *) data) != (size_t) len) return PIT_DUMP_CONDITION_FULL; return PIT_DUMP_CONDITION_NORMAL; } void pit_dump_to_file(pit_runtime *rt, void *vf, pit_value v, bool readable) { pit_dump_with_callback(rt, v, readable, pit_dump_to_file_callback, vf); } i64 pit_fprintf(pit_runtime *rt, void *vf, char *format, ...) { FILE *f = (FILE *) vf; va_list ap; va_start(ap, format); char buf[8192]; pit_printf_handler h[2]; h[0].specifier = 'v'; h[0].callback = pit_dump_printf_callback; h[1].specifier = 'V'; h[1].callback = pit_dump_readable_printf_callback; int ret = pit_vsnprintf_helper(buf, sizeof(buf) - 1, format, ap, h, 2, rt); buf[ret] = 0; va_end(ap); fputs(buf, f); return ret; } bool pit_runtime_print_error(pit_runtime *rt) { if (!pit_value_eq(rt->error, PIT_NIL)) { for (i64 i = 0; i < rt->callstack->next; ++i) { pit_callstack_entry *a = pit_vec_get(pit_callstack_entry)(rt->callstack, i); if (a == NULL) continue; if (a->tag == PIT_NIL) continue; fprintf(stderr, "on line %ld, column %ld (call to ", a->ann.line, a->ann.column); pit_dump_to_file(rt, stderr, a->tag, false); fprintf(stderr, "):\n"); } fprintf(stderr, "error at line %ld, column %ld: ", rt->error_line, rt->error_column); pit_dump_to_file(rt, stderr, rt->error, false); fprintf(stderr, "\n"); return true; } pit_vec_reset(pit_callstack_entry)(rt->callstack); return false; } void pit_debug_trace_(pit_runtime *rt, char *format, pit_value v) { char buf[1024] = {0}; i64 end = pit_dump(rt, buf, sizeof(buf) - 1, v, true); buf[end] = 0; pit_fprintf(rt, stderr, format, buf); } pit_value pit_value_bytes_new_file(pit_runtime *rt, char *path) { if (rt->error != PIT_NIL) return PIT_NIL; FILE *f = fopen(path, "r"); if (f == NULL) { pit_error(rt, "failed to open file: %s", path); return PIT_NIL; } fseek(f, 0, SEEK_END); i64 len = ftell(f); fseek(f, 0, SEEK_SET); u8 *dest = pit_arena_alloc_array(rt->heap, len); if (!dest) { pit_error(rt, "failed to allocate bytes"); fclose(f); return PIT_NIL; } if ((size_t) len != fread(dest, sizeof(char), (size_t) len, f)) { fclose(f); pit_error(rt, "failed to read file: %s", path); return PIT_NIL; } fclose(f); pit_value ret = pit_value_ref_heavy_new(rt); pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret)); if (!h) { pit_error(rt, "failed to create new heavy value for bytes"); return PIT_NIL; } h->hsort = PIT_VALUE_HEAVY_SORT_BYTES; h->in.bytes.data = dest; h->in.bytes.len = len; return ret; } pit_value pit_load_file(pit_runtime *rt, char *path) { pit_lexer lex; pit_parser parse; bool eof = false; pit_value p = PIT_NIL; pit_value compiled = PIT_NIL; pit_value ret = PIT_NIL; if (pit_lex_file(&lex, path) < 0) { pit_error(rt, "failed to lex file: %s", path); return PIT_NIL; } pit_parser_from_lexer(&parse, &lex); while (p = pit_parse(rt, &parse, &eof), !eof) { pit_check_invariants(rt); if (pit_runtime_print_error(rt)) return PIT_NIL; compiled = pit_compile(rt, p); pit_check_invariants(rt); if (pit_runtime_print_error(rt)) return PIT_NIL; ret = pit_vm_eval(rt, compiled); pit_check_invariants(rt); if (pit_runtime_print_error(rt)) return PIT_NIL; } pit_check_invariants(rt); if (pit_runtime_print_error(rt)) return PIT_NIL; return ret; } void pit_repl(pit_runtime *rt) { size_t bufcap = 8; char *buf = malloc(bufcap); i64 len = 0; pit_check_invariants(rt); if (pit_runtime_print_error(rt)) exit(1); setbuf(stdout, NULL); printf("> "); while ((buf[len++] = (char) getchar()) != EOF) { if (len >= (i64) bufcap) { bufcap *= 2; buf = realloc(buf, bufcap); } pit_value res = PIT_NIL; pit_lexer lex; pit_parser parse; bool eof = false; pit_value p = PIT_NIL; i64 depth = 0; bool lex_error = false; pit_lex_token tok = PIT_LEX_TOKEN_EOF; if (buf[len - 1] != '\n') continue; pit_lex_bytes(&lex, buf, len); while (!lex_error && (tok = pit_lex_next(&lex)) != PIT_LEX_TOKEN_EOF) { switch (tok) { case PIT_LEX_TOKEN_ERROR: lex_error = true; break; case PIT_LEX_TOKEN_LPAREN: depth += 1; break; case PIT_LEX_TOKEN_RPAREN: depth -= 1; break; default: break; } } if (lex_error || depth > 0) continue; buf[len - 1] = 0; pit_lex_bytes(&lex, buf, len); pit_parser_from_lexer(&parse, &lex); while (p = pit_parse(rt, &parse, &eof), !eof) { pit_check_invariants(rt); res = pit_vm_eval(rt, pit_compile(rt, pit_macroexpand(rt, p))); pit_check_invariants(rt); } if (pit_runtime_print_error(rt)) { rt->error = PIT_NIL; printf("> "); } else { pit_dump_to_file(rt, stdout, res, true); pit_gc(rt); printf("\n> "); } len = 0; } if (len >= (i64) sizeof(buf)) { fprintf(stderr, "expression exceeded REPL buffer size\n"); } else { printf("bye!\n"); } free(buf); } static pit_value impl_diagnostics(pit_runtime *rt, pit_value args, void *data) { (void) data; (void) args; fprintf(stderr, "value allocs: %ld\n", rt->heap->next); return PIT_NIL; } static pit_value impl_assert(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value msg = pit_value_cons_car(rt, args); args = pit_value_cons_cdr(rt, args); pit_value expected = pit_value_cons_car(rt, args); args = pit_value_cons_cdr(rt, args); pit_value actual = pit_value_cons_car(rt, args); args = pit_value_cons_cdr(rt, args); if (!pit_value_equal(rt, expected, actual)) { pit_fprintf(rt, stderr, "assertion %v failed!\nexpected: %v\nactual: %v\n", msg, expected, actual); exit(1); } return actual; } static pit_value impl_print(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value x = pit_value_cons_car(rt, args); pit_dump_to_file(rt, stdout, x, true); printf("\n"); return x; } static pit_value impl_princ(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value x = pit_value_cons_car(rt, args); pit_dump_to_file(rt, stdout, x, false); printf("\n"); return x; } static pit_value impl_load(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value path = pit_value_cons_car(rt, args); char relbuf[1024] = {0}; i64 len = pit_value_bytes_copy(rt, path, (u8 *) relbuf, sizeof(relbuf) - 1); if (len < 0) { pit_error(rt, "path was not a string"); return PIT_NIL; } relbuf[len] = 0; if (relbuf[0] != '/') { /* if this is a relative path... */ char absbuf[1024] = {0}; struct stat sb; pit_value lp = pit_symtab_get(rt, pit_symtab_intern_cstr(rt, "*load-path*")); /* ...first search against the current directory... */ if (stat(relbuf, &sb) == 0) { return pit_load_file(rt, relbuf); } while (lp != PIT_NIL) { /* ...and then the *load-path* variable */ pit_value ent = pit_value_cons_car(rt, lp); i64 elen = pit_value_bytes_copy(rt, ent, (u8 *) absbuf, sizeof(absbuf) - 1); if (elen > 0 && absbuf[elen - 1] != '/') absbuf[elen++] = '/'; i64 remaining = (i64) sizeof(absbuf) - elen - 1; if (len > remaining) { pit_error(rt, "not enough space in buffer to construct file load path"); return PIT_NIL; } pit_libc_string_memcpy((u8 *) &absbuf[elen], (u8 *) relbuf, (size_t) len); absbuf[elen + len] = 0; if (stat(absbuf, &sb) == 0) { /* the file exists, this is the one we load! */ return pit_load_file(rt, absbuf); } lp = pit_value_cons_cdr(rt, lp); } /* we didn't find the file anywhere :( */ pit_error(rt, "failed to load file: %s", relbuf); return PIT_NIL; } else { /* this is an absolute path, so simply load it */ return pit_load_file(rt, relbuf); } } void pit_install_library_io(pit_runtime *rt) { pit_value load_path = PIT_NIL; char *lp = getenv("PIT_LOAD_PATH"); if (lp != NULL) { char *section = strtok(lp, ":"); while (section) { load_path = pit_value_cons(rt, pit_value_bytes_new_cstr(rt, section), load_path); section = strtok(NULL, ":"); } load_path = pit_value_list_reverse(rt, load_path); } /* variables */ pit_symtab_set(rt, pit_symtab_intern_cstr(rt, "*load-path*"), load_path); /* diagnostics */ pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "diagnostics!"), pit_value_nativefunc_new(rt, impl_diagnostics)); pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "assert!"), pit_value_nativefunc_new(rt, impl_assert)); /* stream IO */ pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "print!"), pit_value_nativefunc_new(rt, impl_print)); pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "princ!"), pit_value_nativefunc_new(rt, impl_princ)); /* disk IO */ pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "load!"), pit_value_nativefunc_new(rt, impl_load)); } struct bytestring { i64 len, cap; u8 *data; }; static pit_value impl_bs_new(pit_runtime *rt, pit_value args, void *data) { (void) data; (void) args; i64 cap = 256; struct bytestring *bs = malloc(sizeof(struct bytestring)); if (!bs) return PIT_NIL; bs->len = 0; bs->cap = cap; bs->data = calloc((size_t) cap, 1); return pit_value_nativedata_new(rt, pit_symtab_intern_cstr(rt, "bs"), (void *) bs); } static pit_value impl_bs_delete(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value v = pit_value_cons_car(rt, args); pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, v)); if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; } if (h->hsort != PIT_VALUE_HEAVY_SORT_NATIVEDATA) { pit_error(rt, "invalid use of value as bytestring nativedata"); return PIT_NIL; } if (!pit_value_eq(h->in.nativedata.tag, pit_symtab_intern_cstr(rt, "bs"))) { pit_error(rt, "native value is not a bytestring"); return PIT_NIL; } if (!h->in.nativedata.data) { pit_error(rt, "bytestring was already freed"); return PIT_NIL; } struct bytestring *bs = h->in.nativedata.data; if (bs->data) free(bs->data); bs->data = NULL; free(bs); h->in.nativedata.data = NULL; return PIT_T; } static pit_value impl_bs_grow(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value vsz = pit_value_cons_car(rt, args); pit_value v = pit_value_cons_car(rt, pit_value_cons_cdr(rt, args)); struct bytestring *bs = pit_value_nativedata_get(rt, pit_symtab_intern_cstr(rt, "bs"), v); if (!bs) return PIT_NIL; i64 sz = pit_value_as_integer(rt, vsz); if (sz > bs->len) { if (sz > bs->cap) { while (bs->cap < sz) bs->cap <<= 1; bs->data = realloc(bs->data, (size_t) bs->cap); } bs->len = sz; } return v; } static pit_value impl_bs_spit(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value path = pit_value_cons_car(rt, args); char pathbuf[1024] = {0}; i64 len = pit_value_bytes_copy(rt, path, (u8 *) pathbuf, sizeof(pathbuf) - 1); if (len < 0) { pit_error(rt, "path was not a string"); return PIT_NIL; } pathbuf[len] = 0; pit_value v = pit_value_cons_car(rt, pit_value_cons_cdr(rt, args)); struct bytestring *bs = pit_value_nativedata_get(rt, pit_symtab_intern_cstr(rt, "bs"), v); if (!bs) return PIT_NIL; FILE *f = fopen(pathbuf, "w+"); if (!f) { pit_error(rt, "failed to open file: %s", pathbuf); return PIT_NIL; } size_t written = fwrite(bs->data, 1, (size_t) bs->len, f); fclose(f); if (written != (size_t) bs->len) { pit_error(rt, "failed to write bytestring to file: %s", pathbuf); return PIT_NIL; } return v; } static pit_value impl_bs_write8(pit_runtime *rt, pit_value args, void *data) { (void) data; pit_value v = pit_value_cons_car(rt, args); pit_value vidx = pit_value_cons_car(rt, pit_value_cons_cdr(rt, args)); pit_value vx = pit_value_cons_car(rt, pit_value_cons_cdr(rt, pit_value_cons_cdr(rt, args))); struct bytestring *bs = pit_value_nativedata_get(rt, pit_symtab_intern_cstr(rt, "bs"), v); if (!bs) return PIT_NIL; i64 idx = pit_value_as_integer(rt, vidx); u8 x = (u8) pit_value_as_integer(rt, vx); if (idx >= bs->len) { pit_error(rt, "index %d out of bounds in bytestring (length %d)", idx, bs->len); return PIT_NIL; } bs->data[idx] = x; return v; } void pit_install_library_bytestring(pit_runtime *rt) { /* bytestrings */ pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "bs/new!"), pit_value_nativefunc_new(rt, impl_bs_new)); pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "bs/delete!"), pit_value_nativefunc_new(rt, impl_bs_delete)); pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "bs/grow!"), pit_value_nativefunc_new(rt, impl_bs_grow)); pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "bs/spit!"), pit_value_nativefunc_new(rt, impl_bs_spit)); pit_symtab_fset(rt, pit_symtab_intern_cstr(rt, "bs/write8!"), pit_value_nativefunc_new(rt, impl_bs_write8)); }