finished circuit form

Signed-off-by: Ava Hahn <ava@aidanis.online>
This commit is contained in:
Ava Hahn 2023-03-03 14:29:53 -08:00
parent c235f9727f
commit 4b587f11ab
Signed by untrusted user who does not match committer: affine
GPG key ID: 3A4645B8CF806069
7 changed files with 144 additions and 53 deletions

View file

@ -136,11 +136,6 @@ This contains any executable target of this project. Notably the main shell file
Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer.
Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered.
*** TODO Control Flow
**** DONE if form
**** DONE let form
**** DONE while form
**** TODO circuit form
*** TODO Clean up tests, simplify, convert some to unit tests, mention tests in Readme as docs
*** TODO Document all internal/builtin functions in the rustiest way possible
*** TODO Custom ast pretty print

View file

@ -59,8 +59,7 @@ pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> {
},
);
let mut config_document = fs::read_to_string(filename)
.unwrap_or_else(|err: io::Error| {
let mut config_document = fs::read_to_string(filename).unwrap_or_else(|err: io::Error| {
eprintln!("{}", err);
"".to_string()
}) + ")";

View file

@ -22,8 +22,8 @@ use std::env;
use std::rc::Rc;
pub mod append;
pub mod control;
pub mod boolean;
pub mod control;
//pub mod str;
/// static_stdlib
@ -107,7 +107,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
args: Args::Infinite,
conditional_branches: false,
value: ValueType::Internal(Rc::new(boolean::bool_and_callback)),
}
},
);
syms.insert(
@ -117,7 +117,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
args: Args::Infinite,
conditional_branches: false,
value: ValueType::Internal(Rc::new(boolean::bool_or_callback)),
}
},
);
syms.insert(
@ -127,7 +127,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
args: Args::Strict(vec![Type::Bool]),
conditional_branches: false,
value: ValueType::Internal(Rc::new(boolean::bool_not_callback)),
}
},
);
syms.insert(
@ -137,7 +137,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
args: Args::Infinite,
conditional_branches: false,
value: ValueType::Internal(Rc::new(boolean::bool_iseq_callback)),
}
},
);
syms.insert(
@ -147,7 +147,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
args: Args::Lazy(1),
conditional_branches: true,
value: ValueType::Internal(Rc::new(boolean::bool_toggle_callback)),
}
},
);
Ok(())

View file

@ -65,7 +65,9 @@ pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
pub fn bool_iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
let head_ctr_ref = &*ast.car;
Ok(Ctr::Bool(ast.circuit(&mut |arg: &Ctr| -> bool {arg == head_ctr_ref})))
Ok(Ctr::Bool(
ast.circuit(&mut |arg: &Ctr| -> bool { arg == head_ctr_ref }),
))
}
pub fn bool_toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
@ -73,20 +75,22 @@ pub fn bool_toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Strin
if let Ctr::Symbol(ref s) = *ast.car {
var_name = s.clone();
} else {
return Err("argument to toggle should be a symbol".to_string())
return Err("argument to toggle should be a symbol".to_string());
}
let mut sym = syms.remove(&var_name).expect(&format!("symbol {var_name} is not defined"));
let mut sym = syms
.remove(&var_name)
.expect(&format!("symbol {var_name} is not defined"));
if let ValueType::VarForm(ref var) = sym.value {
if let Ctr::Bool(ref b) = **var {
sym.value = ValueType::VarForm(Box::new(Ctr::Bool(!b)));
} else {
syms.insert(var_name, sym);
return Err("can only toggle a boolean".to_string())
return Err("can only toggle a boolean".to_string());
}
} else {
syms.insert(var_name, sym);
return Err("cannot toggle a function".to_string())
return Err("cannot toggle a function".to_string());
}
syms.insert(var_name, sym);

View file

