Signed-off-by: Ava Hahn <ava@aidanis.online>
This commit is contained in:
Ava Hahn 2023-03-01 11:38:02 -08:00
parent ecbc47d4fe
commit bc09cb07b1
Signed by untrusted user who does not match committer: affine
GPG key ID: 3A4645B8CF806069
17 changed files with 236 additions and 217 deletions

View file

@ -4,6 +4,7 @@ default:
fmt: fmt:
stage: build stage: build
script: script:
- rustup component add rustfmt
- rustfmt --check tests/* - rustfmt --check tests/*
- rustfmt --check src/*.rs - rustfmt --check src/*.rs
- rustfmt --check src/stl/* - rustfmt --check src/stl/*

View file

@ -17,8 +17,8 @@
use dirs::home_dir; use dirs::home_dir;
use relish::ast::{eval, lex, Ctr, Seg, SymTable}; use relish::ast::{eval, lex, Ctr, Seg, SymTable};
use relish::stdlib::{static_stdlib, dynamic_stdlib};
use relish::aux::configure; use relish::aux::configure;
use relish::stdlib::{dynamic_stdlib, static_stdlib};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::Editor; use rustyline::Editor;
use std::env; use std::env;
@ -39,23 +39,19 @@ fn main() {
} }
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms) static_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err));
.unwrap_or_else(|err: String| eprintln!("{}", err)); dynamic_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
{ // scope the below borrow of syms
let cfg_file = env::var("RELISH_CFG_FILE").unwrap_or(cfg_file_name); let cfg_file = env::var("RELISH_CFG_FILE").unwrap_or(cfg_file_name);
configure( configure(cfg_file.clone(), &mut syms)
cfg_file.clone(), .unwrap_or_else(|err: String| eprintln!("failed to load script {}\n{}", cfg_file, err));
&mut syms,
).unwrap_or_else(|err: String| eprintln!("failed to load script {}\n{}",
cfg_file, err));
} }
dynamic_stdlib(&mut syms) dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err));
.unwrap_or_else(|err: String| eprintln!("{}", err));
loop { loop {
let s = *syms.call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true) let s = *syms
.call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true)
.unwrap_or_else(|err: String| { .unwrap_or_else(|err: String| {
eprintln!("{}", err); eprintln!("{}", err);
Box::new(Ctr::String("<prompt broken!>".to_string())) Box::new(Ctr::String("<prompt broken!>".to_string()))

View file

@ -17,8 +17,8 @@
use crate::eval::eval; use crate::eval::eval;
use crate::lex::lex; use crate::lex::lex;
use crate::segment::{Seg, Ctr}; use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, ValueType, Args, Symbol}; use crate::sym::{Args, SymTable, Symbol, ValueType};
use std::fs; use std::fs;
use std::io; use std::io;
use std::rc::Rc; use std::rc::Rc;
@ -29,28 +29,38 @@ fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
/* loads defaults, evaluates config script */ /* loads defaults, evaluates config script */
pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> {
syms.insert("CFG_RELISH_POSIX".to_string(), Symbol { syms.insert(
"CFG_RELISH_POSIX".to_string(),
Symbol {
name: String::from("CFG_RELISH_POSIX"), name: String::from("CFG_RELISH_POSIX"),
args: Args::None, args: Args::None,
conditional_branches: false, conditional_branches: false,
value: ValueType::VarForm(Box::new(Ctr::String("0".to_string()))), value: ValueType::VarForm(Box::new(Ctr::String("0".to_string()))),
}); },
);
syms.insert("CFG_RELISH_ENV".to_string(), Symbol { syms.insert(
"CFG_RELISH_ENV".to_string(),
Symbol {
name: String::from("CFG_RELISH_ENV"), name: String::from("CFG_RELISH_ENV"),
args: Args::None, args: Args::None,
conditional_branches: false, conditional_branches: false,
value: ValueType::VarForm(Box::new(Ctr::String("1".to_string()))), value: ValueType::VarForm(Box::new(Ctr::String("1".to_string()))),
}); },
);
syms.insert("CFG_RELISH_PROMPT".to_string(), Symbol { syms.insert(
"CFG_RELISH_PROMPT".to_string(),
Symbol {
name: String::from("default relish prompt"), name: String::from("default relish prompt"),
args: Args::None, args: Args::None,
conditional_branches: false, conditional_branches: false,
value: ValueType::Internal(Rc::new(prompt_default_callback)), value: ValueType::Internal(Rc::new(prompt_default_callback)),
}); },
);
let config_document = fs::read_to_string(filename).unwrap_or_else(|err: io::Error| err.to_string()); let config_document =
fs::read_to_string(filename).unwrap_or_else(|err: io::Error| err.to_string());
let config_tree = lex(&config_document)?; let config_tree = lex(&config_document)?;
let config_result = eval(&config_tree, syms)?; let config_result = eval(&config_tree, syms)?;

View file

@ -15,17 +15,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::segment::{Ctr, Seg};
use crate::sym::SymTable; use crate::sym::SymTable;
use crate::segment::{Seg, Ctr};
/* iterates over a syntax tree /* iterates over a syntax tree
* returns a NEW LIST of values * returns a NEW LIST of values
* representing the simplest possible form of the input * representing the simplest possible form of the input
*/ */
pub fn eval ( pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
ast: &Seg,
syms: &mut SymTable,
) -> Result<Box<Ctr>, String> {
// data to return // data to return
let mut ret = Box::from(Ctr::None); let mut ret = Box::from(Ctr::None);
let mut first = true; let mut first = true;
@ -42,12 +39,10 @@ pub fn eval (
let mut none = false; let mut none = false;
while !none { while !none {
match &**arg_car { match &**arg_car {
Ctr::Seg(ref inner) => { Ctr::Seg(ref inner) => match eval(inner, syms) {
match eval(inner, syms) {
Ok(res) => car = res, Ok(res) => car = res,
Err(e) => return Err(format!("evaluation error: {}", e)), Err(e) => return Err(format!("evaluation error: {}", e)),
} },
}
Ctr::Symbol(ref tok) => { Ctr::Symbol(ref tok) => {
let outer_scope_seg_holder: Seg; let outer_scope_seg_holder: Seg;

View file

@ -151,7 +151,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
return Err("Empty token".to_string()); return Err("Empty token".to_string());
} }
let mut current_seg = ref_stack.pop().unwrap(); let mut current_seg = ref_stack.pop().unwrap_or(Seg::new());
let obj; let obj;
if is_str { if is_str {
obj = Box::from(Ctr::String(token)); obj = Box::from(Ctr::String(token));

View file

@ -15,26 +15,22 @@
* 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 config;
mod eval; mod eval;
mod lex; mod lex;
mod segment; mod segment;
mod sym;
mod stl; mod stl;
mod sym;
pub mod ast { pub mod ast {
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};
pub use crate::sym::{ pub use crate::sym::{Args, SymTable, Symbol, UserFn, ValueType};
SymTable, Symbol,
UserFn, ValueType, Args
};
} }
pub mod stdlib { pub mod stdlib {
pub use crate::stl::{static_stdlib, dynamic_stdlib}; pub use crate::stl::{dynamic_stdlib, static_stdlib};
} }
pub mod aux { pub mod aux {

View file

@ -66,7 +66,7 @@ pub struct Seg {
* How this is an acceptable solution I have * How this is an acceptable solution I have
* not a single clue. * not a single clue.
*/ */
_lifetime_variance_determinant: PhantomData<()> _lifetime_variance_determinant: PhantomData<()>,
} }
static NOTHING: Ctr = Ctr::None; static NOTHING: Ctr = Ctr::None;
@ -83,7 +83,6 @@ impl Ctr {
Ctr::None => Type::None, Ctr::None => Type::None,
} }
} }
} }
impl Seg { impl Seg {
@ -96,12 +95,12 @@ impl Seg {
pub fn append(&mut self, obj: Box<Ctr>) { pub fn append(&mut self, obj: Box<Ctr>) {
if let Ctr::None = &*(self.car) { if let Ctr::None = &*(self.car) {
self.car = obj; self.car = obj;
return return;
} }
if let Ctr::Seg(s) = &mut *(self.cdr) { if let Ctr::Seg(s) = &mut *(self.cdr) {
s.append(obj); s.append(obj);
return return;
} }
if let Ctr::None = &mut *(self.cdr) { if let Ctr::None = &mut *(self.cdr) {
@ -148,7 +147,10 @@ impl Seg {
*/ */
pub fn len(&self) -> u128 { pub fn len(&self) -> u128 {
let mut len = 0; let mut len = 0;
self.circuit(&mut |_c: &Ctr| -> bool { len += 1; true }); self.circuit(&mut |_c: &Ctr| -> bool {
len += 1;
true
});
len len
} }
@ -167,7 +169,9 @@ impl Seg {
fn seg_to_string(s: &Seg, parens: bool) -> String { fn seg_to_string(s: &Seg, parens: bool) -> String {
let mut string = String::new(); let mut string = String::new();
if parens { string.push('('); } if parens {
string.push('(');
}
match *(s.car) { match *(s.car) {
Ctr::None => string.push_str("<nil>"), Ctr::None => string.push_str("<nil>"),
_ => string.push_str(&s.car.to_string()), _ => string.push_str(&s.car.to_string()),
@ -175,10 +179,14 @@ fn seg_to_string(s: &Seg, parens: bool) -> String {
string.push(' '); string.push(' ');
match &*(s.cdr) { match &*(s.cdr) {
Ctr::Seg(inner) => string.push_str(&seg_to_string(inner, false)), Ctr::Seg(inner) => string.push_str(&seg_to_string(inner, false)),
Ctr::None => {string.pop();}, Ctr::None => {
string.pop();
}
_ => string.push_str(&s.cdr.to_string()), _ => string.push_str(&s.cdr.to_string()),
} }
if parens { string.push(')'); } if parens {
string.push(')');
}
string string
} }
@ -202,7 +210,7 @@ impl Index<usize> for Seg {
} }
if let Ctr::Seg(ref s) = *self.cdr { if let Ctr::Seg(ref s) = *self.cdr {
return s.index(idx - 1) return s.index(idx - 1);
} }
&NOTHING &NOTHING
@ -236,7 +244,7 @@ impl fmt::Display for Ctr {
} else { } else {
write!(f, "F") write!(f, "F")
} }
}, }
Ctr::Seg(s) => write!(f, "{}", s), Ctr::Seg(s) => write!(f, "{}", s),
Ctr::None => Ok(()), Ctr::None => Ok(()),
} }

View file

@ -15,40 +15,49 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::segment::{Ctr, Seg, Type};
use crate::eval::eval; use crate::eval::eval;
use crate::sym::{SymTable, Symbol, ValueType, Args, UserFn}; use crate::segment::{Ctr, Seg, Type};
use crate::sym::{Args, SymTable, Symbol, UserFn, ValueType};
use std::env; use std::env;
use std::rc::Rc; use std::rc::Rc;
pub mod control;
pub mod append; pub mod append;
pub mod control;
//pub mod str; //pub mod str;
/// static_stdlib /// static_stdlib
/// inserts all stdlib functions that can be inserted without /// inserts all stdlib functions that can be inserted without
/// any kind of further configuration data into a symtable /// any kind of further configuration data into a symtable
pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
syms.insert("append".to_string(), Symbol { syms.insert(
"append".to_string(),
Symbol {
name: String::from("append"), name: String::from("append"),
args: Args::Infinite, args: Args::Infinite,
conditional_branches: false, conditional_branches: false,
value: ValueType::Internal(Rc::new(append::append_callback)), value: ValueType::Internal(Rc::new(append::append_callback)),
}); },
);
syms.insert("expand".to_string(), Symbol { syms.insert(
"expand".to_string(),
Symbol {
name: String::from("expand"), name: String::from("expand"),
args: Args::Strict(vec![Type::Seg]), args: Args::Strict(vec![Type::Seg]),
conditional_branches: false, conditional_branches: false,
value: ValueType::Internal(Rc::new(append::expand_callback)), value: ValueType::Internal(Rc::new(append::expand_callback)),
}); },
);
syms.insert("if".to_string(), Symbol { syms.insert(
"if".to_string(),
Symbol {
name: String::from("if"), name: String::from("if"),
args: Args::Lazy(3), args: Args::Lazy(3),
conditional_branches: true, conditional_branches: true,
value: ValueType::Internal(Rc::new(control::if_callback)), value: ValueType::Internal(Rc::new(control::if_callback)),
}); },
);
Ok(()) Ok(())
} }
@ -58,21 +67,25 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
/// callbacks with configuration into a symtable /// callbacks with configuration into a symtable
pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> { pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> {
//get CFG_RELISH_ENV from syms //get CFG_RELISH_ENV from syms
let env_cfg_user_form = syms.call_symbol( let env_cfg_user_form = syms
&"CFG_RELISH_ENV".to_string(), &Seg::new(), true) .call_symbol(&"CFG_RELISH_ENV".to_string(), &Seg::new(), true)
.unwrap_or_else(|_: String| Box::new(Ctr::None)) .unwrap_or_else(|_: String| Box::new(Ctr::None))
.to_string() .to_string()
.ne(""); .ne("");
syms.insert("def".to_string(), Symbol { syms.insert(
"def".to_string(),
Symbol {
name: String::from("define"), name: String::from("define"),
args: Args::Infinite, args: Args::Infinite,
conditional_branches: true, conditional_branches: true,
value: ValueType::Internal(Rc::new( move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, String> { value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, String> {
_store_callback(ast, syms, env_cfg_user_form) _store_callback(ast, syms, env_cfg_user_form)
}, },
)), )),
}); },
);
Ok(()) Ok(())
} }
@ -82,19 +95,24 @@ fn _store_callback (ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr
if let Ctr::Symbol(ref identifier) = *ast.car { if let Ctr::Symbol(ref identifier) = *ast.car {
match &*ast.cdr { match &*ast.cdr {
Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) { Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) {
Ok(seg) => if let Ctr::Seg(ref val) = *seg { Ok(seg) => {
syms.insert(identifier.clone(), Symbol{ if let Ctr::Seg(ref val) = *seg {
syms.insert(
identifier.clone(),
Symbol {
value: ValueType::VarForm(val.car.clone()), value: ValueType::VarForm(val.car.clone()),
name: identifier.clone(), name: identifier.clone(),
args: Args::None, args: Args::None,
conditional_branches: false, conditional_branches: false,
}); },
);
if env_cfg { if env_cfg {
env::set_var(identifier.clone(), val.car.to_string()); env::set_var(identifier.clone(), val.car.to_string());
} }
} else { } else {
return Err("impossible args to export".to_string()); return Err("impossible args to export".to_string());
}, }
}
Err(e) => return Err(format!("couldnt eval symbol: {}", e)), Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
}, },
Ctr::Seg(data_tree) if !is_var => { Ctr::Seg(data_tree) if !is_var => {
@ -108,24 +126,29 @@ fn _store_callback (ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr
false false
} }
}) { }) {
return Err("all arguments defined for function must be of type symbol".to_string()); return Err(
"all arguments defined for function must be of type symbol".to_string()
);
}; };
if let Ctr::Seg(ref bodies) = *data_tree.cdr { if let Ctr::Seg(ref bodies) = *data_tree.cdr {
syms.insert(identifier.clone(), Symbol{ syms.insert(
identifier.clone(),
Symbol {
value: ValueType::FuncForm(UserFn { value: ValueType::FuncForm(UserFn {
ast: Box::new(bodies.clone()), ast: Box::new(bodies.clone()),
arg_syms: arg_list.clone(), arg_syms: arg_list.clone(),
}), }),
name: identifier.clone(), name: identifier.clone(),
args: Args::Strict(arg_list args: Args::Strict(arg_list.into_iter().map(Type::from).collect()),
.into_iter()
.map(Type::from)
.collect()),
conditional_branches: false, conditional_branches: false,
}); },
);
} else { } else {
return Err("expected one or more function bodies in function definition".to_string()); return Err(
"expected one or more function bodies in function definition"
.to_string(),
);
} }
} else { } else {
return Err("expected list of arguments in function definition".to_string()); return Err("expected list of arguments in function definition".to_string());
@ -136,7 +159,7 @@ fn _store_callback (ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr
if env_cfg { if env_cfg {
env::remove_var(identifier); env::remove_var(identifier);
} }
}, }
_ => return Err("args not in standard form".to_string()), _ => return Err("args not in standard form".to_string()),
} }
} else { } else {
@ -144,4 +167,3 @@ fn _store_callback (ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr
} }
Ok(Ctr::None) Ok(Ctr::None)
} }

