use std::collections::HashMap; use crate::{Node, Value}; /// Evaluate given AST using given context. fn eval_ctx(ast: Node, ctx: &mut HashMap<String, Value>) -> Value { match ast { crate::Node::Num(n) => n.into(), crate::Node::Unit => ().into(), crate::Node::Seq(nodes) => { let mut result = ().into(); for node in nodes { result = eval_ctx(node, ctx); } result } crate::Node::Let { ident, expr } => { let value = eval_ctx(*expr, ctx); ctx.try_insert(ident.clone(), value).unwrap_or_else(|_| { panic!("Let error: `{ident}` shadows previously defined variable.") }); ().into() } crate::Node::Ident(ident) => ctx .get(&ident) .unwrap_or_else(|| panic!("Ident error: `{ident}` is not defined")) .clone(), } } /// Evaluate given AST using a fresh context. pub fn eval(ast: Node) -> Value { eval_ctx(ast, &mut HashMap::new()) } #[cfg(test)] mod tests { use crate::parser::ast; use super::*; fn test(input: &str, result: impl Into<Value>) { assert_eq!(eval(ast(input)), result.into()) } #[test] fn unit() { test("{}", ()); } #[test] fn unit_with_space() { test("{ }", ()); } #[test] fn block_of_unit_with_space() { test("{ { } }", ()); } #[test] fn num() { test("42", 42); } #[test] fn block_of_num() { test("{42}", 42); } #[test] fn block_of_num_whitespace() { test("{ 42 }", 42); } #[test] fn seq_of_unit() { test("{}; { }", ()); } #[test] fn block_of_seq_of_unit() { test("{{ }; {}}", ()); } #[test] fn seq_of_num() { test("1; 2", 2); } #[test] #[should_panic] fn empty_seq_snd_fail() { test("1;", ()); } #[test] #[should_panic] fn empty_seq_fst_fail() { test(";1", ()); } #[test] #[should_panic] fn empty_seq_fail() { test(";", ()); } #[test] fn block_of_seq_of_num() { test("{1; 2}", 2); } #[test] fn block_of_seq_of_mixed() { test("{{ }; 2}", 2); } #[test] fn block_of_seq_len_3() { test("{1; 3; 2}", 2); } #[test] fn seq_len_3() { test("1; 3; 2", 2); } #[test] fn block_of_seq_len_3_mixed() { test("{1; { } ; 2}", 2); } #[test] fn seq_len_3_mixed() { test("{} ; 3; { }", ()); } #[test] fn let_char() { test("let x = {}", ()); } #[test] fn let_long() { test("let foo = 42", ()); } #[test] fn let_ident_with_number() { test("let foo1 = 42", ()); } #[test] fn let_then_unit() { test("let x = 10; {}", ()); } }