@ -17,7 +17,7 @@
use crate::eval::eval;
use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, Symbol, ValueType, Args};
use crate::sym::{Args, SymTable, Symbol, ValueType};
pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
let cond: bool;
@ -32,10 +32,7 @@ pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
}
Ctr::Symbol(ref cond_name) => {
if let Ctr::Bool(cond_from_eval) = *syms.call_symbol(
cond_name,
&Seg::new(), false
)? {
if let Ctr::Bool(cond_from_eval) = *syms.call_symbol(cond_name, &Seg::new(), false)? {
cond = cond_from_eval;
} else {
return Err("first argument to if must evaluate to be a boolean".to_string());
@ -95,13 +92,13 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref locals_form_list) = *ast.car {
locals_form = locals_form_list;
} else {
return Err("first element of let form must contain local variables".to_string())
return Err("first element of let form must contain local variables".to_string());
}
if let Ctr::Seg(ref eval_forms_head) = *ast.cdr {
eval_forms = eval_forms_head;
} else {
return Err("let form should contain one or more elements to evaluate".to_string())
return Err("let form should contain one or more elements to evaluate".to_string());
}
// process locals forms
@ -125,27 +122,30 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
}
if let Err(e) = var_val_res {
eprintln!("failed to evaluate definition of {}: {}", name, e);
return false
return false;
}
localsyms.insert(name.clone(), Symbol {
localsyms.insert(
name.clone(),
Symbol {
name: name.clone(),
args: Args::None,
conditional_branches: false,
value: ValueType::VarForm(Box::new(*var_val_res.unwrap().clone())),
});
},
);
}
} else {
eprintln!("improper declaration of {}: not a list", var_decl);
return false
return false;
}
} else {
eprintln!("improper declaration form: {}", var_decl);
return false
return false;
}
true
}) {
return Err("local variable declaration failure".to_string())
return Err("local variable declaration failure".to_string());
}
let mut result: Box<Ctr> = Box::new(Ctr::None);
@ -167,13 +167,13 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
if let Err(e) = res {
eprintln!("error evaluating let form: {}", e);
return false
return false;
}
result = res.unwrap().clone();
true
}) {
return Err("evaluation failure".to_string())
return Err("evaluation failure".to_string());
}
Ok((*result).clone())
@ -200,7 +200,7 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref eval) = *ast.cdr {
eval_bodies_head = eval;
} else {
return Err("expected N more bodies in while form".to_string())
return Err("expected N more bodies in while form".to_string());
}
loop {
@ -208,15 +208,19 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
if unwrap {
if let Ctr::Seg(ref cond_res_inner) = cond_res {
if let Ctr::Bool(ref b) = *cond_res_inner.car {
if !b {break}
if !b {
break;
}
}
} else {
panic!()
}
} else if let Ctr::Bool(b) = cond_res {
if !b {break}
if !b {
break;
}
} else {
return Err("first body of while form should evaluate to a bool".to_string())
return Err("first body of while form should evaluate to a bool".to_string());
}
if !eval_bodies_head.circuit(&mut |body: &Ctr| -> bool {
@ -242,7 +246,49 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
/*
(circuit makes_a_bool makes_a_bool makes_a_bool makes_a_bool ... )
*/
pub fn circuit_callback(_ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
todo!()
}
pub fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
let mut cursor = 0;
let mut error: String = String::new();
let result = ast.circuit(&mut |form: &Ctr| -> bool {
cursor += 1;
let operand: &Seg;
let mut expand_eval_res = false;
let outer_scope_seg: Seg;
if let Ctr::Seg(ref s) = form {
operand = s;
} else {
outer_scope_seg = Seg::from_mono(Box::new(form.clone()));
operand = &outer_scope_seg;
expand_eval_res = true;
}
let eval_result = eval(operand, syms);
match eval_result {
Err(s) => error = format!("eval failed at form {cursor}: {s}"),
Ok(s) => match *s {
Ctr::Bool(b) => return b,
Ctr::Seg(s) if expand_eval_res => {
if let Ctr::Bool(b) = *s.car {
return b;
} else {
error = "impossible condition in circuit form".to_string();
}
}
_ => error = format!("{cursor} form did not evaluate to a boolean"),
},
}
false
});
if !result && !error.is_empty() {
Err(format!("circuit stopped at form {cursor}: {error}"))
} else {
if !result {
eprintln!("circuit stopped at form {cursor}");
}
Ok(Ctr::Bool(result))
}
}

View file

@ -160,7 +160,7 @@ mod bool_lib_tests {
eval(&change_tree, &mut syms).unwrap();
if let Ctr::Seg(ref s) = *eval(&check_tree, &mut syms).unwrap() {
if let Ctr::Bool(ref b) = *s.car{
if let Ctr::Bool(ref b) = *s.car {
assert_eq!(false, *b)
} else {
panic!()
@ -171,7 +171,7 @@ mod bool_lib_tests {
eval(&change_tree, &mut syms).unwrap();
if let Ctr::Seg(ref s) = *eval(&check_tree, &mut syms).unwrap() {
if let Ctr::Bool(ref b) = *s.car{
if let Ctr::Bool(ref b) = *s.car {
assert_eq!(true, *b)
} else {
panic!()
@ -197,7 +197,10 @@ mod bool_lib_tests {
eval(&doc_tree, &mut syms).unwrap();
if let Err(s) = eval(&change_tree, &mut syms) {
assert_eq!(s, "error in call to toggle: can only toggle a boolean".to_string());
assert_eq!(
s,
"error in call to toggle: can only toggle a boolean".to_string()
);
let intermediate = *eval(&check_tree, &mut syms).unwrap();
if let Ctr::Seg(ref s) = intermediate {
assert_eq!(s.to_string(), "('oops')".to_string());
@ -227,7 +230,10 @@ mod bool_lib_tests {
eval(&doc_tree, &mut syms).unwrap();
if let Err(s) = eval(&change_tree, &mut syms) {
assert_eq!(s, "error in call to toggle: cannot toggle a function".to_string());
assert_eq!(
s,
"error in call to toggle: cannot toggle a function".to_string()
);
if let Ctr::String(ref s) = *eval(&check_tree, &mut syms).unwrap() {
assert_eq!(*s, "1".to_string());
} else {

View file

@ -213,4 +213,45 @@ mod control_lib_tests {
eval(&while_tree, &mut syms).unwrap();
eval(&check_tree, &mut syms).unwrap();
}
#[test]
fn test_circuit_basic() {
let document = "(if (circuit true (and true true) true) (def result 'passed') ())";
let test = "result";
let doc_tree = lex(&document.to_string()).unwrap();
let test_tree = lex(&test.to_string()).unwrap();
let mut syms = SymTable::new();
static_stdlib(&mut syms).unwrap();
dynamic_stdlib(&mut syms).unwrap();
eval(&doc_tree, &mut syms).unwrap();
let res = eval(&test_tree, &mut syms);
println!("{:#?}", res);
res.unwrap();
}
#[test]
fn test_circuit_fail() {
let document = "(if (circuit true (and false true) true) (def result 'passed') ())";
let test = "result";
let doc_tree = lex(&document.to_string()).unwrap();
let test_tree = lex(&test.to_string()).unwrap();
let mut syms = SymTable::new();
static_stdlib(&mut syms).unwrap();
dynamic_stdlib(&mut syms).unwrap();
eval(&doc_tree, &mut syms).unwrap();
if let Err(s) = eval(&test_tree, &mut syms) {
assert_eq!(
s,
"error in call to result: undefined symbol: result".to_string()
);
} else {
panic!();
}
}
}