got all the load script stuff done.

added script args to main shell
also added userlib tests to ci
This commit is contained in:
Ava Apples Affine 2023-03-20 19:00:30 -07:00
parent 381852b3bd
commit 3f75157fac
Signed by: affine
GPG key ID: 3A4645B8CF806069
7 changed files with 122 additions and 49 deletions

View file

@ -10,3 +10,8 @@ unit-tests:
stage: test stage: test
script: script:
- cargo test - cargo test
userlib-tests:
stage: test
script:
- cargo run snippets/userlib.rls snippets/userlib-tests.rls

View file

@ -477,14 +477,8 @@ 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. Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered.
** TODO Pre-alpha tasks ** 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) - Shell prompt is fully configurable (History, L, R, and Delim)
- userlib list contains - userlib list contains
- Extend userlib tests
- Add userlib tests to CI
- Input function - Input function
- Lex function - Lex function
- Read function (Input + Lex) - Read function (Input + Lex)
@ -501,7 +495,6 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt
- Readme documentation for POSIX module - Readme documentation for POSIX module
- logging library - logging library
- make const all the error messages - make const all the error messages
- Main shell calls Load function on arg and exits
- Should globals be immutable? - Should globals be immutable?
** TODO alpha tasks ** TODO alpha tasks

View file

@ -16,8 +16,7 @@
*/ */
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use dirs::home_dir; use dirs::home_dir;
use relish::ast::{eval, lex, Ctr, Seg, SymTable}; use relish::ast::{eval, lex, Ctr, Seg, SymTable, run, load_defaults, load_environment};
use relish::aux::configure;
use relish::stdlib::{dynamic_stdlib, static_stdlib}; use relish::stdlib::{dynamic_stdlib, static_stdlib};
use reedline::{ use reedline::{
FileBackedHistory, DefaultHinter, DefaultValidator, Reedline, Signal, FileBackedHistory, DefaultHinter, DefaultValidator, Reedline, Signal,
@ -67,14 +66,43 @@ impl Prompt for CustomPrompt<'_> {
} }
} }
fn main() -> ! { fn main() {
const HIST_FILE: &str = "/.relish_hist"; const HIST_FILE: &str = "/.relish_hist";
const CONFIG_FILE_DEFAULT: &str = "/.relishrc"; const CONFIG_FILE_DEFAULT: &str = "/.relishrc";
// default config file dirs
let home_dir = home_dir().unwrap().to_str().unwrap().to_owned(); let home_dir = home_dir().unwrap().to_str().unwrap().to_owned();
let hist_file_name = home_dir.clone() + HIST_FILE; let hist_file_name = home_dir.clone() + HIST_FILE;
let cfg_file_name = home_dir + CONFIG_FILE_DEFAULT; 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 mut rl = Reedline::create();
let maybe_hist: Box<FileBackedHistory>; let maybe_hist: Box<FileBackedHistory>;
if !hist_file_name.is_empty() { if !hist_file_name.is_empty() {
@ -82,23 +110,12 @@ fn main() -> ! {
.expect("error reading history!")); .expect("error reading history!"));
rl = rl.with_history(maybe_hist); rl = rl.with_history(maybe_hist);
} }
rl = rl.with_hinter(Box::new( rl = rl.with_hinter(Box::new(
DefaultHinter::default() DefaultHinter::default()
.with_style(Style::new().italic().fg(Color::LightGray)), .with_style(Style::new().italic().fg(Color::LightGray)),
)).with_validator(Box::new(DefaultValidator)); )).with_validator(Box::new(DefaultValidator));
let mut syms = SymTable::new(); // repl :)
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));
loop { loop {
let s = *syms let s = *syms
.call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true) .call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true)

View file

@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
mod config; mod run;
mod eval; mod eval;
mod lex; mod lex;
mod segment; mod segment;
@ -23,6 +23,7 @@ mod stl;
mod sym; mod sym;
pub mod ast { pub mod ast {
pub use crate::run::{run, load_defaults, load_environment};
pub use crate::eval::eval; pub use crate::eval::eval;
pub use crate::lex::lex; pub use crate::lex::lex;
pub use crate::segment::{Ctr, Seg, Type}; pub use crate::segment::{Ctr, Seg, Type};
@ -32,7 +33,3 @@ pub mod ast {
pub mod stdlib { pub mod stdlib {
pub use crate::stl::{dynamic_stdlib, static_stdlib}; pub use crate::stl::{dynamic_stdlib, static_stdlib};
} }
pub mod aux {
pub use crate::config::configure;
}

View file

@ -19,16 +19,24 @@ use crate::eval::eval;
use crate::lex::lex; use crate::lex::lex;
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{Args, SymTable, Symbol, ValueType}; use crate::sym::{Args, SymTable, Symbol, ValueType};
use std::path::Path;
use std::fs; use std::fs;
use std::io; use std::iter::FromIterator;
use std::env::{vars, var, current_dir};
use std::rc::Rc; use std::rc::Rc;
fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> { fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::Symbol("λ".to_string())) Ok(Ctr::Symbol("λ".to_string()))
} }
/* loads defaults, evaluates config script */ fn get_paths() -> Vec<String> {
pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { Vec::from_iter(var("PATH")
.unwrap_or("".to_string().into())
.split(':')
.map(String::from))
}
pub fn load_defaults(syms: &mut SymTable) {
syms.insert( syms.insert(
"CFG_RELISH_POSIX".to_string(), "CFG_RELISH_POSIX".to_string(),
Symbol { Symbol {
@ -74,15 +82,59 @@ default value (<lambda>)"
..Default::default() ..Default::default()
}, },
); );
}
let mut config_document = fs::read_to_string(filename).unwrap_or_else(|err: io::Error| { pub fn load_environment(syms: &mut SymTable) {
eprintln!("{}", err); for (key, value) in vars() {
"".to_string() 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()
}
);
}
}
config_document = "(".to_string() + &config_document; pub fn run(filename: String, syms: &mut SymTable) -> Result<(), String> {
let script_read_res = fs::read_to_string(filename);
let config_tree = lex(&config_document)?; if script_read_res.is_err() {
eval(&config_tree, syms)?; 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(()) 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<Ctr, String> {
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())
}
}

View file

@ -16,6 +16,7 @@
*/ */
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::run::{run_callback, RUN_DOCSTRING};
use crate::sym::{Args, SymTable, Symbol, ValueType}; use crate::sym::{Args, SymTable, Symbol, ValueType};
use std::rc::Rc; 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(()) Ok(())
} }

View file

@ -27,9 +27,6 @@ pub struct SymTable(HashMap<String, Symbol>, usize);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct UserFn { pub struct UserFn {
// Un-evaluated abstract syntax tree // 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<Seg>, pub ast: Box<Seg>,
// list of argument string tokens // list of argument string tokens
pub arg_syms: Vec<String>, pub arg_syms: Vec<String>,
@ -65,11 +62,10 @@ pub struct Symbol {
pub name: String, pub name: String,
pub args: Args, pub args: Args,
// for internal control flow constructs // for internal control flow constructs
// eval() will not eval the args
pub conditional_branches: bool, pub conditional_branches: bool,
pub docs: String, pub docs: String,
// see SymTable::Insert // see SymTable::Insert
// (only pub begrudgingly // (only pub begrudgingly)
pub __generation: usize, pub __generation: usize,
} }