use std::rc::Rc; use talc_lang::{compiler::compile, optimize::optimize, parser, value::Value, vm::Vm}; fn assert_eval(src: &str, value: Value) { let mut ex = parser::parse(src).expect(&format!("failed to parse expression")); optimize(&mut ex); let f = compile(&ex, None).expect("failed to compile expression"); let f = Rc::new(f); let mut vm = Vm::new(16, Vec::new()); let res = vm .run_function(f.clone(), vec![f.into()]) .expect("vm produced an exception"); assert_eq!( res.get_type().name(), value.get_type().name(), "result and target types differ" ); assert_eq!(res, value, "result and target differ"); } #[test] fn scope() { assert_eval( " var x = 7 var y = 10 do var x = 200 y = 100 end x + y ", Value::Int(7 + 100), ); assert_eval( " var cond = true var z = 2 if cond then var z = 5 else var z = 6 end z ", Value::Int(2), ); assert_eval( " var i = 55 var j = 66 for i in 0..10 do j = i end i + j ", Value::Int(55 + 9), ); } #[test] fn forloop() { assert_eval( " sum = 0 for i in 0..5 do for j in 0..10 do sum += i*j end end sum ", Value::Int(45 * 10), ); assert_eval( " map = {a=3, b=2, c=4, d=7} prod = 1 for k in map do prod *= map[k] end prod ", Value::Int(3 * 2 * 4 * 7), ); } #[test] fn closures() { assert_eval( " var x = 2 next = \\. do x = x * 2 + 1 end next() + next() + next() ", Value::Int(5 + 11 + 23), ); assert_eval( " var x = 0 fn outer(n) do fn inner() do x += n n += 1 x end end var f = outer(2) var g = outer(6) f() + f() + g() + g() + f() ", Value::Int(2 + 5 + 11 + 18 + 22), ); } #[test] fn tailcall() { assert_eval( " fn test(n, a) do if n <= 0 then a else self(n-1, n+a) end end test(24, 0) ", Value::Int(300), ) // = 24 + 23 + ... + 1 }