From 307101327c4c8895d131fd9ddb156ab70fe8227c Mon Sep 17 00:00:00 2001 From: Aidan Hahn Date: Sun, 7 Nov 2021 22:04:57 -0800 Subject: [PATCH] variable export and entire config system --- CONFIGURING.md | 23 ++++++++++ Readme.md | 3 ++ src/bin/main.rs | 45 +++++++++++++++++--- src/config.rs | 74 +++++++++++++++++++++++++++++++++ src/lib.rs | 5 +++ src/stl.rs | 26 ++++++++++-- src/vars.rs | 3 +- tests/test_lib_append.rs | 8 ++-- tests/test_lib_str.rs | 6 +-- {src => tests}/test_lib_vars.rs | 12 +++--- 10 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 CONFIGURING.md create mode 100644 src/config.rs rename {src => tests}/test_lib_vars.rs (86%) diff --git a/CONFIGURING.md b/CONFIGURING.md new file mode 100644 index 0000000..5db1932 --- /dev/null +++ b/CONFIGURING.md @@ -0,0 +1,23 @@ +# Configuration +By default Relish will read from `~/.relishrc` for configuration, but the default shell will also accept a filename from the `RELISH_CFG_FILE` environment variable. + +## The configuration file +The configuration file is a script containing arbitrary Relish code. On start, any shell (including the default shell) which leverages the configuration code in [the config module](src/config.rs) will create a clean seperate context, including default configuration values, within which the standard library will be initialized and the configuration file will be run. Afterwards, configuration values found in the variable map will be used to configure the standard library function mappings that the fully functional user shell will leverage. Errors during configuration are non-terminal and will result in default values being returned + +#### Important points to note +- When the configuration file is run, it will be run with default configuration values. +- The user/script interpreter will be run with a *re-instantiation* of the standard library, using the previously defined configuration variables +- Variables and functions defined during configuration will carry over to the user/script interpreter, allowing the user to load any number of custom functions and variables. + + +## Configuration Values +The following are important variables that will determine the runtime behavior of the default shell or standard library functions: + +| Variable | Default Value | Explanation | +|-|-|-| +| CFG_RELISH_POSIX | 1 | When on, enables POSIX style job control. [more information.](shell.md) | +| CFG_RELISH_ENV | 1 | When on, variables defined will be synchronized with environment variables. | +| CFG_RELISH_PROMPT | (echo "λ ") | *note: this is a function not a variable*. This function will be called to output the prompt each time the REPL loops. | + +#### Specific Behaviors +- CFG_RELISH_POSIX is only loaded once and will be ignored after initial configuration. diff --git a/Readme.md b/Readme.md index 937f7d6..c6a8af5 100644 --- a/Readme.md +++ b/Readme.md @@ -16,6 +16,9 @@ Relish is a language meant to iterate on the ideas and designs that were tested - Stdlib including string operations, arithmetic operations, and file operations - TESTS TESTS TESTS +## Configuration +[See docs here](CONFIGURATION.md) + ## Compilation `$ cargo build` diff --git a/src/bin/main.rs b/src/bin/main.rs index 1b180c1..ff0ccef 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -19,15 +19,17 @@ use rustyline::error::ReadlineError; use rustyline::Editor; use dirs::home_dir; use std::rc::Rc; +use std::env; use std::cell::RefCell; -use relish::ast::{VTable, FTable, Ctr, lex, eval, ast_to_string}; +use relish::ast::{VTable, FTable, Ctr, lex, eval, ast_to_string, new_ast, func_call}; +use relish::aux::configure; use relish::stdlib::{get_stdlib}; fn main() { let mut rl = Editor::<()>::new(); const HIST_FILE: &str = ".relish_hist"; - //const CONFIG_FILE: &str = ".relishrc"; + const CONFIG_FILE_DEFAULT: &str = ".relishrc"; let mut hist: String = "".to_owned(); @@ -45,14 +47,47 @@ fn main() { let vt = Rc::new(RefCell::new(VTable::new())); // if we fail to get stdlib we can just use this one let mut ft = Rc::new(RefCell::new(FTable::new())); - match get_stdlib() { + + match env::var("RELISH_CFG_FILE") { + Ok(s) => configure(s, vt.clone(), ft.clone()), + Err(_) => configure(String::from(CONFIG_FILE_DEFAULT), vt.clone(), ft.clone()) + } + + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => println!("{}", s) } loop { - // TODO: configurable prompt - let readline = rl.readline("λ "); + let readline: Result; + // Rust is pain + let tmp_ft_clone = ft.clone(); + // this is not okay + let t_ft_c_b = tmp_ft_clone.borrow(); + let pfunc = t_ft_c_b.get("CFG_RELISH_PROMPT"); + if let Some(fnc) = pfunc { + match func_call(fnc.clone(), new_ast(Ctr::None, Ctr::None), vt.clone(), ft.clone()) { + Err(s) => { + eprintln!("Couldnt generate prompt: {}", s); + readline = rl.readline(""); + }, + + Ok(c) => { + match c { + Ctr::Symbol(s) => readline = rl.readline(&s.to_owned()), + Ctr::String(s) => readline = rl.readline(&s), + Ctr::Integer(i) => readline = rl.readline(&format!("{}", i)), + Ctr::Float(f) => readline = rl.readline(&format!("{}", f)), + Ctr::Bool(b) => readline = rl.readline(&format!("{}", b)), + Ctr::Seg(c) => readline = rl.readline(&ast_to_string(c.clone())), + Ctr::None => readline = rl.readline("") + } + } + } + } else { + readline = rl.readline(""); + } + match readline { Ok(line) => { rl.add_history_entry(line.as_str()); diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..1137e4e --- /dev/null +++ b/src/config.rs @@ -0,0 +1,74 @@ +/* relish: versatile lisp shell + * Copyright (C) 2021 Aidan Hahn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use crate::vars::{VTable, define}; +use crate::func::{FTable, Args, Function, Operation, func_declare}; +use crate::segment::{Ast, Ctr}; +use crate::lex::lex; +use crate::eval::eval; +use std::rc::Rc; +use std::cell::RefCell; +use std::fs; + +pub fn configure(filename: String, vars: Rc>, funcs: Rc>) { + define(vars.clone(), String::from("CFG_RELISH_POSIX"), Rc::new(Ctr::String(String::from("1")))); + define(vars.clone(), String::from("CFG_RELISH_ENV"), Rc::new(Ctr::String(String::from("1")))); + func_declare(funcs.clone(), Rc::new(RefCell::new(Function{ + name: String::from("CFG_RELISH_PROMPT"), + loose_syms: false, + eval_lazy: false, + args: Args::Lazy(0), + function: Operation::Internal( + |_: Ast, _: Rc>, _: Rc>| -> Ctr { + print!("λ "); + return Ctr::None; + } + ) + }))); + + match fs::read_to_string(filename) { + Err(s) => { + eprintln!("Couldnt open configuration file: {}", s); + return + }, + + Ok(raw_config) => { + match lex(raw_config) { + Err(s) => { + println!("Error in configuration: {}", s); + return + }, + + Ok(config) => { + match eval(config, vars, funcs, false) { + Err(s) => { + println!("Error in applying configuration: {}", s); + return + }, + + Ok(ctr) => { + match ctr { + Ctr:: String(s) => println!("{}", s), + _ => () + } + } + } + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index c7c8334..97caf87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ mod vars; mod stl; mod str; mod append; +mod config; pub mod ast { pub use crate::segment::{Seg, Ctr, ast_to_string, Type, Ast, new_ast}; @@ -40,3 +41,7 @@ pub mod stdlib { pub use crate::append::{get_append}; pub use crate::vars::{get_export}; } + +pub mod aux { + pub use crate::config::configure; +} diff --git a/src/stl.rs b/src/stl.rs index 1dca9fa..b03eb9d 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -15,14 +15,15 @@ * along with this program. If not, see . */ +use crate::segment::{Ctr}; use crate::str::{get_echo, get_concat}; use crate::append::get_append; use crate::func::{FTable, func_declare}; -use crate::vars::{get_export}; +use crate::vars::{VTable, get_export}; use std::rc::Rc; use std::cell::RefCell; -pub fn get_stdlib() -> Result>, String> { +pub fn get_stdlib(conf: Rc>) -> Result>, String> { let ft = Rc::new(RefCell::new(FTable::new())); if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_echo()))) { return Err(s) @@ -33,7 +34,26 @@ pub fn get_stdlib() -> Result>, String> { if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_concat()))) { return Err(s) } - if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_export()))) { + + let mut cfg_env = true; + match conf.borrow().get(&String::from("CFG_RELISH_ENV")) { + None => { + println!("CFG_RELISH_ENV not defined. defaulting to ON.") + } + Some(ctr) => { + match (**ctr).clone() { + Ctr::String(ref s) => { + cfg_env = s.eq("0") + } + _ => { + println!("Invalid value for CFG_RELISH_ENV. must be a string (0 or 1)."); + println!("Defaulting CFG_RELISH_ENV to ON"); + } + } + } + } + + if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_export(cfg_env)))) { return Err(s) } diff --git a/src/vars.rs b/src/vars.rs index 2813c2b..93181ad 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -37,7 +37,8 @@ pub fn define( } } -pub fn get_export() -> Function { +// TODO: Accept bool config value for env vars +pub fn get_export(env_cfg: bool) -> Function { return Function{ name: String::from("export"), loose_syms: true, diff --git a/tests/test_lib_append.rs b/tests/test_lib_append.rs index a9f8383..a5984e2 100644 --- a/tests/test_lib_append.rs +++ b/tests/test_lib_append.rs @@ -10,7 +10,7 @@ mod append_lib_tests { let result = "(1 2 3)"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); @@ -54,7 +54,7 @@ mod append_lib_tests { let result = "(1 2 3)"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); @@ -98,7 +98,7 @@ mod append_lib_tests { let result = "()"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); @@ -142,7 +142,7 @@ mod append_lib_tests { let result = "('test' 1 2 3)"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); diff --git a/tests/test_lib_str.rs b/tests/test_lib_str.rs index 2cad97d..1fc3e6e 100644 --- a/tests/test_lib_str.rs +++ b/tests/test_lib_str.rs @@ -10,7 +10,7 @@ mod str_lib_tests { let result = "test"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); @@ -54,7 +54,7 @@ mod str_lib_tests { let result = "test123"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); @@ -98,7 +98,7 @@ mod str_lib_tests { let result = ""; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); diff --git a/src/test_lib_vars.rs b/tests/test_lib_vars.rs similarity index 86% rename from src/test_lib_vars.rs rename to tests/test_lib_vars.rs index cd96e67..7408cca 100644 --- a/src/test_lib_vars.rs +++ b/tests/test_lib_vars.rs @@ -1,6 +1,6 @@ -mod str_lib_tests { +mod var_lib_tests { use relish::stdlib::{get_stdlib}; - use relish::ast::{lex, eval, ast_to_string, VTable, FTable, Ctr}; + use relish::ast::{lex, eval, VTable, FTable, Ctr}; use std::rc::Rc; use std::cell::RefCell; @@ -11,7 +11,7 @@ mod str_lib_tests { let result = "1"; let vt = Rc::new(RefCell::new(VTable::new())); let ft: Rc>; - match get_stdlib() { + match get_stdlib(vt.clone()) { Ok(f) => ft = f, Err(s) => { ft = Rc::new(RefCell::new(FTable::new())); @@ -34,8 +34,10 @@ mod str_lib_tests { }, Ok(ctr) => { - Ctr::None => assert!(true), - _ => assert!(false) + match ctr { + Ctr::None => assert!(true), + _ => assert!(false) + } } } }