got all the load script stuff done.
added script args to main shell also added userlib tests to ci
This commit is contained in:
parent
381852b3bd
commit
3f75157fac
7 changed files with 122 additions and 49 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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| {
|
|
||||||
eprintln!("{}", err);
|
pub fn load_environment(syms: &mut SymTable) {
|
||||||
"".to_string()
|
for (key, value) in vars() {
|
||||||
}) + ")";
|
syms.insert(
|
||||||
|
key.clone(),
|
||||||
config_document = "(".to_string() + &config_document;
|
Symbol{
|
||||||
|
name: key,
|
||||||
let config_tree = lex(&config_document)?;
|
args: Args::None,
|
||||||
eval(&config_tree, syms)?;
|
conditional_branches: false,
|
||||||
Ok(())
|
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<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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
13
src/stl.rs
13
src/stl.rs
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue