From c05b94e92a641c88954406fa93ea0e69b21956f5 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Thu, 12 Oct 2023 20:50:27 -0700 Subject: [PATCH] fall back on shell command behaviour for undefined variables Signed-off-by: Ava Hahn --- Cargo.toml | 1 + Shell.org | 24 +++++++++++++++++++ src/lib.rs | 2 ++ src/stl/posix.rs | 12 ++++++---- src/sym.rs | 60 ++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2efea6d..656f333 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ phf = { version = "0.11", default-features = false, features = ["macros"] } [features] default = ["posix"] posix = ["dep:nix", "dep:libc"] +implicit-load = ["posix"] diff --git a/Shell.org b/Shell.org index 0437761..2b39ade 100644 --- a/Shell.org +++ b/Shell.org @@ -170,3 +170,27 @@ Or: (pacman-search "xfce4") #+END_EXAMPLE + +* Implicit Load +Typically, any call to an undefined function, lambda, or symbol will result in an undefined symbol error. For example, here is a call to GCC without explicitly prepending the ~load~ function: +#+BEGIN_EXAMPLE + > (gcc myfile.c -o myfile) + + ** ERROR TRACEBACK + > gdb: (is an undefined symbol) + Please refactor forms and try again... +#+END_EXAMPLE + +With the ~implicit load~ feature any call to an undefined function will trigger the ~load~ function as if it had been there the whole time. The above example would yield a new subprocess running GCC (assuming it is installed and accessible). This allows users to quickly type shell commands at the prompt as if they were using a standard shell. Any reference to an undefined symbol outside of where a function would be will still cause an error. + +#+BEGIN_EXAMPLE + > ls -la .config + + ...... (redacted directory list) ..... +#+END_EXAMPLE + +Implicit load can be used by building relish with the following command: + +#+BEGIN_EXAMPLE +cargo build -F implicit-load +#+END_EXAMPLE diff --git a/src/lib.rs b/src/lib.rs index 2a6f2c9..cf090f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,4 +57,6 @@ pub mod aux { pub use crate::stl::posix::ShellState; #[cfg(feature="posix")] pub use crate::stl::posix::check_jobs; + #[cfg(feature="posix")] + pub use crate::stl::posix::POSIX_LOAD_NAME; } diff --git a/src/stl/posix.rs b/src/stl/posix.rs index e2a8cdf..de637d0 100644 --- a/src/stl/posix.rs +++ b/src/stl/posix.rs @@ -60,6 +60,8 @@ use { }, }; +pub const POSIX_LOAD_NAME: &str = "load"; + pub struct ShellState { pub parent_pid: Pid, pub parent_sid: Pid, @@ -190,7 +192,7 @@ fn launch_command( .stdin(infd) .stdout(outfd) .stderr(errfd) - .process_group(0) + .process_group(0) .spawn(); } @@ -273,7 +275,7 @@ const LOAD_DOCSTRING: &str = "Calls a binary off disk with given arguments. Arguments may be of any type except lambda. If a symbol is not defined it will be passed as a string. first argument (command name) will be found on path (or an error returned). -examples: +examples:call (l ping google.com) (let ((ping-count 4)) (l ping -c ping-count google.com)) @@ -787,7 +789,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc syms.insert( String::from("l"), Symbol { - name: String::from("load"), + name: String::from(POSIX_LOAD_NAME), args: Args::Infinite, conditional_branches: true, docs: String::from(LOAD_DOCSTRING), @@ -799,9 +801,9 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc ); syms.insert( - String::from("load"), + String::from(POSIX_LOAD_NAME), Symbol { - name: String::from("load"), + name: String::from(POSIX_LOAD_NAME), args: Args::Infinite, conditional_branches: true, docs: String::from(LOAD_DOCSTRING), diff --git a/src/sym.rs b/src/sym.rs index adc456e..bd3f497 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -18,6 +18,7 @@ use crate::eval::eval; use crate::error::{Traceback, start_trace}; use crate::segment::{Ctr, Seg, Type}; +use crate::stl::posix::POSIX_LOAD_NAME; use std::collections::HashMap; use std::fmt; use std::rc::Rc; @@ -129,8 +130,42 @@ impl SymTable { args: &Seg, call_func: bool, ) -> Result, Traceback> { + let outer_scope_seg: Seg; + let mut call_args = args; + let mut name_token = name.to_string(); let mut symbol = match self.remove(name) { Some(s) => s, + + /* implicit load: + * on a call to an undefined function + * assume a shell command is being run + */ + None if cfg!(feature="implicit-load") + && call_func => match self.remove(&POSIX_LOAD_NAME.to_string()) { + Some(s) => { + name_token = String::from(POSIX_LOAD_NAME); + /* highly unfortunate circumstance + * we must now rebuild the original ast + * costs a whole clone of the args + * this feature is non-standard + */ + outer_scope_seg = Seg::from( + Box::from(Ctr::Symbol(name.to_string())), + if let Ctr::None = *args.car { + Box::from(Ctr::None) + } else { + Box::from(Ctr::Seg(args.clone())) + }, + ); + call_args = &outer_scope_seg; + s + }, + None => return Err( + Traceback::new() + .with_trace(("(implicit load)", "(load function not found)").into()) + ) + }, + None => return Err( Traceback::new() .with_trace((name, "(is an undefined symbol)").into()) @@ -139,7 +174,7 @@ impl SymTable { // will re-increment when inserted // but we dont want to increment it symbol.__generation -= 1; - self.insert(name.to_string(), symbol.clone()); + self.insert(name_token, symbol.clone()); if let ValueType::VarForm(ref val) = symbol.value { match **val { Ctr::Lambda(ref l) if call_func => { @@ -165,7 +200,7 @@ impl SymTable { } } if call_func { - symbol.call(args, self) + symbol.call(call_args, self) } else { // its a function but call_func is off Ok(Box::new(Ctr::Symbol(name.to_string()))) @@ -173,14 +208,19 @@ impl SymTable { } pub fn is_function_type(&self, name: &String) -> Option { - if let ValueType::VarForm(ref val) = self.get(name)?.value { - match **val { - Ctr::Lambda(_) => Some(true), - Ctr::Symbol(ref n) => self.is_function_type(n), - _ => Some(false), - } - } else { - Some(true) + match self.get(name) { + /* implicit-load: assume you just drew load function */ + None if cfg!(feature="implicit-load") => Some(true), + Some(value) => if let ValueType::VarForm(ref val) = value.value { + match **val { + Ctr::Lambda(_) => Some(true), + Ctr::Symbol(ref n) => self.is_function_type(n), + _ => Some(false), + } + } else { + Some(true) + }, + None => None, } } }