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

@ -17,8 +17,8 @@
use dirs::home_dir;
use relish::ast::{eval, lex, Ctr, Seg, SymTable};
use relish::stdlib::{static_stdlib, dynamic_stdlib};
use relish::aux::configure;
use relish::stdlib::{dynamic_stdlib, static_stdlib};
use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::env;
@ -39,23 +39,19 @@ fn main() {
}
let mut syms = SymTable::new();
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
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));
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));
dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err));
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| {
eprintln!("{}", err);
Box::new(Ctr::String("<prompt broken!>".to_string()))

View file

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

View file

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

View file

@ -15,26 +15,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
mod config;
mod eval;
mod lex;
mod segment;
mod sym;
mod stl;
mod sym;
pub mod ast {
pub use crate::eval::eval;
pub use crate::lex::lex;
pub use crate::segment::{Ctr, Seg, Type};
pub use crate::sym::{
SymTable, Symbol,
UserFn, ValueType, Args
};
pub use crate::sym::{Args, SymTable, Symbol, UserFn, ValueType};
}
pub mod stdlib {
pub use crate::stl::{static_stdlib, dynamic_stdlib};
pub use crate::stl::{dynamic_stdlib, static_stdlib};
}
pub mod aux {

View file

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

View file

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

View file

@ -17,7 +17,7 @@
use crate::segment::{Ctr, Seg};
use crate::sym::SymTable;
pub fn append_callback (ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref s) = *ast.car {
let mut temp = s.clone();
if let Ctr::Seg(ref list) = *ast.cdr {
@ -47,7 +47,7 @@ pub fn append_callback (ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
}
}
pub fn expand_callback (ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
pub fn expand_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(_) = *ast.car {
Ok(*ast.car.clone())
} else {

View file

@ -15,32 +15,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::segment::{Ctr, Seg};
use crate::eval::eval;
use crate::segment::{Ctr, Seg};
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> {
let cond: bool;
match *ast.car {
Ctr::Seg(ref cond_form) => {
if let Ctr::Bool(cond_from_eval) = *eval(cond_form, syms)? {
cond = cond_from_eval;
} 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,
_ => {
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;
if let Ctr::Seg(ref s) = *ast.cdr {
then_form = s;
} 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 {
@ -56,9 +54,8 @@ pub fn if_callback (ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
} else {
Err("impossible condition: list evals to non list".to_string())
}
},
}
}
} else {
// else
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 {
Err("impossible condition: list evals to non list".to_string())
}
},
}
}
} else {
Err("impossible condition: args not in standard form".to_string())
@ -81,18 +78,18 @@ pub fn if_callback (ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
}
}
pub fn let_callback (_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
pub fn let_callback(_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
todo!()
}
pub fn while_callback (_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
pub fn while_callback(_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
todo!()
}
pub fn map_callback (_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
pub fn map_callback(_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
todo!()
}
pub fn circuit_callback (_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
pub fn circuit_callback(_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
todo!()
}

View file

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