diff --git a/Makefile b/Makefile index 9824bc4..8ebe63b 100644 --- a/Makefile +++ b/Makefile @@ -101,8 +101,8 @@ generate: go generate ./... aot: gocmd $(STDLIB-TARGETS) - GLOJURE_USE_AOT=false \ - GLOJURE_STDLIB_PATH=./pkg/stdlib \ + GLJ_USE_AOT=false \ + GLJPATH=./pkg/stdlib \ $(GO-CMD) run -tags glj_no_aot_stdlib ./cmd/glj \ <<<"(map compile '[$(AOT-NAMESPACES)])" diff --git a/pkg/gen/gljimports/gljimports_darwin_amd64.go b/pkg/gen/gljimports/gljimports_darwin_amd64.go index 2639ccb..1361d70 100644 --- a/pkg/gen/gljimports/gljimports_darwin_amd64.go +++ b/pkg/gen/gljimports/gljimports_darwin_amd64.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gen/gljimports/gljimports_darwin_arm64.go b/pkg/gen/gljimports/gljimports_darwin_arm64.go index e2d00c5..fbe5127 100644 --- a/pkg/gen/gljimports/gljimports_darwin_arm64.go +++ b/pkg/gen/gljimports/gljimports_darwin_arm64.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gen/gljimports/gljimports_js_wasm.go b/pkg/gen/gljimports/gljimports_js_wasm.go index ce69685..502d41b 100644 --- a/pkg/gen/gljimports/gljimports_js_wasm.go +++ b/pkg/gen/gljimports/gljimports_js_wasm.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gen/gljimports/gljimports_linux_amd64.go b/pkg/gen/gljimports/gljimports_linux_amd64.go index 70b5f45..4dfac11 100644 --- a/pkg/gen/gljimports/gljimports_linux_amd64.go +++ b/pkg/gen/gljimports/gljimports_linux_amd64.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gen/gljimports/gljimports_linux_arm64.go b/pkg/gen/gljimports/gljimports_linux_arm64.go index 2d834e7..6838e96 100644 --- a/pkg/gen/gljimports/gljimports_linux_arm64.go +++ b/pkg/gen/gljimports/gljimports_linux_arm64.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gen/gljimports/gljimports_windows_amd64.go b/pkg/gen/gljimports/gljimports_windows_amd64.go index f76aa59..d36a743 100644 --- a/pkg/gen/gljimports/gljimports_windows_amd64.go +++ b/pkg/gen/gljimports/gljimports_windows_amd64.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gen/gljimports/gljimports_windows_arm.go b/pkg/gen/gljimports/gljimports_windows_arm.go index 5c5de12..09ecaf6 100644 --- a/pkg/gen/gljimports/gljimports_windows_arm.go +++ b/pkg/gen/gljimports/gljimports_windows_arm.go @@ -3999,6 +3999,7 @@ func RegisterImports(_register func(string, interface{})) { _register("github.com/glojurelang/glojure/pkg/runtime.*Fn", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Fn)(nil))) _register("github.com/glojurelang/glojure/pkg/runtime.Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil)).Elem()) _register("github.com/glojurelang/glojure/pkg/runtime.*Generator", reflect.TypeOf((*github_com_glojurelang_glojure_pkg_runtime.Generator)(nil))) + _register("github.com/glojurelang/glojure/pkg/runtime.GetLoadPath", github_com_glojurelang_glojure_pkg_runtime.GetLoadPath) _register("github.com/glojurelang/glojure/pkg/runtime.GetNSLoader", github_com_glojurelang_glojure_pkg_runtime.GetNSLoader) _register("github.com/glojurelang/glojure/pkg/runtime.GetUseAOT", github_com_glojurelang_glojure_pkg_runtime.GetUseAOT) _register("github.com/glojurelang/glojure/pkg/runtime.NewEnvironment", github_com_glojurelang_glojure_pkg_runtime.NewEnvironment) diff --git a/pkg/gljmain/gljmain.go b/pkg/gljmain/gljmain.go index a199c2d..f69fc9e 100644 --- a/pkg/gljmain/gljmain.go +++ b/pkg/gljmain/gljmain.go @@ -38,8 +38,6 @@ For more information, visit: https://github.com/glojurelang/glojure } func Main(args []string) { - runtime.AddLoadPath(os.DirFS(".")) - if len(args) == 0 { repl.Start() } else if args[0] == "--version" { diff --git a/pkg/runtime/codegen_test.go b/pkg/runtime/codegen_test.go index 0bf53a1..f7e15d0 100644 --- a/pkg/runtime/codegen_test.go +++ b/pkg/runtime/codegen_test.go @@ -19,7 +19,7 @@ import ( func TestCodegen(t *testing.T) { if runtime.GetUseAOT() { - t.Skip("Skipping codegen tests with AOT enabled; run with GLOJURE_USE_AOT=0") + t.Skip("Skipping codegen tests with AOT enabled; run with GLJ_USE_AOT=0") } var testFiles []string diff --git a/pkg/runtime/envinit.go b/pkg/runtime/envinit.go index 9d564e2..67e388d 100644 --- a/pkg/runtime/envinit.go +++ b/pkg/runtime/envinit.go @@ -145,6 +145,17 @@ func NewEnvironment(opts ...EvalOption) lang.Environment { versionVar.BindRoot(parseVersion(Version)) } + // Set the load path + loadPathVar := core.FindInternedVar(lang.NewSymbol("*load-path*")) + if loadPathVar != nil { + pathStrings := GetLoadPath() + pathValues := make([]any, len(pathStrings)) + for i, path := range pathStrings { + pathValues[i] = path + } + loadPathVar.BindRoot(lang.NewVector(pathValues...)) + } + lang.InternVar(core, lang.NewSymbol("load-file"), func(filename string) any { buf, err := os.ReadFile(filename) if err != nil { diff --git a/pkg/runtime/environment.go b/pkg/runtime/environment.go index 8bf9586..ced44e6 100644 --- a/pkg/runtime/environment.go +++ b/pkg/runtime/environment.go @@ -64,6 +64,7 @@ func newEnvironment(ctx context.Context, stdout, stderr io.Writer) *environment "print-dup", "read-eval", "glojure-version", + "load-path", } { coreNS.InternWithValue(lang.NewSymbol("*"+dyn+"*"), nil, true).SetDynamic() } diff --git a/pkg/runtime/loadpath_test.go b/pkg/runtime/loadpath_test.go new file mode 100644 index 0000000..fc5c74b --- /dev/null +++ b/pkg/runtime/loadpath_test.go @@ -0,0 +1,69 @@ +package runtime_test + +import ( + "os" + "strings" + "testing" + + "github.com/glojurelang/glojure/pkg/runtime" +) + +func TestGetLoadPath(t *testing.T) { + // Test that GetLoadPath returns a valid slice of strings + paths := runtime.GetLoadPath() + if len(paths) == 0 { + t.Fatal("GetLoadPath returned empty slice") + } + + // Should contain at least the current directory and stdlib + foundCurrent := false + foundStdlib := false + for _, path := range paths { + if path == "." { + foundCurrent = true + } + if path == "" || strings.Contains(path, "stdlib") { + foundStdlib = true + } + } + + if !foundCurrent { + t.Error("Load path should contain current directory '.'") + } + if !foundStdlib { + t.Error("Load path should contain stdlib") + } +} + +func TestGLJPATHEnvironmentVariable(t *testing.T) { + // This test would need to be run in a subprocess to avoid affecting + // the global loadPath, but for now we'll just verify the structure + paths := runtime.GetLoadPath() + + // Verify all paths are strings + for i, path := range paths { + if path == "" { + t.Errorf("Path at index %d is empty", i) + } + } + + // The expected order is: + // [...GLJPATH dirs, current dir, ] + if len(paths) > 0 { + // Last element should always be + last := paths[len(paths)-1] + if last != "" { + t.Errorf("Last path should be '', got: %s", last) + } + + // If no GLJPATH, order should be [current dir, ] + if os.Getenv("GLJPATH") == "" { + if len(paths) >= 2 { + secondToLast := paths[len(paths)-2] + if secondToLast != "." { + t.Errorf("Second to last path should be '.', got: %s", secondToLast) + } + } + } + } +} \ No newline at end of file diff --git a/pkg/runtime/rtcompat.go b/pkg/runtime/rtcompat.go index 3e74f76..22004f7 100644 --- a/pkg/runtime/rtcompat.go +++ b/pkg/runtime/rtcompat.go @@ -29,18 +29,32 @@ var ( useAot = func() bool { // default to true - gua := strings.ToLower(os.Getenv("GLOJURE_USE_AOT")) + gua := strings.ToLower(os.Getenv("GLJ_USE_AOT")) return !(gua == "0" || gua == "false" || gua == "no" || gua == "off") }() ) func init() { - stdlibPath := os.Getenv("GLOJURE_STDLIB_PATH") - if stdlibPath != "" { - AddLoadPath(os.DirFS(stdlibPath)) - } else { - AddLoadPath(stdlib.StdLib) + // Start with current directory + AddLoadPath(os.DirFS(".")) + + // Add GLJPATH directories if set + gljPath := os.Getenv("GLJPATH") + if gljPath != "" { + paths := strings.Split(gljPath, ":") + for i := len(paths) - 1; i >= 0; i-- { + path := paths[i] + if path != "" { + // Prepend each path so they appear in the correct order + loadPathLock.Lock() + loadPath = append([]fs.FS{os.DirFS(path)}, loadPath...) + loadPathLock.Unlock() + } + } } + + // Always add at the end + AddLoadPath(stdlib.StdLib) } func GetUseAOT() bool { @@ -55,6 +69,28 @@ func AddLoadPath(fs fs.FS) { loadPath = append(loadPath, fs) } +// GetLoadPath returns the current load path as a slice of strings. +// This is used to expose the load path to Clojure code via the *load-path* dynamic variable. +func GetLoadPath() []string { + loadPathLock.Lock() + defer loadPathLock.Unlock() + + result := make([]string, len(loadPath)) + for i, fs := range loadPath { + // Convert fs.FS to string representation + // os.DirFS returns a string type under the hood + if reflect.TypeOf(fs).Kind() == reflect.String { + result[i] = fmt.Sprintf("%s", fs) + } else if fs == stdlib.StdLib { + // For embedded filesystem (stdlib), use a special marker + result[i] = "" + } else { + result[i] = fmt.Sprintf("<%T>", fs) + } + } + return result +} + // RT is a struct with methods that map to Clojure's RT class' static // methods. This approach is used to make translation of core.clj to // Glojure easier. @@ -172,6 +208,7 @@ func (rt *RTMethods) Load(scriptBase string) { loadPathLock.Lock() lp := loadPath loadPathLock.Unlock() + found := false for _, fs := range lp { for _, ext := range []string{".glj", ".cljc"} { testFilename := scriptBase + ext @@ -180,9 +217,13 @@ func (rt *RTMethods) Load(scriptBase string) { buf = b foundFS = fs filename = testFilename + found = true break } } + if found { + break + } } if filename == "" { panic(fmt.Errorf("failed to load %s: not found in load path", scriptBase)) diff --git a/pkg/stdlib/clojure/core/loader.go b/pkg/stdlib/clojure/core/loader.go index 7efe4bd..cddf6a6 100644 --- a/pkg/stdlib/clojure/core/loader.go +++ b/pkg/stdlib/clojure/core/loader.go @@ -68,6 +68,7 @@ func LoadNS() { sym__STAR_flush_DASH_on_DASH_newline_STAR_ := lang.NewSymbol("*flush-on-newline*") sym__STAR_glojure_DASH_version_STAR_ := lang.NewSymbol("*glojure-version*") sym__STAR_in_STAR_ := lang.NewSymbol("*in*") + sym__STAR_load_DASH_path_STAR_ := lang.NewSymbol("*load-path*") sym__STAR_loaded_DASH_libs_STAR_ := lang.NewSymbol("*loaded-libs*") sym__STAR_loading_DASH_verbosely_STAR_ := lang.NewSymbol("*loading-verbosely*") sym__STAR_ns_STAR_ := lang.NewSymbol("*ns*") @@ -1465,6 +1466,8 @@ func LoadNS() { var_clojure_DOT_core__STAR_glojure_DASH_version_STAR_ := lang.InternVarName(sym_clojure_DOT_core, sym__STAR_glojure_DASH_version_STAR_) // var clojure.core/*in* var_clojure_DOT_core__STAR_in_STAR_ := lang.InternVarName(sym_clojure_DOT_core, sym__STAR_in_STAR_) + // var clojure.core/*load-path* + var_clojure_DOT_core__STAR_load_DASH_path_STAR_ := lang.InternVarName(sym_clojure_DOT_core, sym__STAR_load_DASH_path_STAR_) // var clojure.core/*loaded-libs* var_clojure_DOT_core__STAR_loaded_DASH_libs_STAR_ := lang.InternVarName(sym_clojure_DOT_core, sym__STAR_loaded_DASH_libs_STAR_) // var clojure.core/*loading-verbosely* @@ -3133,6 +3136,14 @@ func LoadNS() { var_clojure_DOT_core__STAR_glojure_DASH_version_STAR_.SetMeta(tmp0.Meta().(lang.IPersistentMap)) } } + // *load-path* + { + tmp0 := sym__STAR_load_DASH_path_STAR_.WithMeta(lang.NewMap()).(*lang.Symbol) + var_clojure_DOT_core__STAR_load_DASH_path_STAR_ = ns.InternWithValue(tmp0, lang.NewVector("./pkg/stdlib", ".", ""), true) + if tmp0.Meta() != nil { + var_clojure_DOT_core__STAR_load_DASH_path_STAR_.SetMeta(tmp0.Meta().(lang.IPersistentMap)) + } + } // *loaded-libs* { tmp0 := sym__STAR_loaded_DASH_libs_STAR_.WithMeta(lang.NewMap(kw_dynamic, true, kw_file, "clojure/core.glj", kw_line, int(5809), kw_column, int(10), kw_end_DASH_line, int(5812), kw_end_DASH_column, int(15), kw_ns, lang.FindOrCreateNamespace(sym_clojure_DOT_core))).(*lang.Symbol)