From c1a6dfb43efca52cacae738ce3d9648c4981906e Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Fri, 25 Sep 2026 14:34:59 -0400 Subject: pit: Add load path --- pit/notes/evaluator1.org | 51 ++++++++++++++++++++++++++++++++++++++++ pit/src/native.c | 60 ++++++++++++++++++++++++++++++++++++++++++------ pit/test/loadpath.pit | 2 ++ pit/whereweleftoff.org | 51 ---------------------------------------- 4 files changed, 106 insertions(+), 58 deletions(-) create mode 100644 pit/notes/evaluator1.org create mode 100644 pit/test/loadpath.pit delete mode 100644 pit/whereweleftoff.org diff --git a/pit/notes/evaluator1.org b/pit/notes/evaluator1.org new file mode 100644 index 0000000..c7a5ccb --- /dev/null +++ b/pit/notes/evaluator1.org @@ -0,0 +1,51 @@ +[2026-06-26] + +- annotations do not work properly because macroexpansion creates an entirely new body for lambdas +- we realized that we are handling macro application in two separate places: pit_expand_macros expands lambda bodies, and pit_eval expands macros encountered while evaling. we ought to unify this so that only one is used (probably pit_expand_macros, because we need to expand macros eagerly to identify free variables to capture) +- we probably can make pit_expand_macros and pit_eval much nicer +- we can probably make pit_expand_macros operate in place +- if we want to be really smart, cool, happy, rich: + let's just make stuff translate to a little VM before it evaluates, and let's store VM programs as functions instead of sexps + +* little vm thing +let's have a "program" (representation of one function) be a cons-list of instructions +during execution, we maintain a stack of programs. +calling a lisp function entails pushing a new "frame" to this stack +we iterate over the frame by cdring as usual and we pop the frame when we reach the end + +#+begin_src pit +(defun! add2 (x y) (+ x y 2)) +#+end_src + +#+begin_src pit +[ + (literal x) + (get) + (literal y) + (get) + (literal 2) + (literal +) + (fget) + (apply 3) +] +#+end_src + +#+begin_src pit +(defun! foo (x) (* (add2 x x) 4)) +#+end_src + +#+begin_src pit +[ + (literal x) + (get) + (literal x) + (get) + (literal add2) + (fget) + (apply 2) ;; evaluating this pushes a new frame with the code for add2. next "tick" of evaluator continues in that code. once it returns, result is on the stack (for free) + (literal 4) + (literal *) + (fget) + (apply 2) +] +#+end_src diff --git a/pit/src/native.c b/pit/src/native.c index d0989a5..4dab09d 100644 --- a/pit/src/native.c +++ b/pit/src/native.c @@ -2,6 +2,8 @@ #include #include +#include + #include #include #include @@ -101,7 +103,7 @@ pit_value pit_value_bytes_new_file(pit_runtime *rt, char *path) { /* check invariants - these should always hold between top-level evaluations! */ static void check_invariants(pit_runtime *rt) { if (rt->saved_bindings->next != 0) { - pit_error(rt, "leaked saved_bindings memory! %ld", rt->result_stack->next); + pit_error(rt, "leaked saved_bindings memory! %ld", rt->saved_bindings->next); } if (rt->expr_stack->next != 0) { pit_error(rt, "leaked expr_stack memory! %ld", rt->expr_stack->next); @@ -110,7 +112,7 @@ static void check_invariants(pit_runtime *rt) { pit_error(rt, "leaked result_stack memory! %ld", rt->result_stack->next); } if (rt->compilation_stack->next != 0) { - pit_error(rt, "leaked compilation_stack memory! %ld", rt->result_stack->next); + pit_error(rt, "leaked compilation_stack memory! %ld", rt->compilation_stack->next); } if (rt->traversal->next != 0) { pit_error(rt, "leaked traversal memory! %ld", rt->traversal->next); @@ -146,7 +148,6 @@ void pit_repl(pit_runtime *rt) { size_t bufcap = 8; char *buf = malloc(bufcap); i64 len = 0; - pit_runtime_freeze(rt); check_invariants(rt); if (pit_runtime_print_error(rt)) exit(1); setbuf(stdout, NULL); printf("> "); @@ -223,13 +224,58 @@ static pit_value impl_princ(pit_runtime *rt, pit_value args, void *data) { 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 pathbuf[1024] = {0}; - i64 len = pit_value_bytes_copy(rt, path, (u8 *) pathbuf, sizeof(pathbuf) - 1); + 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; } - pathbuf[len] = 0; - return pit_load_file(rt, pathbuf); + 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)); /* stream IO */ diff --git a/pit/test/loadpath.pit b/pit/test/loadpath.pit new file mode 100644 index 0000000..5f8bcf8 --- /dev/null +++ b/pit/test/loadpath.pit @@ -0,0 +1,2 @@ +;; (setq! *load-path* (list "test/")) +(load! "test.pit") \ No newline at end of file diff --git a/pit/whereweleftoff.org b/pit/whereweleftoff.org deleted file mode 100644 index c7a5ccb..0000000 --- a/pit/whereweleftoff.org +++ /dev/null @@ -1,51 +0,0 @@ -[2026-06-26] - -- annotations do not work properly because macroexpansion creates an entirely new body for lambdas -- we realized that we are handling macro application in two separate places: pit_expand_macros expands lambda bodies, and pit_eval expands macros encountered while evaling. we ought to unify this so that only one is used (probably pit_expand_macros, because we need to expand macros eagerly to identify free variables to capture) -- we probably can make pit_expand_macros and pit_eval much nicer -- we can probably make pit_expand_macros operate in place -- if we want to be really smart, cool, happy, rich: - let's just make stuff translate to a little VM before it evaluates, and let's store VM programs as functions instead of sexps - -* little vm thing -let's have a "program" (representation of one function) be a cons-list of instructions -during execution, we maintain a stack of programs. -calling a lisp function entails pushing a new "frame" to this stack -we iterate over the frame by cdring as usual and we pop the frame when we reach the end - -#+begin_src pit -(defun! add2 (x y) (+ x y 2)) -#+end_src - -#+begin_src pit -[ - (literal x) - (get) - (literal y) - (get) - (literal 2) - (literal +) - (fget) - (apply 3) -] -#+end_src - -#+begin_src pit -(defun! foo (x) (* (add2 x x) 4)) -#+end_src - -#+begin_src pit -[ - (literal x) - (get) - (literal x) - (get) - (literal add2) - (fget) - (apply 2) ;; evaluating this pushes a new frame with the code for add2. next "tick" of evaluator continues in that code. once it returns, result is on the stack (for free) - (literal 4) - (literal *) - (fget) - (apply 2) -] -#+end_src -- cgit v1.3.1