From e8cf59b7a7067a39cd3c1f311ee12c45e179e835 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 28 Mar 2023 20:47:55 -0700 Subject: [PATCH] implemented load-with.... still need docstring and to fix some write errors --- Readme.org | 5 +- src/stl/posix.rs | 167 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 156 insertions(+), 16 deletions(-) diff --git a/Readme.org b/Readme.org index 12c4511..9df6fed 100644 --- a/Readme.org +++ b/Readme.org @@ -103,9 +103,10 @@ Like the *if form*, if the conditional returns a non-boolean value the *while lo **** Let *Let* is one of the most powerful forms Relish offers. The first body in a call to let is a list of lists. -Specifically, a list of variable declarations that lookf like this: ~(name value)~. +Specifically, a list of variable declarations that look like this: ~(name value)~. + Each successive variable definition can build off of the last one, like this: ~((step1 "hello") (step2 (concat step1 " ")) (step3 (concat step2 "world")))~. -In said example, the resulting value of step3 is "hello world". After the variable declaration list, the next for is one or more unevaluated trees of code to be evaluated. +In said example, the resulting value of step3 is "hello world". After the variable declaration list, the next form is one or more unevaluated trees of code to be evaluated. Here is an example of a complete let statement using hypothetical data and methods: #+BEGIN_SRC lisp diff --git a/src/stl/posix.rs b/src/stl/posix.rs index e7ac439..4aac90b 100644 --- a/src/stl/posix.rs +++ b/src/stl/posix.rs @@ -22,6 +22,7 @@ use crate::run; use std::collections::VecDeque; use std::cell::RefCell; use std::rc::Rc; +use std::fs::File; use std::process::{Command, Child, Stdio}; use nix::unistd; use ctrlc; @@ -137,26 +138,148 @@ examples: (l ping -c ping-count google.com)) (l emacs -nw (concat HOME '/.relishrc')) "; - - fn load_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result { if ast.is_empty() { Err("need at least one argument".to_string()) } else { let mut args = VecDeque::from(args_from_ast(ast, syms)); - if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) { - launch_command( - filepath, - &Vec::from(args.make_contiguous()), - Stdio::inherit(), - Stdio::inherit(), - Stdio::inherit(), - false, - state, - )?; - Ok(Ctr::Integer(state.last_exit_code.into())) + if args.is_empty() { + Err("empty command".to_string()) } else { - Err("file not found".to_string()) + if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) { + launch_command( + filepath, + &Vec::from(args.make_contiguous()), + Stdio::inherit(), + Stdio::inherit(), + Stdio::inherit(), + false, + state, + )?; + Ok(Ctr::Integer(state.last_exit_code.into())) + } else { + Err("file not found".to_string()) + } + } + } +} + +// TODO: rework this callback to reduce spaghett +const LOAD_WITH_DOCSTRING: &str = ""; +fn load_with_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result { + if ast.len() != 2 { + Err("exactly two arguments needed".to_string()) + } else { + if let Ctr::Seg(ref fd_redirect_forms) = *ast.car { + let mut stdout: Option = None; + let mut stdin: Option = None; + let mut stderr: Option = None; + let mut e: Option = None; + if !fd_redirect_forms.circuit(&mut |arg: &Ctr| -> bool { + // of the form Seg(Str(fd), Seg(Str(filepath))) + if let Ctr::Seg(ref io_redir) = arg { + if let Ctr::String(ref fd) = *io_redir.car { + if let Ctr::Seg(ref io_file) = *io_redir.cdr { + if let Ctr::String(ref filename) = *io_file.car { + match fd.as_str() { + "stdin" => { + if !stdin.is_none() { + e = Some(format!("stdin already defined")); + return false + } + let input = File::open(filename); + if !input.is_ok() { + e = Some(format!("input file does not exist")); + return false + } + stdin = Some(Stdio::from(input.ok().unwrap())); + return true + }, + "stdout" => { + if !stdout.is_none() { + e = Some(format!("stdout already defined")); + return false + } + + let out = File::open(filename).or(File::create(filename)); + if !out.is_ok() { + e = Some(out.unwrap_err().to_string()); + return false + } + stdout = Some(Stdio::from(out.ok().unwrap())); + return true + }, + "stderr" => { + if !stderr.is_none() { + e = Some(format!("stderr already defined")); + return false + } + + let err = File::open(filename).or(File::create(filename)); + if !err.is_ok() { + e = Some(err.unwrap_err().to_string()); + return false + } + stderr = Some(Stdio::from(err.ok().unwrap())); + return true + }, + _ => { + e = Some(format!("can only override stdin, stdout, or stderr")); + false + } + } + } else { + e = Some(format!("{} is not a string", *io_file.car)); + false + } + } else { + e = Some(format!("fd override must have both an fd and a filename")); + false + } + } else { + e = Some(format!("{} is not a string", *io_redir.car)); + false + } + } else { + e = Some(format!("not a list: {}", arg)); + false + } + }) { + return Err(e.unwrap()) + } + + // now deal with the actual command + if let Ctr::Seg(ref command_forms) = *ast.cdr { + let mut args: VecDeque; + if let Ctr::Seg(ref command) = *command_forms.car { + args = VecDeque::from(args_from_ast(command, syms)); + } else { + return Err("command not list".to_string()) + } + + if args.is_empty() { + Err("empty command".to_string()) + } else { + if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) { + launch_command( + filepath, + &Vec::from(args.make_contiguous()), + stdin.or(Some(Stdio::inherit())).unwrap(), + stdout.or(Some(Stdio::inherit())).unwrap(), + stderr.or(Some(Stdio::inherit())).unwrap(), + false, + state, + )?; + Ok(Ctr::Integer(state.last_exit_code.into())) + } else { + Err("file not found".to_string()) + } + } + } else { + Err("second argument expected to be a list of command elements".to_string()) + } + } else { + Err("first argument expected to be a list of lists".to_string()) } } } @@ -215,6 +338,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc let load_ss = shell_state.clone(); let bg_ss = shell_state.clone(); let q_ss = shell_state.clone(); + let lw_ss = shell_state.clone(); syms.insert( String::from("l"), @@ -272,6 +396,21 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc }, ); + syms.insert( + String::from("load-with"), + Symbol { + name: String::from("load-with"), + args: Args::Lazy(2), + conditional_branches: true, + docs: String::from(LOAD_WITH_DOCSTRING), + value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result { + load_with_callback(ast, symtable, &mut lw_ss.clone().borrow_mut()) + })), + ..Default::default() + }, + ); + + if let Err(e) = ctrlc::set_handler(move || println!("POSIX layer caught SIG-something")) { eprintln!("WARNING: couldn't set sig handler: {}", e); }