pub use itsy::{build_str, run as itsy_run, parser::parse_module, sizes::{StackAddress, STACK_ADDRESS_TYPE}, resolver::resolved::meta::Type}; pub use std::{any::Any, fmt::Debug}; pub use std::{u8, u16, u32, u64, i8, i16, i32, i64, f32, f64}; pub type ContextElement = Box; pub type Context = Vec; use itsy::itsy_api; /// Compare a ContextElement with given value. #[allow(dead_code)] pub fn assert(result: &ContextElement, expected: T) where T: PartialEq+Debug+'static { if let Some(result) = result.downcast_ref::() { assert!(result == &expected, "Result <{:?}> did not match expected <{:?}>", result, &expected); } else { panic!("Result-type did not match type of expected value <{:?}>", &expected); } } /// Compare Context with given values. #[allow(dead_code)] pub fn assert_all(result: &Context, expected: &[ T ]) where T: PartialEq+Debug+'static { for index in 0..expected.len().min(result.len()) { if let Some(result) = result[index].downcast_ref::() { assert!(result == &expected[index], "Result <{:?}> did not match expected <{:?}> at index {}", result, &expected[index], index); } else { panic!("Result-type did not match type of expected value <{:?}>", &expected); } } assert!(result.len() == expected.len(), "Result length {} did not match expected length {}", result.len(), expected.len()); } #[allow(dead_code)] pub fn assert_all_sa(result: &Context, expected: &[ u64 ]) { match STACK_ADDRESS_TYPE { Type::u8 => assert_all(&result, &expected.iter().map(|u| *u as u8).collect::>()), Type::u16 => assert_all(&result, &expected.iter().map(|u| *u as u16).collect::>()), Type::u32 => assert_all(&result, &expected.iter().map(|u| *u as u32).collect::>()), Type::u64 => assert_all(&result, expected), _ => panic!("this test does not support the selected StackAddress type"), } } // Implement some VM methods to write values of specific types to the VM context. itsy_api!(TestFns, Context, { fn ret_u8(&mut context, value: u8) { context.push(Box::new(value)); } fn ret_u16(&mut context, value: u16) { context.push(Box::new(value)); } fn ret_u32(&mut context, value: u32) { context.push(Box::new(value)); } fn ret_u64(&mut context, value: u64) { context.push(Box::new(value)); } fn ret_i8(&mut context, value: i8) { context.push(Box::new(value)); } fn ret_i16(&mut context, value: i16) { context.push(Box::new(value)); } fn ret_i32(&mut context, value: i32) { context.push(Box::new(value)); } fn ret_i64(&mut context, value: i64) { context.push(Box::new(value)); } fn ret_f32(&mut context, value: f32) { context.push(Box::new(value)); } fn ret_f64(&mut context, value: f64) { context.push(Box::new(value)); } fn ret_bool(&mut context, value: bool) { context.push(Box::new(value)); } fn ret_string(&mut context, value: String) { context.push(Box::new(value)); } fn ret_str(&mut context, value: &str) { context.push(Box::new(value.to_string())); } }); /// Run a bit of itsy code and return the vm's custom field (populated by the code). #[allow(dead_code)] pub fn run(code: &str) -> Context { let tmp; let input = if code.find("fn main()").is_some() { code } else { tmp = format!("fn main() {{ {} }}", code); &tmp }; let program = match build_str::(input) { Ok(program) => program, Err(err) => { let loc = err.loc(&input); panic!("{} in line {}, column {}.", err, loc.0, loc.1); } }; let mut context = Vec::new(); match itsy_run(&program, &mut context) { Ok(_) => { }, Err(err) => { let loc = err.loc(&input); panic!("{} in line {}, column {}.", err, loc.0, loc.1); } } context }