rustfmt
Signed-off-by: Ava Hahn <ava@aidanis.online>
This commit is contained in:
parent
ecbc47d4fe
commit
bc09cb07b1
17 changed files with 236 additions and 217 deletions
|
|
@ -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/*
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
|
||||||
|
|
@ -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)?;
|
||||||
|
|
||||||
|
|
|
||||||
13
src/eval.rs
13
src/eval.rs
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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(()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
78
src/stl.rs
78
src/stl.rs
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
58
src/sym.rs
58
src/sym.rs
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
)),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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}");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue