use assert_matches::assert_matches;
use core::iter::FromIterator;
use arithmetic_eval::{Assertions, Environment, ErrorKind, ExecutableModule, Prelude, Value};
use arithmetic_parser::{
grammars::{F64Grammar, MockTypes, Parse, WithMockedTypes},
BinaryOp, StripCode, StripResultExt,
};
struct MockedTypesList;
impl MockTypes for MockedTypesList {
const MOCKED_TYPES: &'static [&'static str] = &["Num", "[_]", "any"];
}
type Grammar = WithMockedTypes<F64Grammar, MockedTypesList>;
fn create_module<'a>(
module_name: &'static str,
program: &'a str,
) -> anyhow::Result<ExecutableModule<'a, f64>> {
let block = Grammar::parse_statements(program).strip_err()?;
Ok(ExecutableModule::builder(module_name, &block)
.strip_err()?
.with_imports_from(&Prelude)
.with_imports_from(&Assertions)
.with_import("INF", Value::Prim(f64::INFINITY))
.set_imports(|_| Value::void()))
}
fn create_static_module(
module_name: &'static str,
program: &str,
) -> anyhow::Result<ExecutableModule<'static, f64>> {
create_module(module_name, program).map(StripCode::strip_code)
}
fn main() -> anyhow::Result<()> {
let sum_module = {
let dynamic_program = String::from("|var| var.fold(0, |acc, x| acc + x)");
create_static_module("sum", &dynamic_program)?
};
let sum_fn = sum_module.run()?;
assert!(sum_fn.is_function());
let mut test_module = create_module("test", "(1, 2, -5).sum()")?;
test_module.set_import("sum", sum_fn.clone());
let sum_value = test_module.run()?;
assert_eq!(sum_value, Value::Prim(-2.0));
let bogus_module = create_module("bogus", "(1, true, -5).sum()")?;
let mut env = Environment::from_iter(bogus_module.imports());
env.insert("sum", sum_fn);
let err = bogus_module.run_in_env(&mut env).unwrap_err();
println!("Expected error:\n{:#}", err);
assert_matches!(
err.source().kind(),
ErrorKind::UnexpectedOperand { op } if *op == BinaryOp::Add.into()
);
assert_eq!(
err.source().main_span().code().code_or_location("call"),
"call at 1:34"
);
let fold_program = include_str!("rfold.script");
let fold_program = String::from(fold_program);
let fold_module = create_module("rfold", &fold_program)?;
let rfold_fn = fold_module.run().strip_err()?;
let mut env = Environment::from_iter(sum_module.imports());
env.insert("fold", rfold_fn);
let rfold_sum = sum_module.run_in_env(&mut env).strip_err()?;
let mut test_module = test_module;
test_module.set_import("sum", rfold_sum);
let sum_value = test_module.run().strip_err()?;
assert_eq!(sum_value, Value::Prim(-2.0));
Ok(())
}