variable export and entire config system
This commit is contained in:
parent
0931fbdcf0
commit
307101327c
10 changed files with 184 additions and 21 deletions
23
CONFIGURING.md
Normal file
23
CONFIGURING.md
Normal file
|
|
@ -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.
|
||||
|
|
@ -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`
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String, ReadlineError>;
|
||||
// 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());
|
||||
|
|
|
|||
74
src/config.rs
Normal file
74
src/config.rs
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<RefCell<VTable>>, funcs: Rc<RefCell<FTable>>) {
|
||||
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<RefCell<VTable>>, _: Rc<RefCell<FTable>>| -> 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),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
26
src/stl.rs
26
src/stl.rs
|
|
@ -15,14 +15,15 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Rc<RefCell<FTable>>, String> {
|
||||
pub fn get_stdlib(conf: Rc<RefCell<VTable>>) -> Result<Rc<RefCell<FTable>>, 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<Rc<RefCell<FTable>>, 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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ mod append_lib_tests {
|
|||
let result = "(1 2 3)";
|
||||
let vt = Rc::new(RefCell::new(VTable::new()));
|
||||
let ft: Rc<RefCell<FTable>>;
|
||||
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<RefCell<FTable>>;
|
||||
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<RefCell<FTable>>;
|
||||
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<RefCell<FTable>>;
|
||||
match get_stdlib() {
|
||||
match get_stdlib(vt.clone()) {
|
||||
Ok(f) => ft = f,
|
||||
Err(s) => {
|
||||
ft = Rc::new(RefCell::new(FTable::new()));
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ mod str_lib_tests {
|
|||
let result = "test";
|
||||
let vt = Rc::new(RefCell::new(VTable::new()));
|
||||
let ft: Rc<RefCell<FTable>>;
|
||||
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<RefCell<FTable>>;
|
||||
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<RefCell<FTable>>;
|
||||
match get_stdlib() {
|
||||
match get_stdlib(vt.clone()) {
|
||||
Ok(f) => ft = f,
|
||||
Err(s) => {
|
||||
ft = Rc::new(RefCell::new(FTable::new()));
|
||||
|
|
|
|||
|
|
@ -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<RefCell<FTable>>;
|
||||
match get_stdlib() {
|
||||
match get_stdlib(vt.clone()) {
|
||||
Ok(f) => ft = f,
|
||||
Err(s) => {
|
||||
ft = Rc::new(RefCell::new(FTable::new()));
|
||||
|
|
@ -34,12 +34,14 @@ mod str_lib_tests {
|
|||
},
|
||||
|
||||
Ok(ctr) => {
|
||||
match ctr {
|
||||
Ctr::None => assert!(true),
|
||||
_ => assert!(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match lex(doc2.to_string()) {
|
||||
Err(s) => {
|
||||
Loading…
Add table
Add a link
Reference in a new issue