View file

@ -15,8 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::segment::{Ctr, Seg};
use crate::eval::eval; use crate::eval::eval;
use crate::segment::{Ctr, Seg};
use crate::sym::SymTable; use crate::sym::SymTable;
pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> { pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
@ -26,21 +26,19 @@ pub fn if_callback (ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Bool(cond_from_eval) = *eval(cond_form, syms)? { if let Ctr::Bool(cond_from_eval) = *eval(cond_form, syms)? {
cond = cond_from_eval; cond = cond_from_eval;
} else { } else {
return Err("first argument to if must evaluate to be a boolean".to_string()) return Err("first argument to if must evaluate to be a boolean".to_string());
} }
} }
Ctr::Bool(cond_from_car) => cond = cond_from_car, Ctr::Bool(cond_from_car) => cond = cond_from_car,
_ => { _ => return Err("first argument to if must evaluate to be a boolean".to_string()),
return Err("first argument to if must evaluate to be a boolean".to_string())
}
} }
let then_form: &Seg; let then_form: &Seg;
if let Ctr::Seg(ref s) = *ast.cdr { if let Ctr::Seg(ref s) = *ast.cdr {
then_form = s; then_form = s;
} else { } else {
return Err("impossible condition: not enough args to if".to_string()) return Err("impossible condition: not enough args to if".to_string());
} }
if cond { if cond {
@ -56,9 +54,8 @@ pub fn if_callback (ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
} else { } else {
Err("impossible condition: list evals to non list".to_string()) Err("impossible condition: list evals to non list".to_string())
} }
},
} }
}
} else { } else {
// else // else
if let Ctr::Seg(ref else_form) = *then_form.cdr { if let Ctr::Seg(ref else_form) = *then_form.cdr {
@ -73,7 +70,7 @@ pub fn if_callback (ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
} else { } else {
Err("impossible condition: list evals to non list".to_string()) Err("impossible condition: list evals to non list".to_string())
} }
}, }
} }
} else { } else {
Err("impossible condition: args not in standard form".to_string()) Err("impossible condition: args not in standard form".to_string())

View file

@ -16,7 +16,7 @@
*/ */
use crate::eval::eval; use crate::eval::eval;
use crate::segment::{Seg, Ctr, Type}; use crate::segment::{Ctr, Seg, Type};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub struct SymTable(HashMap<String, Symbol>); pub struct SymTable(HashMap<String, Symbol>);
@ -41,7 +41,7 @@ pub struct UserFn {
pub enum ValueType { pub enum ValueType {
Internal(Rc<dyn Fn(&Seg, &mut SymTable) -> Result<Ctr, String>>), Internal(Rc<dyn Fn(&Seg, &mut SymTable) -> Result<Ctr, String>>),
FuncForm(UserFn), FuncForm(UserFn),
VarForm(Box<Ctr>) VarForm(Box<Ctr>),
} }
/* Function Args /* Function Args
@ -53,7 +53,7 @@ pub enum Args {
Lazy(u128), Lazy(u128),
Strict(Vec<Type>), Strict(Vec<Type>),
Infinite, Infinite,
None None,
} }
#[derive(Clone)] #[derive(Clone)]
@ -83,7 +83,12 @@ impl SymTable {
self.0.remove(arg) self.0.remove(arg)
} }
pub fn call_symbol(&mut self, name: &String, args: &Seg, call_func: bool) -> Result<Box<Ctr>, String> { pub fn call_symbol(
&mut self,
name: &String,
args: &Seg,
call_func: bool,
) -> Result<Box<Ctr>, String> {
let symbol = match self.remove(name) { let symbol = match self.remove(name) {
Some(s) => s, Some(s) => s,
None => return Err(format!("undefined symbol: {}", name)), None => return Err(format!("undefined symbol: {}", name)),
@ -125,21 +130,21 @@ impl Args {
Args::None => { Args::None => {
if args.len() == 1 { if args.len() == 1 {
if let Ctr::None = *args.car { if let Ctr::None = *args.car {
return Ok(()) return Ok(());
} else { } else {
return Err("expected no args".to_string()) return Err("expected no args".to_string());
} }
} else { } else {
return Err("expected no args".to_string()) return Err("expected no args".to_string());
}
} }
},
Args::Infinite => { Args::Infinite => {
if !args.is_empty() { if !args.is_empty() {
return Ok(()) return Ok(());
} else { } else {
return Err("expected args but none were provided".to_string()) return Err("expected args but none were provided".to_string());
}
} }
},
Args::Lazy(ref num) => { Args::Lazy(ref num) => {
let called_arg_count = args.len(); let called_arg_count = args.len();
@ -150,15 +155,9 @@ impl Args {
return Err("expected 0 args. Got one or more.".to_string()); return Err("expected 0 args. Got one or more.".to_string());
} }
} else if *num != called_arg_count { } else if *num != called_arg_count {
return Err(format!( return Err(format!("expected {} args. Got {}.", num, called_arg_count));
"expected {} args. Got {}.",
num, called_arg_count
));
} else if let Ctr::None = *args.car { } else if let Ctr::None = *args.car {
return Err(format!( return Err(format!("expected {} args. Got 0.", num,));
"expected {} args. Got 0.",
num,
));
} }
} }
@ -181,10 +180,7 @@ impl Args {
}); });
if passes && idx < (arg_types.len() - 1) { if passes && idx < (arg_types.len() - 1) {
return Err(format!( return Err(format!("{} too few arguments", arg_types.len() - (idx + 1)));
"{} too few arguments",
arg_types.len() - (idx + 1)
));
} }
if !passes { if !passes {
@ -209,11 +205,7 @@ impl Symbol {
/* call /* call
* routine is called by eval when a symbol is expanded * routine is called by eval when a symbol is expanded
*/ */
pub fn call( pub fn call(&self, args: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
&self,
args: &Seg,
syms: &mut SymTable
) -> Result<Box<Ctr>, String> {
let evaluated_args: &Seg; let evaluated_args: &Seg;
let outer_scope_seg_storage: Seg; let outer_scope_seg_storage: Seg;
let outer_scope_eval: Box<Ctr>; let outer_scope_eval: Box<Ctr>;
@ -251,13 +243,15 @@ impl Symbol {
// Prep var table for function execution // Prep var table for function execution
for n in 0..f.arg_syms.len() { for n in 0..f.arg_syms.len() {
if let Some(old) = syms.insert(f.arg_syms[n].clone(), Symbol{ if let Some(old) = syms.insert(
f.arg_syms[n].clone(),
Symbol {
name: f.arg_syms[n].clone(), name: f.arg_syms[n].clone(),
value: ValueType::VarForm(Box::new(evaluated_args[n].clone())), value: ValueType::VarForm(Box::new(evaluated_args[n].clone())),
args: Args::None, args: Args::None,
conditional_branches: false, conditional_branches: false,
}) },
{ ) {
holding_table.insert(f.arg_syms[n].clone(), old); holding_table.insert(f.arg_syms[n].clone(), old);
} }
} }
@ -280,7 +274,7 @@ impl Symbol {
} else { } else {
result = ctr; result = ctr;
} }
}, }
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} }

