From 281bccb3738012584ae0bc84a4c958f4b95d7a05 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 30 Jul 2026 17:26:51 -0700 Subject: [PATCH 1/9] wip broken --- MILESTONES.md | 1 + bin/native_test.ml | 4 ++-- lib/interpreter/dune | 7 ++++++- lib/interpreter/interpret.ml | 27 +++++++++++++++------------ lib/interpreter/native.ml | 12 +++++++----- lib/interpreter/native.mli | 2 +- native/native.ml | 2 +- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/MILESTONES.md b/MILESTONES.md index 761a5934..e4de750a 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -119,3 +119,4 @@ ## Bugs - [ ] bug: stacktrace printing is wrong in REPL - [ ] bug: Do static methods need to take self as first arg? +- [ ] bug: $cwd is NOT always absolute diff --git a/bin/native_test.ml b/bin/native_test.ml index 257dc337..2615ce8b 100644 --- a/bin/native_test.ml +++ b/bin/native_test.ml @@ -1,6 +1,6 @@ let map_some cb = function Some v -> cb v | None -> failwith "Nope!" let map_none cb = function None -> cb () | Some _ -> failwith "Nope!" -let map_ok cb = function Ok () -> cb () | Error _ -> failwith "Nope!" +let map_ok cb = function Ok v -> cb v | Error _ -> failwith "Nope!" let map_error cb = function Ok _ -> failwith "Nope!" | Error msg -> cb msg let () = @@ -12,4 +12,4 @@ let () = |> map_error (fun msg -> Printf.printf "CD-ing to /nosuchdir returned \"%s\"\n" msg); Sn.Native.chdir "/" - |> map_ok (fun () -> print_endline "CD-ing to / succeeded.") + |> map_ok (fun msg -> Printf.printf "CD-ing to / succeeded with \"%s\"." msg) diff --git a/lib/interpreter/dune b/lib/interpreter/dune index 8128f5c8..6f236672 100644 --- a/lib/interpreter/dune +++ b/lib/interpreter/dune @@ -1,5 +1,10 @@ (library (name interpreter) - (libraries compiler core_unix sloth_common core_unix.sys_unix) + (libraries + compiler + core_unix + core_unix.sys_unix + core_unix.filename_unix + sloth_common) (preprocess (pps ppx_jane))) diff --git a/lib/interpreter/interpret.ml b/lib/interpreter/interpret.ml index e0b62147..c57f254c 100644 --- a/lib/interpreter/interpret.ml +++ b/lib/interpreter/interpret.ml @@ -740,11 +740,19 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = let middleware name prev next = match name with | "$cwd" -> + let cd_wrapper p = + match M.chdir p with + | Ok new_p -> ( + match + Context.reassign globals.context_ids "$cwd" + (Runtime.String new_p) + with + | Some () -> () + | None -> internal_failure "Failed to re-assign $cwd") + | Error msg -> fail ~globals pos msg + in (match Runtime.string_of_val next with - | Some s -> ( - match M.chdir s with - | Ok () -> () - | Error msg -> fail ~globals pos msg) + | Some s -> cd_wrapper s | None -> fail ~globals pos @@ Printf.sprintf @@ -753,14 +761,9 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = post_block_hook := Some (fun () -> - let unit_res = - Runtime.string_of_val prev - |> option_value ~message:__LOC__ - |> M.chdir - in - match unit_res with - | Ok () -> () - | Error msg -> fail ~globals pos msg) + Runtime.string_of_val prev + |> option_value ~message:__LOC__ + |> cd_wrapper) | _ -> () in diff --git a/lib/interpreter/native.ml b/lib/interpreter/native.ml index ff33089b..4f77382d 100644 --- a/lib/interpreter/native.ml +++ b/lib/interpreter/native.ml @@ -35,7 +35,7 @@ module type Sig = sig string array -> (Runtime.t, string) Result.t - val chdir : string -> (unit, string) Result.t + val chdir : string -> (string, string) Result.t val directory_exists : string -> bool val file_exists : string -> bool val mkdir : string -> unit @@ -97,10 +97,12 @@ module Prod : Sig = struct else Ok () let chdir path = - try Ok (Core_unix.chdir path) - with Core_unix.Unix_error (err, _, _) -> - let err_msg = Core_unix.Error.message err in - Error (Printf.sprintf "`chdir(%s)` failed with \"%s\"" path err_msg) + let open Result.Monad_infix in + (try Ok (Core_unix.chdir path) + with Core_unix.Unix_error (err, _, _) -> + let err_msg = Core_unix.Error.message err in + Error (Printf.sprintf "`chdir(%s)` failed with \"%s\"" path err_msg)) + >>= fun () -> Core_unix.Filename_unix.realpath "." let mkdir = Core_unix.mkdir ~perm:0o775 diff --git a/lib/interpreter/native.mli b/lib/interpreter/native.mli index 5d2557bc..5a10993e 100644 --- a/lib/interpreter/native.mli +++ b/lib/interpreter/native.mli @@ -28,7 +28,7 @@ module type Sig = sig string array -> (Runtime.t, string) Result.t - val chdir : string -> (unit, string) Result.t + val chdir : string -> (string, string) Result.t val directory_exists : string -> bool val file_exists : string -> bool val mkdir : string -> unit diff --git a/native/native.ml b/native/native.ml index f531a3b7..37f716fa 100644 --- a/native/native.ml +++ b/native/native.ml @@ -2,7 +2,7 @@ Check for correctness if switching to Core! *) external file_read_all : string -> string option = "file_read_all" -external chdir : string -> (unit, string) Result.t = "_chdir" +external chdir : string -> (string, string) Result.t = "_chdir" (* val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t From 06802b7924b8b9b99ed19cc17f4fda4a8a3c47c6 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 30 Jul 2026 17:41:54 -0700 Subject: [PATCH 2/9] wip --- lib/interpreter/dune | 7 +------ lib/interpreter/interpret.ml | 8 ++++---- lib/interpreter/native.ml | 9 +++------ lib/interpreter/native.mli | 5 +++++ 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/interpreter/dune b/lib/interpreter/dune index 6f236672..d5f1866f 100644 --- a/lib/interpreter/dune +++ b/lib/interpreter/dune @@ -1,10 +1,5 @@ (library (name interpreter) - (libraries - compiler - core_unix - core_unix.sys_unix - core_unix.filename_unix - sloth_common) + (libraries compiler core_unix sloth_common core_unix.sys_unix core_unix.filename_unix) (preprocess (pps ppx_jane))) diff --git a/lib/interpreter/interpret.ml b/lib/interpreter/interpret.ml index c57f254c..13602bc7 100644 --- a/lib/interpreter/interpret.ml +++ b/lib/interpreter/interpret.ml @@ -747,7 +747,7 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = Context.reassign globals.context_ids "$cwd" (Runtime.String new_p) with - | Some () -> () + | Some () -> Printf.printf "[DEBUG] %s\n" new_p; () | None -> internal_failure "Failed to re-assign $cwd") | Error msg -> fail ~globals pos msg in @@ -771,7 +771,7 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = List.fold assignments ~init:(inner_globals, First ()) ~f:(fun prev (name, expr) -> prev >>= fun globals () -> - interpret_expr globals expr >>= fun globals next_path -> + interpret_expr globals expr >>= fun globals next_value -> let prev = match Context.get globals.context_ids name with | Some prev -> prev @@ -780,8 +780,8 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = name |> fail ~globals pos in - middleware name prev next_path; - (match Context.reassign globals.context_ids name next_path with + middleware name prev next_value; + (match Context.reassign globals.context_ids name next_value with | Some () -> () | None -> internal_failure __LOC__); (globals, First ())) diff --git a/lib/interpreter/native.ml b/lib/interpreter/native.ml index 4f77382d..4414b1af 100644 --- a/lib/interpreter/native.ml +++ b/lib/interpreter/native.ml @@ -8,13 +8,10 @@ type processMode = | BlockBuffer (* proc&! -> ProcessResult *) module type Sig = sig - (* TODO we should never be returning Runtime.t, but more specific types *) - val file_read_all : string -> string val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t - (* TODO does this type of `data` need to be more generic to support binary? *) val write : Core_unix.File_descr.t -> data:string -> (unit, string) Result.t val read : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val wait : Runtime.process_handle -> (Runtime.t, string) Result.t @@ -102,7 +99,7 @@ module Prod : Sig = struct with Core_unix.Unix_error (err, _, _) -> let err_msg = Core_unix.Error.message err in Error (Printf.sprintf "`chdir(%s)` failed with \"%s\"" path err_msg)) - >>= fun () -> Core_unix.Filename_unix.realpath "." + >>= fun () -> Ok (Filename_unix.realpath path) let mkdir = Core_unix.mkdir ~perm:0o775 @@ -436,8 +433,8 @@ module Make_test () : TestSig = struct this_pid let proc_expectations : Mock_process.spec option ref = ref None - let chdir _ = Ok () - (* This function only exists to cause OS side-effects, no-op in tests *) + let chdir path = Ok path + (* Note: this will not normalize paths as the real one does *) let mkdir path = match Hashtbl.add path_to_entity ~key:path ~data:Directory with diff --git a/lib/interpreter/native.mli b/lib/interpreter/native.mli index 5a10993e..08c497cb 100644 --- a/lib/interpreter/native.mli +++ b/lib/interpreter/native.mli @@ -7,13 +7,18 @@ type processMode = | BlockBuffer (* proc&! -> ProcessResult *) module type Sig = sig + (* TODO we should never be returning Runtime.t, but more specific types *) + val file_read_all : string -> string val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t + + (* TODO does this type of `data` need to be more generic to support binary? *) val write : Core_unix.File_descr.t -> data:string -> (unit, string) Result.t val read : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val wait : Runtime.process_handle -> (Runtime.t, string) Result.t val pipe : unit -> Core_unix.File_descr.t * Core_unix.File_descr.t + (** unit -> read * write *) val open_file : mode:Core_unix.open_flag list -> From a05331077459c9c6f1b7b1549ca1ba846aa624b6 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 30 Jul 2026 21:44:33 -0700 Subject: [PATCH 3/9] wip broken --- lib/interpreter/context.ml | 6 +++++ lib/interpreter/context.mli | 1 + lib/interpreter/dune | 1 + lib/interpreter/globals.ml | 4 +-- lib/interpreter/interpret.ml | 18 +++++++++++--- lib/interpreter/native.ml | 40 ++---------------------------- lib/interpreter/native.mli | 43 ++------------------------------- lib/interpreter/native_sig.mli | 43 +++++++++++++++++++++++++++++++++ lib/interpreter/stdlib_impl.ml | 12 ++++----- lib/interpreter/stdlib_impl.mli | 4 +-- 10 files changed, 80 insertions(+), 92 deletions(-) create mode 100644 lib/interpreter/native_sig.mli diff --git a/lib/interpreter/context.ml b/lib/interpreter/context.ml index ebaf216b..abb3d418 100644 --- a/lib/interpreter/context.ml +++ b/lib/interpreter/context.ml @@ -33,3 +33,9 @@ let reassign t' name v = let tbl = List.hd_exn t' in Hashtbl.change tbl name ~f:(fun _ -> Some v); Some () + +let debug t' to_s = + List.iter t' ~f:(fun tbl -> + Hashtbl.iter_keys tbl ~f:(fun key -> + let value = Hashtbl.find tbl key |> Option.value_exn in + Printf.printf "%s => %s\n" key (to_s value))) diff --git a/lib/interpreter/context.mli b/lib/interpreter/context.mli index afeb1b15..96679fd1 100644 --- a/lib/interpreter/context.mli +++ b/lib/interpreter/context.mli @@ -5,3 +5,4 @@ val push_empty : 'a t -> 'a t val get : 'a t -> string -> 'a option val bind : 'a t -> string -> 'a -> unit option val reassign : 'a t -> string -> 'a -> unit option +val debug : 'a t -> ('a -> string) -> unit diff --git a/lib/interpreter/dune b/lib/interpreter/dune index d5f1866f..6e849f90 100644 --- a/lib/interpreter/dune +++ b/lib/interpreter/dune @@ -1,5 +1,6 @@ (library (name interpreter) (libraries compiler core_unix sloth_common core_unix.sys_unix core_unix.filename_unix) + (modules_without_implementation native_sig) (preprocess (pps ppx_jane))) diff --git a/lib/interpreter/globals.ml b/lib/interpreter/globals.ml index 63433693..dc238e4f 100644 --- a/lib/interpreter/globals.ml +++ b/lib/interpreter/globals.ml @@ -1,7 +1,7 @@ open Core type t = { - l : (module Native.Sig); + l : (module Native_sig.Sig); identifiers : Runtime.t Identifiers.t; context_ids : Runtime.t Context.t; (* We need to store these at the top level so that we can find these for @@ -26,7 +26,7 @@ let push_frame t next_func pos = (* TODO memoize *) let make_globals m src script_path ~argv ~env ~version = - let module M = (val m : Native.Sig) in + let module M = (val m : Native_sig.Sig) in let classes = Hashtbl.create (module String) in let identifiers : Runtime.t Identifiers.t = Identifiers.create () in let context_ids = Context.create () in diff --git a/lib/interpreter/interpret.ml b/lib/interpreter/interpret.ml index 13602bc7..d17a1f13 100644 --- a/lib/interpreter/interpret.ml +++ b/lib/interpreter/interpret.ml @@ -728,16 +728,21 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = | Exit _ -> (globals, either) | Return _ -> (globals, either))) | WithExpr (assignments, block, pos) -> + (* TODO *) let module M = (val globals.l) in let post_block_hook = ref None in let inner_globals = { globals with context_ids = Context.push_empty globals.context_ids } in + let debug tag = + let cwd = Context.get inner_globals.context_ids "$cwd" |> Option.value_exn |> Runtime.to_s in + Printf.printf "[DEBUG] %s\t$CWD = %s\n" tag cwd + in (* Process side effects for certain context variables These can't be recovered from, we must fail immediately *) - let middleware name prev next = + let middleware name prev next = (* TODO: this is wrong, we shouldn't re-assign $cwd here, we do it later; we should have a seperate syscall for realpath, and do it before here *) match name with | "$cwd" -> let cd_wrapper p = @@ -747,7 +752,9 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = Context.reassign globals.context_ids "$cwd" (Runtime.String new_p) with - | Some () -> Printf.printf "[DEBUG] %s\n" new_p; () + | Some () -> + debug "B"; + () | None -> internal_failure "Failed to re-assign $cwd") | Error msg -> fail ~globals pos msg in @@ -767,6 +774,7 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = | _ -> () in + debug "C"; let _, either = List.fold assignments ~init:(inner_globals, First ()) ~f:(fun prev (name, expr) -> @@ -780,15 +788,19 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = name |> fail ~globals pos in + debug "D"; middleware name prev next_value; + debug "E"; (match Context.reassign globals.context_ids name next_value with | Some () -> () | None -> internal_failure __LOC__); (globals, First ())) >>= fun globals () -> (globals, interpret_block globals block) in + debug "F"; (* This needs to run regardless of either's state *) let _ = Option.map !post_block_hook ~f:(fun hook -> hook ()) in + debug "G"; (globals, either) | CatchExpr { subject; capture; catch; pos = _ } -> ( let globals, subject_either = interpret_expr globals subject in @@ -1155,7 +1167,7 @@ and cast_to_file_descriptor ~globals ~pos ~mode ~m t : match t with | FileDescriptor fd -> First fd | File { path } -> ( - let module M = (val m : Native.Sig) in + let module M = (val m : Native_sig.Sig) in match M.open_file ~mode path with | Ok fd -> First fd | Error msg -> failure_obj ~globals ~pos msg) diff --git a/lib/interpreter/native.ml b/lib/interpreter/native.ml index 4414b1af..c176f404 100644 --- a/lib/interpreter/native.ml +++ b/lib/interpreter/native.ml @@ -1,44 +1,8 @@ open Core open Sloth_common.Common +open Native_sig -type processMode = - | BlockInherit (* proc! -> null *) - | ForkInherit (* ? -> ProcessHandle *) - | ForkBuffer (* proc& -> ProcessHandle *) - | BlockBuffer (* proc&! -> ProcessResult *) - -module type Sig = sig - val file_read_all : string -> string - val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t - val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t - - val write : Core_unix.File_descr.t -> data:string -> (unit, string) Result.t - val read : Core_unix.File_descr.t -> (Runtime.t, string) Result.t - val wait : Runtime.process_handle -> (Runtime.t, string) Result.t - - val pipe : unit -> Core_unix.File_descr.t * Core_unix.File_descr.t - (** unit -> read * write *) - - val open_file : - mode:Core_unix.open_flag list -> - string -> - (Core_unix.File_descr.t, string) Result.t - - val close : Core_unix.File_descr.t -> (unit, string) Result.t - - val proc_exec : - mode:processMode -> - Runtime.process -> - string array -> - (Runtime.t, string) Result.t - - val chdir : string -> (string, string) Result.t - val directory_exists : string -> bool - val file_exists : string -> bool - val mkdir : string -> unit -end - -module Prod : Sig = struct +module Prod : Native_sig.Sig = struct let pipe = Core_unix.pipe ~close_on_exec:true let file_read_all = In_channel.read_all diff --git a/lib/interpreter/native.mli b/lib/interpreter/native.mli index 08c497cb..c357fcbe 100644 --- a/lib/interpreter/native.mli +++ b/lib/interpreter/native.mli @@ -1,48 +1,9 @@ open Core -type processMode = - | BlockInherit (* proc! -> null *) - | ForkInherit (* ? -> ProcessHandle *) - | ForkBuffer (* proc& -> ProcessHandle *) - | BlockBuffer (* proc&! -> ProcessResult *) - -module type Sig = sig - (* TODO we should never be returning Runtime.t, but more specific types *) - - val file_read_all : string -> string - val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t - val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t - - (* TODO does this type of `data` need to be more generic to support binary? *) - val write : Core_unix.File_descr.t -> data:string -> (unit, string) Result.t - val read : Core_unix.File_descr.t -> (Runtime.t, string) Result.t - val wait : Runtime.process_handle -> (Runtime.t, string) Result.t - val pipe : unit -> Core_unix.File_descr.t * Core_unix.File_descr.t - (** unit -> read * write *) - - val open_file : - mode:Core_unix.open_flag list -> - string -> - (Core_unix.File_descr.t, string) Result.t - - val close : Core_unix.File_descr.t -> (unit, string) Result.t - - val proc_exec : - mode:processMode -> - Runtime.process -> - string array -> - (Runtime.t, string) Result.t - - val chdir : string -> (string, string) Result.t - val directory_exists : string -> bool - val file_exists : string -> bool - val mkdir : string -> unit -end - -module Prod : Sig +module Prod : Native_sig.Sig module type TestSig = sig - include Sig + include Native_sig.Sig type fs_entity = File of string ref * Core_unix.File_descr.t | Directory diff --git a/lib/interpreter/native_sig.mli b/lib/interpreter/native_sig.mli new file mode 100644 index 00000000..86c48f0a --- /dev/null +++ b/lib/interpreter/native_sig.mli @@ -0,0 +1,43 @@ +open Core + +(* This is its own, interface-only module to work around: + https://discuss.ocaml.org/t/why-do-i-need-to-repeat-type-declarations-between-interfaces-and-implementations-or-how-do-i-get-around-this/3350 *) + +type processMode = + | BlockInherit (* proc! -> null *) + | ForkInherit (* ? -> ProcessHandle *) + | ForkBuffer (* proc& -> ProcessHandle *) + | BlockBuffer (* proc&! -> ProcessResult *) + +module type Sig = sig + (* TODO we should never be returning Runtime.t, but more specific types *) + + val file_read_all : string -> string + val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t + val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t + + (* TODO does this type of `data` need to be more generic to support binary? *) + val write : Core_unix.File_descr.t -> data:string -> (unit, string) Result.t + val read : Core_unix.File_descr.t -> (Runtime.t, string) Result.t + val wait : Runtime.process_handle -> (Runtime.t, string) Result.t + val pipe : unit -> Core_unix.File_descr.t * Core_unix.File_descr.t + (** unit -> read * write *) + + val open_file : + mode:Core_unix.open_flag list -> + string -> + (Core_unix.File_descr.t, string) Result.t + + val close : Core_unix.File_descr.t -> (unit, string) Result.t + + val proc_exec : + mode:processMode -> + Runtime.process -> + string array -> + (Runtime.t, string) Result.t + + val chdir : string -> (string, string) Result.t + val directory_exists : string -> bool + val file_exists : string -> bool + val mkdir : string -> unit +end diff --git a/lib/interpreter/stdlib_impl.ml b/lib/interpreter/stdlib_impl.ml index 64224fbe..9099d57f 100644 --- a/lib/interpreter/stdlib_impl.ml +++ b/lib/interpreter/stdlib_impl.ml @@ -37,7 +37,7 @@ let make_method ~arity ~name cb = fun self -> Ok (make_native_func ~arity ~name (cb self)) let make_ids m = - let module M = (val m : Native.Sig) in + let module M = (val m : Native_sig.Sig) in [ ( "print", make_native_func ~arity:(Some 1) ~name:"print" (fun ctx _ args -> @@ -114,7 +114,7 @@ let make_ids m = ] let make_protos m = - let module M = (val m : Native.Sig) in + let module M = (val m : Native_sig.Sig) in [ { name = "List"; @@ -365,7 +365,7 @@ let make_protos m = `HashMap[String]String`, but got %s" @@ Runtime.to_s env) in - match M.proc_exec ~mode:Native.ForkBuffer self env with + match M.proc_exec ~mode:Native_sig.ForkBuffer self env with | Ok t' -> First t' | Error err -> Second (Runtime.Error (None, String err))) ); ( "forkInherit", @@ -388,7 +388,7 @@ let make_protos m = `HashMap[String]String`, but got %s" @@ Runtime.to_s env) in - match M.proc_exec ~mode:Native.ForkInherit self env with + match M.proc_exec ~mode:Native_sig.ForkInherit self env with | Ok t' -> First t' | Error err -> Second (Runtime.Error (None, String err))) ); ( "blockBuffer", @@ -411,7 +411,7 @@ let make_protos m = `HashMap[String]String`, but got %s" @@ Runtime.to_s env) in - match M.proc_exec ~mode:Native.BlockBuffer self env with + match M.proc_exec ~mode:Native_sig.BlockBuffer self env with | Ok t' -> First t' | Error err -> Second (Runtime.Error (None, String err))) ); ( "blockInherit", @@ -434,7 +434,7 @@ let make_protos m = `HashMap[String]String`, but got %s" @@ Runtime.to_s env) in - match M.proc_exec ~mode:Native.BlockInherit self env with + match M.proc_exec ~mode:Native_sig.BlockInherit self env with | Ok t' -> First t' | Error err -> Second (Runtime.Error (None, String err))) ); ( "stdout", diff --git a/lib/interpreter/stdlib_impl.mli b/lib/interpreter/stdlib_impl.mli index bd8316ad..c5bc9b05 100644 --- a/lib/interpreter/stdlib_impl.mli +++ b/lib/interpreter/stdlib_impl.mli @@ -5,8 +5,8 @@ type proto_t = { static_getters : (string * (Runtime.t -> (Runtime.t, string) Result.t)) list; } -val make_ids : (module Native.Sig) -> (string * Runtime.t) list -val make_protos : (module Native.Sig) -> proto_t list +val make_ids : (module Native_sig.Sig) -> (string * Runtime.t) list +val make_protos : (module Native_sig.Sig) -> proto_t list val context_ids : cwd:string -> From ad39d3bfdbc2aec1801d8ca6ca2a916010a6ad72 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 31 Jul 2026 12:32:18 -0700 Subject: [PATCH 4/9] add failing integration test --- lib/interpreter/dune | 7 ++++++- lib/interpreter/interpret.ml | 8 ++++++-- lib/interpreter/native.mli | 1 - lib/interpreter/native_sig.mli | 1 + test/integration_tests/cwd/test.sloth | 16 +++++++++++++++- tool/ci.ml | 4 ++-- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/interpreter/dune b/lib/interpreter/dune index 6e849f90..6b2cf1ce 100644 --- a/lib/interpreter/dune +++ b/lib/interpreter/dune @@ -1,6 +1,11 @@ (library (name interpreter) - (libraries compiler core_unix sloth_common core_unix.sys_unix core_unix.filename_unix) + (libraries + compiler + core_unix + sloth_common + core_unix.sys_unix + core_unix.filename_unix) (modules_without_implementation native_sig) (preprocess (pps ppx_jane))) diff --git a/lib/interpreter/interpret.ml b/lib/interpreter/interpret.ml index d17a1f13..22be904e 100644 --- a/lib/interpreter/interpret.ml +++ b/lib/interpreter/interpret.ml @@ -735,14 +735,18 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = { globals with context_ids = Context.push_empty globals.context_ids } in let debug tag = - let cwd = Context.get inner_globals.context_ids "$cwd" |> Option.value_exn |> Runtime.to_s in + let cwd = + Context.get inner_globals.context_ids "$cwd" + |> Option.value_exn |> Runtime.to_s + in Printf.printf "[DEBUG] %s\t$CWD = %s\n" tag cwd in (* Process side effects for certain context variables These can't be recovered from, we must fail immediately *) - let middleware name prev next = (* TODO: this is wrong, we shouldn't re-assign $cwd here, we do it later; we should have a seperate syscall for realpath, and do it before here *) + let middleware name prev next = + (* TODO: this is wrong, we shouldn't re-assign $cwd here, we do it later; we should have a seperate syscall for realpath, and do it before here *) match name with | "$cwd" -> let cd_wrapper p = diff --git a/lib/interpreter/native.mli b/lib/interpreter/native.mli index c357fcbe..df28ad3a 100644 --- a/lib/interpreter/native.mli +++ b/lib/interpreter/native.mli @@ -1,5 +1,4 @@ open Core - module Prod : Native_sig.Sig module type TestSig = sig diff --git a/lib/interpreter/native_sig.mli b/lib/interpreter/native_sig.mli index 86c48f0a..a9a09060 100644 --- a/lib/interpreter/native_sig.mli +++ b/lib/interpreter/native_sig.mli @@ -20,6 +20,7 @@ module type Sig = sig val write : Core_unix.File_descr.t -> data:string -> (unit, string) Result.t val read : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val wait : Runtime.process_handle -> (Runtime.t, string) Result.t + val pipe : unit -> Core_unix.File_descr.t * Core_unix.File_descr.t (** unit -> read * write *) diff --git a/test/integration_tests/cwd/test.sloth b/test/integration_tests/cwd/test.sloth index 64a71c9e..d8300e44 100755 --- a/test/integration_tests/cwd/test.sloth +++ b/test/integration_tests/cwd/test.sloth @@ -1,14 +1,28 @@ #!/usr/bin/env sloth +let sub = Directory("${$scriptDir}/sub") +# Cleanup from previous failed runs +if sub.exists() { + "rmdir ${ sub.path }"! +} + with ($cwd = $scriptDir) { let cwd = ("pwd"&!).stdout.trim() assert(cwd == $cwd) - let sub = Directory("${$cwd}/sub") sub.create() with ($cwd = sub.path) { cwd = ("pwd"&!).stdout.trim() assert(cwd == $cwd) + + # Go back up to $scriptDir + with ($cwd = '..') { + cwd = ("pwd"&!).stdout.trim() + if cwd != $cwd { + throw "${ cwd } != ${ $cwd }" + } + assert(cwd == $cwd) + } } cwd = ("pwd"&!).stdout.trim() diff --git a/tool/ci.ml b/tool/ci.ml index 4d533f10..f32a1197 100644 --- a/tool/ci.ml +++ b/tool/ci.ml @@ -122,5 +122,5 @@ let () = match res with | Ok -> print_endline "CI suite successful!" | Error errs -> - print_endline "CI suite failed with the following errors:"; - List.iter (fun msg -> Printf.printf "-> %s\n" msg) errs + print_endline "\n\nCI suite failed with the following errors:"; + List.iter (fun msg -> Printf.printf " - %s\n" msg) errs From 73fd596fda36ee443f042929ef5f4f207c242198 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 31 Jul 2026 12:54:17 -0700 Subject: [PATCH 5/9] fix --- MILESTONES.md | 2 +- lib/common/common.ml | 1 + lib/interpreter/interpret.ml | 40 ++++++++++++++-------------------- lib/interpreter/native.ml | 20 ++++++++++------- lib/interpreter/native_sig.mli | 3 ++- 5 files changed, 32 insertions(+), 34 deletions(-) diff --git a/MILESTONES.md b/MILESTONES.md index e4de750a..a0557572 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -41,6 +41,7 @@ ## next - [x] Feature: Process.forkInherit() +- [x] bug: $cwd is NOT always absolute ## v0.1 - initially tagged version - [x] Language versioning @@ -119,4 +120,3 @@ ## Bugs - [ ] bug: stacktrace printing is wrong in REPL - [ ] bug: Do static methods need to take self as first arg? -- [ ] bug: $cwd is NOT always absolute diff --git a/lib/common/common.ml b/lib/common/common.ml index 6181e324..8ed94185 100644 --- a/lib/common/common.ml +++ b/lib/common/common.ml @@ -26,6 +26,7 @@ let wrap_error cb = | CompileError msg -> Error msg | RuntimeError msg -> Error msg +(* TODO: why is this different from `internal_failure` above? *) let option_value opt ~message = match opt with Some v -> v | None -> raise @@ InternalFailure message diff --git a/lib/interpreter/interpret.ml b/lib/interpreter/interpret.ml index 22be904e..a7dca300 100644 --- a/lib/interpreter/interpret.ml +++ b/lib/interpreter/interpret.ml @@ -734,13 +734,6 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = let inner_globals = { globals with context_ids = Context.push_empty globals.context_ids } in - let debug tag = - let cwd = - Context.get inner_globals.context_ids "$cwd" - |> Option.value_exn |> Runtime.to_s - in - Printf.printf "[DEBUG] %s\t$CWD = %s\n" tag cwd - in (* Process side effects for certain context variables These can't be recovered from, we must fail immediately @@ -751,15 +744,7 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = | "$cwd" -> let cd_wrapper p = match M.chdir p with - | Ok new_p -> ( - match - Context.reassign globals.context_ids "$cwd" - (Runtime.String new_p) - with - | Some () -> - debug "B"; - () - | None -> internal_failure "Failed to re-assign $cwd") + | Ok _ -> () | Error msg -> fail ~globals pos msg in (match Runtime.string_of_val next with @@ -778,7 +763,6 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = | _ -> () in - debug "C"; let _, either = List.fold assignments ~init:(inner_globals, First ()) ~f:(fun prev (name, expr) -> @@ -792,19 +776,27 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = name |> fail ~globals pos in - debug "D"; + let next_value = + match name with + | "$cwd" -> ( + match Runtime.string_of_val next_value with + | Some path -> Runtime.String (M.realpath path) + | None -> + fail ~globals pos + @@ Printf.sprintf + "Cannot assign %s to $cwd because it must be a \ + `String`" + @@ Runtime.to_s next_value) + | _ -> next_value + in + Context.reassign globals.context_ids name next_value + |> option_value ~message:__LOC__; middleware name prev next_value; - debug "E"; - (match Context.reassign globals.context_ids name next_value with - | Some () -> () - | None -> internal_failure __LOC__); (globals, First ())) >>= fun globals () -> (globals, interpret_block globals block) in - debug "F"; (* This needs to run regardless of either's state *) let _ = Option.map !post_block_hook ~f:(fun hook -> hook ()) in - debug "G"; (globals, either) | CatchExpr { subject; capture; catch; pos = _ } -> ( let globals, subject_either = interpret_expr globals subject in diff --git a/lib/interpreter/native.ml b/lib/interpreter/native.ml index c176f404..d55519e1 100644 --- a/lib/interpreter/native.ml +++ b/lib/interpreter/native.ml @@ -58,12 +58,13 @@ module Prod : Native_sig.Sig = struct else Ok () let chdir path = - let open Result.Monad_infix in - (try Ok (Core_unix.chdir path) - with Core_unix.Unix_error (err, _, _) -> - let err_msg = Core_unix.Error.message err in - Error (Printf.sprintf "`chdir(%s)` failed with \"%s\"" path err_msg)) - >>= fun () -> Ok (Filename_unix.realpath path) + try Ok (Core_unix.chdir path) + with Core_unix.Unix_error (err, _, _) -> + let err_msg = Core_unix.Error.message err in + Error (Printf.sprintf "`chdir(%s)` failed with \"%s\"" path err_msg) + + (** TODO make a Result.t *) + let realpath path = Filename_unix.realpath path let mkdir = Core_unix.mkdir ~perm:0o775 @@ -397,8 +398,11 @@ module Make_test () : TestSig = struct this_pid let proc_expectations : Mock_process.spec option ref = ref None - let chdir path = Ok path - (* Note: this will not normalize paths as the real one does *) + let chdir _ = Ok () + (* Note: this function only exists to cause side effects *) + + let realpath _ = "TODO normalize test realpath" + (* TODO: write test normalization algorithm? *) let mkdir path = match Hashtbl.add path_to_entity ~key:path ~data:Directory with diff --git a/lib/interpreter/native_sig.mli b/lib/interpreter/native_sig.mli index a9a09060..ef78572d 100644 --- a/lib/interpreter/native_sig.mli +++ b/lib/interpreter/native_sig.mli @@ -37,8 +37,9 @@ module type Sig = sig string array -> (Runtime.t, string) Result.t - val chdir : string -> (string, string) Result.t + val chdir : string -> (unit, string) Result.t val directory_exists : string -> bool val file_exists : string -> bool val mkdir : string -> unit + val realpath : string -> string end From bcfda7c904104ca851f1666774c47a2638d11034 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 31 Jul 2026 12:55:54 -0700 Subject: [PATCH 6/9] remove debug function --- lib/interpreter/context.ml | 6 ------ lib/interpreter/context.mli | 1 - 2 files changed, 7 deletions(-) diff --git a/lib/interpreter/context.ml b/lib/interpreter/context.ml index abb3d418..ebaf216b 100644 --- a/lib/interpreter/context.ml +++ b/lib/interpreter/context.ml @@ -33,9 +33,3 @@ let reassign t' name v = let tbl = List.hd_exn t' in Hashtbl.change tbl name ~f:(fun _ -> Some v); Some () - -let debug t' to_s = - List.iter t' ~f:(fun tbl -> - Hashtbl.iter_keys tbl ~f:(fun key -> - let value = Hashtbl.find tbl key |> Option.value_exn in - Printf.printf "%s => %s\n" key (to_s value))) diff --git a/lib/interpreter/context.mli b/lib/interpreter/context.mli index 96679fd1..afeb1b15 100644 --- a/lib/interpreter/context.mli +++ b/lib/interpreter/context.mli @@ -5,4 +5,3 @@ val push_empty : 'a t -> 'a t val get : 'a t -> string -> 'a option val bind : 'a t -> string -> 'a -> unit option val reassign : 'a t -> string -> 'a -> unit option -val debug : 'a t -> ('a -> string) -> unit From e14d7676c94112221c36a3c26edf37ab048f0558 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 31 Jul 2026 12:58:32 -0700 Subject: [PATCH 7/9] remove todos --- lib/interpreter/interpret.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/interpreter/interpret.ml b/lib/interpreter/interpret.ml index a7dca300..3d97f464 100644 --- a/lib/interpreter/interpret.ml +++ b/lib/interpreter/interpret.ml @@ -728,7 +728,6 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = | Exit _ -> (globals, either) | Return _ -> (globals, either))) | WithExpr (assignments, block, pos) -> - (* TODO *) let module M = (val globals.l) in let post_block_hook = ref None in let inner_globals = @@ -739,7 +738,6 @@ and interpret_expr globals (expr : Compiler.Optimizer.expr) = These can't be recovered from, we must fail immediately *) let middleware name prev next = - (* TODO: this is wrong, we shouldn't re-assign $cwd here, we do it later; we should have a seperate syscall for realpath, and do it before here *) match name with | "$cwd" -> let cd_wrapper p = From 1580cef4c95071545b403d76ad3e3313b2b8dd09 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 31 Jul 2026 13:01:17 -0700 Subject: [PATCH 8/9] fix revert --- native/native.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/native.ml b/native/native.ml index 37f716fa..f531a3b7 100644 --- a/native/native.ml +++ b/native/native.ml @@ -2,7 +2,7 @@ Check for correctness if switching to Core! *) external file_read_all : string -> string option = "file_read_all" -external chdir : string -> (string, string) Result.t = "_chdir" +external chdir : string -> (unit, string) Result.t = "_chdir" (* val fd_read_all : Core_unix.File_descr.t -> (Runtime.t, string) Result.t val fd_write_all : Core_unix.File_descr.t -> string -> (unit, string) Result.t From 3f28c4e9b6d3c199c99714804e478dfb310d1e39 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 31 Jul 2026 13:07:53 -0700 Subject: [PATCH 9/9] fix --- bin/native_test.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/native_test.ml b/bin/native_test.ml index 2615ce8b..727bc197 100644 --- a/bin/native_test.ml +++ b/bin/native_test.ml @@ -12,4 +12,4 @@ let () = |> map_error (fun msg -> Printf.printf "CD-ing to /nosuchdir returned \"%s\"\n" msg); Sn.Native.chdir "/" - |> map_ok (fun msg -> Printf.printf "CD-ing to / succeeded with \"%s\"." msg) + |> map_ok (fun () -> Printf.printf "CD-ing to / succeeded.\n")