Finish out the basic primitives along with testing infra for both parsing and evalaution, with some slick Into<> impls to make Rust values Kraken values, which is esp useful for testing

This commit is contained in:
2023-02-08 23:37:23 -05:00
parent d861d91397
commit 02e359f42d
3 changed files with 159 additions and 32 deletions

View File

@@ -1,4 +1,17 @@
use std::rc::Rc; use std::rc::Rc;
use std::convert::From;
impl From<i32> for Form { fn from(item: i32) -> Self { Form::Int(item) } }
impl From<bool> for Form { fn from(item: bool) -> Self { Form::Bool(item) } }
// todo, strings not symbols?
impl From<String> for Form { fn from(item: String) -> Self { Form::Symbol(item) } }
impl From<&str> for Form { fn from(item: &str) -> Self { Form::Symbol(item.to_owned()) } }
impl<A: Into<Form>, B: Into<Form>> From<(A, B)> for Form {
fn from(item: (A, B)) -> Self {
Form::Pair(Rc::new(item.0.into()), Rc::new(item.1.into()))
}
}
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum Form { pub enum Form {

View File

@@ -21,7 +21,7 @@ match {
".", ".",
"'", "'",
r"[0-9]+" => NUM, r"[0-9]+" => NUM,
r"[a-zA-Z+*/_=-][\w+*/=_-]*" => SYM, r"[a-zA-Z+*/_=?%&|^-][\w+*/=_?%&|^-]*" => SYM,
r"(;[^\n]*\n)|\s+" => { } r"(;[^\n]*\n)|\s+" => { }
} }

View File

@@ -8,28 +8,59 @@ use crate::ast::Form;
#[test] #[test]
fn parse_test() { fn parse_test() {
assert!(grammar::TermParser::new().parse("22").is_ok()); let g = grammar::TermParser::new();
assert!(grammar::TermParser::new().parse("(22)").is_ok()); for test in [
assert!(grammar::TermParser::new().parse("(((22)))").is_ok()); "22", "(22)", "(((22)))",
"(22 )", "()", "( )", "( 44)", "(44 )",
"(22 44 (1) 33 (4 5 (6) 6))", "hello",
"-", "+", "(+ 1 ;hi
3)", "'13", "hello-world", "_",
] {
assert!(g.parse(test).is_ok());
}
assert!(g.parse("((22)").is_err());
assert!(grammar::TermParser::new().parse("((22)").is_err()); let e = root_env();
assert!(grammar::TermParser::new().parse("22").is_ok()); fn eval_test<T: Into<Form>>(gram: &grammar::TermParser, e: &Rc<Form>, code: &str, expected: T) {
assert!(grammar::TermParser::new().parse("(22)").is_ok()); assert_eq!(*eval(Rc::clone(e), Rc::new(gram.parse(code).unwrap())), expected.into());
assert!(grammar::TermParser::new().parse("(22 )").is_ok()); }
assert!(grammar::TermParser::new().parse("()").is_ok());
assert!(grammar::TermParser::new().parse("( )").is_ok()); eval_test(&g, &e, "(+ 2 (car (cons 4 '(1 2))))", 6);
assert!(grammar::TermParser::new().parse("( 44)").is_ok()); eval_test(&g, &e, "(= 17 ((vau d p (+ (eval (car p) d) 13)) (+ 1 3)))", true);
assert!(grammar::TermParser::new().parse("(44 )").is_ok()); eval_test(&g, &e, "(if (= 2 2) (+ 1 2) (+ 3 4))", 3);
assert!(grammar::TermParser::new().parse("(22 44 (1) 33 (4 5 (6) 6))").is_ok()); eval_test(&g, &e, "(quote a)", "a");
assert!(grammar::TermParser::new().parse("hello").is_ok()); eval_test(&g, &e, "'a", "a");
assert!(grammar::TermParser::new().parse("-").is_ok()); eval_test(&g, &e, "'(1 . a)", (1, "a"));
assert!(grammar::TermParser::new().parse("+").is_ok()); eval_test(&g, &e, "'(1 a)", (1, ("a", Form::Nil)));
assert!(grammar::TermParser::new().parse("(+ 1 ;hi eval_test(&g, &e, "true", true);
3)").is_ok()); eval_test(&g, &e, "false", false);
assert!(grammar::TermParser::new().parse("'13").is_ok()); eval_test(&g, &e, "nil", Form::Nil);
assert!(grammar::TermParser::new().parse("hello-world").is_ok());
assert!(grammar::TermParser::new().parse("_").is_ok()); eval_test(&g, &e, "(+ 1 2)", 3);
eval_test(&g, &e, "(- 1 2)", -1);
eval_test(&g, &e, "(* 1 2)", 2);
eval_test(&g, &e, "(/ 4 2)", 2);
eval_test(&g, &e, "(% 3 2)", 1);
eval_test(&g, &e, "(& 3 2)", 2);
eval_test(&g, &e, "(| 2 1)", 3);
eval_test(&g, &e, "(^ 2 1)", 3);
eval_test(&g, &e, "(^ 3 1)", 2);
eval_test(&g, &e, "(comb? +)", true);
eval_test(&g, &e, "(comb? (vau d p 1))", true);
eval_test(&g, &e, "(comb? 1)", false);
eval_test(&g, &e, "(pair? '(a))", true);
//eval_test(&g, &e, "(pair? '())", true);
eval_test(&g, &e, "(nil? nil)", true);
eval_test(&g, &e, "(nil? 1)", false);
eval_test(&g, &e, "(pair? 1)", false);
eval_test(&g, &e, "(symbol? 'a)", true);
eval_test(&g, &e, "(symbol? 1)", false);
eval_test(&g, &e, "(int? 1)", true);
eval_test(&g, &e, "(int? true)", false);
eval_test(&g, &e, "(bool? true)", true);
eval_test(&g, &e, "(bool? 1)", false);
} }
fn eval(e: Rc<Form>, f: Rc<Form>) -> Rc<Form> { fn eval(e: Rc<Form>, f: Rc<Form>) -> Rc<Form> {
@@ -84,8 +115,8 @@ fn assoc_vec(kvs: Vec<(&str, Rc<Form>)>) -> Rc<Form> {
to_ret to_ret
} }
fn main() { fn root_env() -> Rc<Form> {
let env = assoc_vec(vec![ assoc_vec(vec![
// TODO: Should be properly tail recursive // TODO: Should be properly tail recursive
("eval", Rc::new(Form::PrimComb("eval".to_owned(), |e, p| { ("eval", Rc::new(Form::PrimComb("eval".to_owned(), |e, p| {
println!("To get eval body, evaluating {:?} in {:?}", p.car(), e); println!("To get eval body, evaluating {:?} in {:?}", p.car(), e);
@@ -120,11 +151,6 @@ fn main() {
Rc::new(Form::Nil) Rc::new(Form::Nil)
} }
}))), }))),
("+", Rc::new(Form::PrimComb("+".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a + b))
}))),
("cons", Rc::new(Form::PrimComb("cons".to_owned(), |e, p| { ("cons", Rc::new(Form::PrimComb("cons".to_owned(), |e, p| {
let h = eval(Rc::clone(&e), p.car().unwrap()); let h = eval(Rc::clone(&e), p.car().unwrap());
let t = eval(e, p.cdr().unwrap().car().unwrap()); let t = eval(e, p.cdr().unwrap().car().unwrap());
@@ -139,7 +165,95 @@ fn main() {
("quote", Rc::new(Form::PrimComb("quote".to_owned(), |e, p| { ("quote", Rc::new(Form::PrimComb("quote".to_owned(), |e, p| {
p.car().unwrap() p.car().unwrap()
}))), }))),
]);
("+", Rc::new(Form::PrimComb("+".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a + b))
}))),
("-", Rc::new(Form::PrimComb("-".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a - b))
}))),
("*", Rc::new(Form::PrimComb("*".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a * b))
}))),
("/", Rc::new(Form::PrimComb("/".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a / b))
}))),
("%", Rc::new(Form::PrimComb("%".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a % b))
}))),
("&", Rc::new(Form::PrimComb("&".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a & b))
}))),
("|", Rc::new(Form::PrimComb("|".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a | b))
}))),
("^", Rc::new(Form::PrimComb("^".to_owned(), |e, p| {
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
Rc::new(Form::Int(a ^ b))
}))),
("comb?", Rc::new(Form::PrimComb("comb?".to_owned(), |e, p| {
Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
Form::PrimComb(n, f) => true,
Form::DeriComb { .. } => true,
_ => false,
}))
}))),
("pair?", Rc::new(Form::PrimComb("pair?".to_owned(), |e, p| {
Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
Form::Pair(_a,_b) => true,
_ => false,
}))
}))),
("symbol?", Rc::new(Form::PrimComb("symbol?".to_owned(), |e, p| {
Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
Form::Symbol(_) => true,
_ => false,
}))
}))),
("int?", Rc::new(Form::PrimComb("int?".to_owned(), |e, p| {
Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
Form::Int(_) => true,
_ => false,
}))
}))),
// maybe bool? but also could be derived. Nil def
("bool?", Rc::new(Form::PrimComb("bool?".to_owned(), |e, p| {
Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
Form::Bool(_) => true,
_ => false,
}))
}))),
("nil?", Rc::new(Form::PrimComb("nil?".to_owned(), |e, p| {
Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
Form::Nil => true,
_ => false,
}))
}))),
// consts
("true", Rc::new(Form::Bool(true))),
("false", Rc::new(Form::Bool(false))),
("nil", Rc::new(Form::Nil)),
])
}
fn main() {
//let input = "(+ 2 (car (cons 4 '(1 2))))"; //let input = "(+ 2 (car (cons 4 '(1 2))))";
let input = "(= 17 ((vau d p (+ (eval (car p) d) 13)) (+ 1 3)))"; let input = "(= 17 ((vau d p (+ (eval (car p) d) 13)) (+ 1 3)))";
//let input = "(if (= 2 2) (+ 1 2) (+ 3 4))"; //let input = "(if (= 2 2) (+ 1 2) (+ 3 4))";
@@ -147,6 +261,6 @@ fn main() {
//let input = "'a"; //let input = "'a";
let parsed_input = grammar::TermParser::new().parse(input).unwrap(); let parsed_input = grammar::TermParser::new().parse(input).unwrap();
println!("Parsed input is {:?}", parsed_input); println!("Parsed input is {:?}", parsed_input);
let result = eval(env, Rc::new(parsed_input)); let result = eval(root_env(), Rc::new(parsed_input));
println!("Result is {:?}", result); println!("Result is {:?}", result);
} }