View file

@ -1,6 +1,6 @@
mod eval_tests { mod eval_tests {
use relish::ast::{eval, lex, SymTable}; use relish::ast::{eval, lex, SymTable};
use relish::ast::{Args, Symbol, Ctr, Seg, ValueType, UserFn}; use relish::ast::{Args, Ctr, Seg, Symbol, UserFn, ValueType};
#[test] #[test]
fn eval_simple() { fn eval_simple() {
@ -62,7 +62,8 @@ mod eval_tests {
arg_syms: vec!["input".to_string()], arg_syms: vec!["input".to_string()],
ast: Box::new(Seg::from( ast: Box::new(Seg::from(
Box::from(Ctr::Symbol("input".to_string())), Box::from(Ctr::Symbol("input".to_string())),
Box::from(Ctr::None))), Box::from(Ctr::None),
)),
}), }),
}; };
@ -101,7 +102,8 @@ mod eval_tests {
arg_syms: vec!["input".to_string()], arg_syms: vec!["input".to_string()],
ast: Box::new(Seg::from( ast: Box::new(Seg::from(
Box::from(Ctr::Symbol("input".to_string())), Box::from(Ctr::Symbol("input".to_string())),
Box::from(Ctr::None))), Box::from(Ctr::None),
)),
}), }),
}; };

