diff --git a/crates/steel-core/src/compiler/compiler.rs b/crates/steel-core/src/compiler/compiler.rs index 956d9841d..f46dc1b29 100644 --- a/crates/steel-core/src/compiler/compiler.rs +++ b/crates/steel-core/src/compiler/compiler.rs @@ -296,6 +296,8 @@ pub struct Compiler { analysis: Analysis, shadowed_variable_renamer: RenameShadowedVariables, + + search_dirs: Vec, } #[derive(Serialize, Deserialize)] @@ -364,6 +366,7 @@ impl Compiler { lifted_macro_environments: HashSet::new(), analysis: Analysis::pre_allocated(), shadowed_variable_renamer: RenameShadowedVariables::default(), + search_dirs: Vec::new(), } } @@ -387,6 +390,7 @@ impl Compiler { lifted_macro_environments: HashSet::new(), analysis: Analysis::pre_allocated(), shadowed_variable_renamer: RenameShadowedVariables::default(), + search_dirs: Vec::new(), } } @@ -426,6 +430,10 @@ impl Compiler { self.module_manager.add_builtin_module(name, contents); } + pub fn add_search_directory(&mut self, dir: PathBuf) { + self.search_dirs.push(dir); + } + pub fn compile_executable_from_expressions( &mut self, exprs: Vec, @@ -549,7 +557,7 @@ impl Compiler { sources: &mut Sources, builtin_modules: ModuleContainer, ) -> Result> { - #[cfg(feature = "modules")] + // #[cfg(feature = "modules")] return self.module_manager.compile_main( &mut self.macro_env, &mut self.kernel, @@ -559,11 +567,12 @@ impl Compiler { builtin_modules, &mut self.lifted_kernel_environments, &mut self.lifted_macro_environments, + &self.search_dirs, ); - #[cfg(not(feature = "modules"))] - self.module_manager - .expand_expressions(&mut self.macro_env, exprs) + // #[cfg(not(feature = "modules"))] + // self.module_manager + // .expand_expressions(&mut self.macro_env, exprs) } fn generate_instructions_for_executable( diff --git a/crates/steel-core/src/compiler/modules.rs b/crates/steel-core/src/compiler/modules.rs index 4ef7c1dd0..44b2252be 100644 --- a/crates/steel-core/src/compiler/modules.rs +++ b/crates/steel-core/src/compiler/modules.rs @@ -192,6 +192,7 @@ impl ModuleManager { builtin_modules, global_macro_map, &self.custom_builtins, + &[], )?; module_builder.compile()?; @@ -212,6 +213,7 @@ impl ModuleManager { builtin_modules: ModuleContainer, lifted_kernel_environments: &mut HashMap, lifted_macro_environments: &mut HashSet, + search_dirs: &[PathBuf], ) -> Result> { // Wipe the visited set on entry self.visited.clear(); @@ -234,6 +236,7 @@ impl ModuleManager { builtin_modules, global_macro_map, &self.custom_builtins, + search_dirs, )?; let mut module_statements = module_builder.compile()?; @@ -1650,6 +1653,7 @@ struct ModuleBuilder<'a> { builtin_modules: ModuleContainer, global_macro_map: &'a FxHashMap, custom_builtins: &'a HashMap, + search_dirs: &'a [PathBuf], } impl<'a> ModuleBuilder<'a> { @@ -1666,6 +1670,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules: ModuleContainer, global_macro_map: &'a FxHashMap, custom_builtins: &'a HashMap, + search_dirs: &'a [PathBuf], ) -> Result { // TODO don't immediately canonicalize the path unless we _know_ its coming from a path // change the path to not always be required @@ -1697,6 +1702,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules, global_macro_map, custom_builtins, + search_dirs, }) } @@ -1875,6 +1881,7 @@ impl<'a> ModuleBuilder<'a> { self.builtin_modules.clone(), self.global_macro_map, self.custom_builtins, + self.search_dirs, )?; // Walk the tree and compile any dependencies @@ -2526,7 +2533,35 @@ impl<'a> ModuleBuilder<'a> { current = home; log::info!("Searching STEEL_HOME for {:?}", current); + + if !current.exists() { + for dir in self.search_dirs { + let mut dir = dir.clone(); + dir.push(s); + + if dir.exists() { + current = dir; + break; + } + } + } } else { + // TODO: Check if this module exists in STEEL_HOME first. If it does, we'll take that as our candidate + // and then continue on to the final module resolution part. + // + // If it doesn't exist, we should iterate through the search directories and attempt to find + // a matching path there. + + for dir in self.search_dirs { + let mut dir = dir.clone(); + dir.push(s); + + if dir.exists() { + current = dir; + break; + } + } + stop!(Generic => format!("Module not found: {:?} with STEEL_HOME: {:?}", current, home); *span) } } @@ -2637,8 +2672,30 @@ impl<'a> ModuleBuilder<'a> { home.push(path); current = home; + if !current.exists() { + for dir in self.search_dirs { + let mut dir = dir.clone(); + dir.push(path); + + if dir.exists() { + current = dir; + break; + } + } + } + log::info!("Searching STEEL_HOME for {:?}", current); } else { + for dir in self.search_dirs { + let mut dir = dir.clone(); + dir.push(path); + + if dir.exists() { + current = dir; + break; + } + } + stop!(Generic => format!("Module not found: {:?}", current); r.location.span) } } @@ -2733,6 +2790,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules, global_macro_map, custom_builtins, + &[], ) .parse_builtin(input) } @@ -2747,6 +2805,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules: ModuleContainer, global_macro_map: &'a FxHashMap, custom_builtins: &'a HashMap, + search_dirs: &'a [PathBuf], ) -> Result { ModuleBuilder::raw( name, @@ -2758,6 +2817,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules, global_macro_map, custom_builtins, + search_dirs, ) .parse_from_path() } @@ -2772,6 +2832,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules: ModuleContainer, global_macro_map: &'a FxHashMap, custom_builtins: &'a HashMap, + search_dirs: &'a [PathBuf], ) -> Self { ModuleBuilder { name, @@ -2791,6 +2852,7 @@ impl<'a> ModuleBuilder<'a> { builtin_modules, global_macro_map, custom_builtins, + search_dirs, } } diff --git a/crates/steel-core/src/steel_vm/engine.rs b/crates/steel-core/src/steel_vm/engine.rs index c6be5dff3..b626b8acc 100644 --- a/crates/steel-core/src/steel_vm/engine.rs +++ b/crates/steel-core/src/steel_vm/engine.rs @@ -1191,6 +1191,15 @@ impl Engine { engine } + /// Adds a directory for the engine to resolve paths from. + /// + /// By default, the engine will search $STEEL_HOME/cogs for modules, + /// but any additional path added this way will increase the module + /// resolution search space. + pub fn add_search_directory(&mut self, dir: PathBuf) { + self.compiler.add_search_directory(dir) + } + pub(crate) fn new_printer() -> Self { let mut engine = fresh_kernel_image(); diff --git a/crates/steel-language-server/src/backend.rs b/crates/steel-language-server/src/backend.rs index 9c56bb055..ed0dc1015 100644 --- a/crates/steel-language-server/src/backend.rs +++ b/crates/steel-language-server/src/backend.rs @@ -647,6 +647,7 @@ fn filter_interned_string_with_char( if !resolved.starts_with("#") && !resolved.starts_with("%") + && !resolved.starts_with("mangler") && !resolved.starts_with("mangler#%") && !resolved.starts_with("!!dummy-rest") && !resolved.starts_with("__module-mangler") diff --git a/crates/steel-language-server/src/main.rs b/crates/steel-language-server/src/main.rs index b691b4315..b281fe137 100644 --- a/crates/steel-language-server/src/main.rs +++ b/crates/steel-language-server/src/main.rs @@ -54,6 +54,7 @@ async fn main() { if !resolved.starts_with("#") && !resolved.starts_with("%") && !resolved.starts_with("mangler#%") + && !resolved.starts_with("mangler") { defined_globals.insert(resolved.to_string()); } diff --git a/crates/steel-repl/src/repl.rs b/crates/steel-repl/src/repl.rs index 1a5ab6fdb..59e36b592 100644 --- a/crates/steel-repl/src/repl.rs +++ b/crates/steel-repl/src/repl.rs @@ -95,6 +95,11 @@ pub fn repl_base(mut vm: Engine) -> std::io::Result<()> { .bright_yellow() .bold() ); + + #[cfg(target_os = "windows")] + let mut prompt = String::from("λ > "); + + #[cfg(not(target_os = "windows"))] let mut prompt = format!("{}", "λ > ".bright_green().bold().italic()); let mut rl = Editor::::new().expect("Unable to instantiate the repl!"); diff --git a/crates/steel-webrequests/src/lib.rs b/crates/steel-webrequests/src/lib.rs index 51662a897..bf463f17d 100644 --- a/crates/steel-webrequests/src/lib.rs +++ b/crates/steel-webrequests/src/lib.rs @@ -13,21 +13,30 @@ fn create_module() -> FFIModule { module .register_fn("get", get) + .register_fn("post", post) + .register_fn("put", put) + .register_fn("delete", delete) + .register_fn("patch", patch) + .register_fn("head", head) + .register_fn("set-header!", BlockingRequest::set) + .register_fn("set-query-parameter!", BlockingRequest::query) + .register_fn("set-timeout/ms!", BlockingRequest::timeout_ms) .register_fn( "call", |request: BlockingRequest| -> Result { - Request::call(request.0) + Request::call(request.0.unwrap()) .map(|x| x.into()) .map_err(BlockingError::Ureq) }, ) + .register_fn("call-with-json", BlockingRequest::call_with_json) .register_fn("response->text", SteelResponse::into_text); module } #[derive(Clone)] -struct BlockingRequest(Request); +struct BlockingRequest(Option); struct BlockingResponse(Response); enum BlockingError { @@ -40,7 +49,53 @@ impl Custom for BlockingResponse {} impl Custom for BlockingError {} fn get(url: String) -> BlockingRequest { - BlockingRequest(ureq::get(&url)) + BlockingRequest(Some(ureq::get(&url))) +} + +fn post(url: String) -> BlockingRequest { + BlockingRequest(Some(ureq::post(&url))) +} + +fn put(url: String) -> BlockingRequest { + BlockingRequest(Some(ureq::put(&url))) +} + +fn delete(url: String) -> BlockingRequest { + BlockingRequest(Some(ureq::delete(&url))) +} + +fn patch(url: String) -> BlockingRequest { + BlockingRequest(Some(ureq::patch(&url))) +} + +fn head(url: String) -> BlockingRequest { + BlockingRequest(Some(ureq::head(&url))) +} + +impl BlockingRequest { + fn query(&mut self, parameter: String, value: String) { + self.0 = Some(self.0.take().unwrap().query(¶meter, &value)); + } + + fn set(&mut self, header: String, value: String) { + self.0 = Some(self.0.take().unwrap().set(&header, &value)); + } + + // TODO: Add FFI conversion form u64 as well + fn timeout_ms(&mut self, time_in_ms: usize) { + self.0 = Some( + self.0 + .take() + .unwrap() + .timeout(std::time::Duration::from_millis(time_in_ms as u64)), + ); + } + + fn call_with_json(&mut self, json: String) -> Result { + Request::send_json(self.0.clone().unwrap(), json) + .map(|x| x.into()) + .map_err(BlockingError::Ureq) + } } struct SteelResponse { diff --git a/crates/steel-webrequests/webrequests.scm b/crates/steel-webrequests/webrequests.scm index e5c06f005..854b6d100 100644 --- a/crates/steel-webrequests/webrequests.scm +++ b/crates/steel-webrequests/webrequests.scm @@ -1,5 +1,43 @@ -(#%require-dylib "libsteel_webrequests" (only-in get call response->text)) +(#%require-dylib "libsteel_webrequests" + (only-in get + post + put + delete + patch + head + call + call-with-json + response->text + set-query-parameter! + set-header! + set-timeout/ms!)) (provide get + post + put + delete + patch + head + call-with-json-body + set-query-parameter! + set-header! + set-timeout/ms! call - response->text) + response->text + response->json + with-query-parameter + with-header) + +(define (response->json resp) + (string->jsexpr (response->text resp))) + +(define (with-query-parameter req) + (set-query-parameter! req) + req) + +(define (with-header req) + (set-header! req) + req) + +(define (call-with-json-body req json) + (call-with-json req (value->jsexpr-string json)))