From 3f75157fac71f012f252baca9fcc9c80218b07bf Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Mon, 20 Mar 2023 19:00:30 -0700 Subject: [PATCH] got all the load script stuff done. added script args to main shell also added userlib tests to ci --- .gitlab-ci.yml | 5 +++ Readme.org | 13 ++----- src/bin/main.rs | 47 +++++++++++++++-------- src/lib.rs | 7 +--- src/{config.rs => run.rs} | 80 ++++++++++++++++++++++++++++++++------- src/stl.rs | 13 +++++++ src/sym.rs | 6 +-- 7 files changed, 122 insertions(+), 49 deletions(-) rename src/{config.rs => run.rs} (54%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7eb6215..36d55a3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,3 +10,8 @@ unit-tests: stage: test script: - cargo test + +userlib-tests: + stage: test + script: + - cargo run snippets/userlib.rls snippets/userlib-tests.rls diff --git a/Readme.org b/Readme.org index 2f2526b..e9408fa 100644 --- a/Readme.org +++ b/Readme.org @@ -477,18 +477,12 @@ Note: this section will not show the status of each item unless you are viewing Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered. ** TODO Pre-alpha tasks -- Load (load a script) function - - Pull/Refactor the logic out of the configure functions. - - Optionally return a list of new variables and/or functions? - - Will need a concatenate function for tables - Shell prompt is fully configurable (History, L, R, and Delim) - userlib list contains -- Extend userlib tests -- Add userlib tests to CI - Input function -- Lex function -- Read function (Input + Lex) -- get type function +- Lex function +- Read function (Input + Lex) +- get type function - Shell module - Only loadable via POSIX config var - Overload Load function to hook into this lib @@ -501,7 +495,6 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt - Readme documentation for POSIX module - logging library - make const all the error messages -- Main shell calls Load function on arg and exits - Should globals be immutable? ** TODO alpha tasks diff --git a/src/bin/main.rs b/src/bin/main.rs index 0bda1b9..53cd61c 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -16,8 +16,7 @@ */ use nu_ansi_term::{Color, Style}; use dirs::home_dir; -use relish::ast::{eval, lex, Ctr, Seg, SymTable}; -use relish::aux::configure; +use relish::ast::{eval, lex, Ctr, Seg, SymTable, run, load_defaults, load_environment}; use relish::stdlib::{dynamic_stdlib, static_stdlib}; use reedline::{ FileBackedHistory, DefaultHinter, DefaultValidator, Reedline, Signal, @@ -67,14 +66,43 @@ impl Prompt for CustomPrompt<'_> { } } -fn main() -> ! { +fn main() { const HIST_FILE: &str = "/.relish_hist"; const CONFIG_FILE_DEFAULT: &str = "/.relishrc"; + // default config file dirs let home_dir = home_dir().unwrap().to_str().unwrap().to_owned(); let hist_file_name = home_dir.clone() + HIST_FILE; let cfg_file_name = home_dir + CONFIG_FILE_DEFAULT; + // setup symtable + let mut syms = SymTable::new(); + load_defaults(&mut syms); + load_environment(&mut syms); + static_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); + dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); + + // if there are args those are scripts, run them and exit + if env::args().count() > 1 { + let mut iter = env::args(); + iter.next(); + for i in iter { + run(i, &mut syms).unwrap(); + } + + return + } + + // this is a user shell. attempt to load configuration + { + // scope the below borrow of syms + let cfg_file = env::var("RELISH_CFG_FILE").unwrap_or(cfg_file_name); + run(cfg_file.clone(), &mut syms) + .unwrap_or_else(|err: String| eprintln!("failed to load script {}\n{}", cfg_file, err)); + } + dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); + + // setup readline let mut rl = Reedline::create(); let maybe_hist: Box; if !hist_file_name.is_empty() { @@ -82,23 +110,12 @@ fn main() -> ! { .expect("error reading history!")); rl = rl.with_history(maybe_hist); } - rl = rl.with_hinter(Box::new( DefaultHinter::default() .with_style(Style::new().italic().fg(Color::LightGray)), )).with_validator(Box::new(DefaultValidator)); - let mut syms = SymTable::new(); - static_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); - dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); - { - // scope the below borrow of syms - let cfg_file = env::var("RELISH_CFG_FILE").unwrap_or(cfg_file_name); - configure(cfg_file.clone(), &mut syms) - .unwrap_or_else(|err: String| eprintln!("failed to load script {}\n{}", cfg_file, err)); - } - dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); - + // repl :) loop { let s = *syms .call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true) diff --git a/src/lib.rs b/src/lib.rs index d526798..e88b5f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -mod config; +mod run; mod eval; mod lex; mod segment; @@ -23,6 +23,7 @@ mod stl; mod sym; pub mod ast { + pub use crate::run::{run, load_defaults, load_environment}; pub use crate::eval::eval; pub use crate::lex::lex; pub use crate::segment::{Ctr, Seg, Type}; @@ -32,7 +33,3 @@ pub mod ast { pub mod stdlib { pub use crate::stl::{dynamic_stdlib, static_stdlib}; } - -pub mod aux { - pub use crate::config::configure; -} diff --git a/src/config.rs b/src/run.rs similarity index 54% rename from src/config.rs rename to src/run.rs index 23ad8dd..e9adfee 100644 --- a/src/config.rs +++ b/src/run.rs @@ -19,16 +19,24 @@ use crate::eval::eval; use crate::lex::lex; use crate::segment::{Ctr, Seg}; use crate::sym::{Args, SymTable, Symbol, ValueType}; +use std::path::Path; use std::fs; -use std::io; +use std::iter::FromIterator; +use std::env::{vars, var, current_dir}; use std::rc::Rc; fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result { Ok(Ctr::Symbol("λ".to_string())) } -/* loads defaults, evaluates config script */ -pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { +fn get_paths() -> Vec { + Vec::from_iter(var("PATH") + .unwrap_or("".to_string().into()) + .split(':') + .map(String::from)) +} + +pub fn load_defaults(syms: &mut SymTable) { syms.insert( "CFG_RELISH_POSIX".to_string(), Symbol { @@ -74,15 +82,59 @@ default value ()" ..Default::default() }, ); - - let mut config_document = fs::read_to_string(filename).unwrap_or_else(|err: io::Error| { - eprintln!("{}", err); - "".to_string() - }) + ")"; - - config_document = "(".to_string() + &config_document; - - let config_tree = lex(&config_document)?; - eval(&config_tree, syms)?; - Ok(()) +} + +pub fn load_environment(syms: &mut SymTable) { + for (key, value) in vars() { + syms.insert( + key.clone(), + Symbol{ + name: key, + args: Args::None, + conditional_branches: false, + docs: String::from("from env vars at time of load"), + value: ValueType::VarForm(Box::new(Ctr::String(value))), + ..Default::default() + } + ); + } +} + +pub fn run(filename: String, syms: &mut SymTable) -> Result<(), String> { + let script_read_res = fs::read_to_string(filename); + if script_read_res.is_err() { + Err(format!("Couldnt read script: {}", script_read_res.err().unwrap())) + } else { + let script_read = script_read_res.unwrap() + ")"; + let script = "(".to_string() + &script_read; + eval(&*lex(&script)?, syms)?; + Ok(()) + } +} + +pub const RUN_DOCSTRING: &str = "Takes one string argument. +Attempts to find argument in PATH and attempts to call argument"; + +pub fn run_callback(ast: &Seg, syms: &mut SymTable) -> Result { + let mut prefixes = get_paths(); + if let Ok(s) = current_dir() { + prefixes.push(String::from(s.to_str().unwrap())); + } + if let Ctr::String(ref filename) = *ast.car { + for prefix in prefixes { + let candidate = Path::new(&prefix.clone()).join(filename); + if candidate.exists() { + if filename.ends_with(".rls") { + return run(String::from(candidate.to_str().unwrap()), syms) + .and(Ok(Ctr::None)) + } else { + return Err("binary called, unimplemented!".to_string()) + } + } + } + + Err(format!("file {} not found", filename)) + } else { + Err("impossible: not a string".to_string()) + } } diff --git a/src/stl.rs b/src/stl.rs index b4cd122..0c0c92e 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -16,6 +16,7 @@ */ use crate::segment::{Ctr, Seg, Type}; +use crate::run::{run_callback, RUN_DOCSTRING}; use crate::sym::{Args, SymTable, Symbol, ValueType}; use std::rc::Rc; @@ -582,6 +583,18 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { } ); + syms.insert( + "load".to_string(), + Symbol { + name: String::from("load"), + args: Args::Strict(vec![Type::String]), + conditional_branches: false, + docs: RUN_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(run_callback)), + ..Default::default() + } + ); + Ok(()) } diff --git a/src/sym.rs b/src/sym.rs index a96d845..c20266f 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -27,9 +27,6 @@ pub struct SymTable(HashMap, usize); #[derive(Debug, Clone)] pub struct UserFn { // Un-evaluated abstract syntax tree - // TODO: Intermediate evaluation to simplify branches with no argument in them - // Simplified branches must not have side effects. - // TODO: Apply Memoization? pub ast: Box, // list of argument string tokens pub arg_syms: Vec, @@ -65,11 +62,10 @@ pub struct Symbol { pub name: String, pub args: Args, // for internal control flow constructs - // eval() will not eval the args pub conditional_branches: bool, pub docs: String, // see SymTable::Insert - // (only pub begrudgingly + // (only pub begrudgingly) pub __generation: usize, }