View file

@ -1,8 +1,8 @@
mod func_tests { mod func_tests {
use std::rc::Rc;
use relish::ast::lex; use relish::ast::lex;
use relish::ast::{Args, Ctr, Seg, Symbol, ValueType};
use relish::ast::{SymTable, Type, UserFn}; use relish::ast::{SymTable, Type, UserFn};
use relish::ast::{Args, Symbol, Ctr, Seg, ValueType}; use std::rc::Rc;
#[test] #[test]
fn decl_and_call_internal_func() { fn decl_and_call_internal_func() {
@ -22,10 +22,7 @@ mod func_tests {
}, },
)), )),
}; };
let args = Seg::from( let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None));
Box::new(Ctr::Bool(true)),
Box::new(Ctr::None)
);
syms.insert(String::from("test_func_in"), test_internal_func); syms.insert(String::from("test_func_in"), test_internal_func);
@ -61,7 +58,7 @@ mod func_tests {
let args = Seg::from( let args = Seg::from(
Box::new(Ctr::String("test".to_string())), Box::new(Ctr::String("test".to_string())),
Box::new(Ctr::None) Box::new(Ctr::None),
); );
syms.insert(String::from("test_func_in"), test_external_func); syms.insert(String::from("test_func_in"), test_external_func);
@ -101,7 +98,7 @@ mod func_tests {
let args = Seg::from( let args = Seg::from(
Box::new(Ctr::String("test".to_string())), Box::new(Ctr::String("test".to_string())),
Box::new(Ctr::None) Box::new(Ctr::None),
); );
syms.insert(String::from("echo_2"), test_external_func); syms.insert(String::from("echo_2"), test_external_func);
@ -153,10 +150,7 @@ mod func_tests {
}), }),
}; };
let args = Seg::from( let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None));
Box::new(Ctr::Bool(true)),
Box::new(Ctr::None)
);
syms.insert(String::from("test_inner"), inner_func); syms.insert(String::from("test_inner"), inner_func);
syms.insert(String::from("test_outer"), outer_func); syms.insert(String::from("test_outer"), outer_func);
@ -196,10 +190,7 @@ mod func_tests {
}, },
)), )),
}; };
let args = Seg::from( let args = Seg::from(Box::new(Ctr::Integer(1)), Box::new(Ctr::None));
Box::new(Ctr::Integer(1)),
Box::new(Ctr::None)
);
syms.insert(String::from("test_func_in"), test_internal_func); syms.insert(String::from("test_func_in"), test_internal_func);
@ -229,7 +220,7 @@ mod func_tests {
let args = Seg::from( let args = Seg::from(
Box::new(Ctr::String("test".to_string())), Box::new(Ctr::String("test".to_string())),
Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))) Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))),
); );
syms.insert(String::from("test_func_in"), test_external_func); syms.insert(String::from("test_func_in"), test_external_func);
@ -293,13 +284,16 @@ mod func_tests {
}; };
let args = Seg::from( let args = Seg::from(
Box::new(Ctr::Symbol("undefined-symbol".to_string())), Box::new(Ctr::Symbol("undefined-symbol".to_string())),
Box::new(Ctr::None) Box::new(Ctr::None),
); );
syms.insert(String::from("test_func_in"), test_internal_func); syms.insert(String::from("test_func_in"), test_internal_func);
if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
assert_eq!(s, "error in call to undefined-symbol: undefined symbol: undefined-symbol"); assert_eq!(
s,
"error in call to undefined-symbol: undefined symbol: undefined-symbol"
);
} else { } else {
print!("call to function succeeded (shouldnt have)"); print!("call to function succeeded (shouldnt have)");
assert!(false); assert!(false);

View file

@ -133,7 +133,8 @@ mod lex_tests {
#[test] #[test]
fn test_postline_comment() { fn test_postline_comment() {
let document = String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))"); let document =
String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))");
let output: &str = "((one two) (three four))"; let output: &str = "((one two) (three four))";
match lex(&document) { match lex(&document) {
Ok(tree) => { Ok(tree) => {

View file

@ -1,6 +1,6 @@
mod append_lib_tests { mod append_lib_tests {
use relish::ast::{Ctr, eval, lex, SymTable}; use relish::ast::{eval, lex, Ctr, SymTable};
use relish::stdlib::{static_stdlib, dynamic_stdlib}; use relish::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_append_to_empty_list() { fn test_append_to_empty_list() {

View file

@ -1,6 +1,6 @@
mod control_lib_tests { mod control_lib_tests {
use relish::ast::{Ctr, eval, lex, SymTable}; use relish::ast::{eval, lex, Ctr, SymTable};
use relish::stdlib::{static_stdlib, dynamic_stdlib}; use relish::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_if_first_case_singlet() { fn test_if_first_case_singlet() {

View file

@ -1,6 +1,6 @@
mod var_lib_tests { mod var_lib_tests {
use relish::ast::{eval, lex, SymTable, Ctr}; use relish::ast::{eval, lex, Ctr, SymTable};
use relish::stdlib::{static_stdlib, dynamic_stdlib}; use relish::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_variable_def_and_lookup() { fn test_variable_def_and_lookup() {
@ -134,7 +134,10 @@ mod var_lib_tests {
println!("tree: {tree}"); println!("tree: {tree}");
let eval_result = eval(&tree, &mut syms); let eval_result = eval(&tree, &mut syms);
if let Err(s) = eval_result { if let Err(s) = eval_result {
assert_eq!(s.to_string(), "error in call to test: undefined symbol: test".to_string()); assert_eq!(
s.to_string(),
"error in call to test: undefined symbol: test".to_string()
);
} else { } else {
let res = eval_result.unwrap(); let res = eval_result.unwrap();
eprintln!("shouldn't have suceeded: {res}"); eprintln!("shouldn't have suceeded: {res}");