Lines
100 %
Functions
Branches
// Skipped under Miri: these tests compile+run wasm via wasmtime, whose
// Cranelift backend refuses to run under Miri.
#![cfg(not(miri))]
use nms::interpreter::Interpreter;
use scripting::nomiscript::{Fraction, Value};
#[test]
fn test_eval_add() {
let mut interp = Interpreter::new(false).unwrap();
let results = interp.eval("(+ 1 2 3 4 5 6 7)").unwrap();
assert_eq!(results, vec![Value::Number(Fraction::from_integer(28))]);
}
fn test_eval_add_identity() {
let results = interp.eval("(+)").unwrap();
assert_eq!(results, vec![Value::Number(Fraction::from_integer(0))]);
fn test_eval_add_fractions() {
let results = interp.eval("(+ 0.5 0.25)").unwrap();
assert_eq!(results, vec![Value::Number(Fraction::new(3, 4))]);
fn test_eval_sub_negation() {
assert_eq!(
interp.eval("(- 5)").unwrap(),
vec![Value::Number(Fraction::from_integer(-5))]
);
fn test_eval_sub_binary() {
interp.eval("(- 10 3)").unwrap(),
vec![Value::Number(Fraction::from_integer(7))]
fn test_eval_sub_variadic() {
interp.eval("(- 100 10 20 30)").unwrap(),
vec![Value::Number(Fraction::from_integer(40))]
fn test_eval_sub_no_args_error() {
assert!(interp.eval("(-)").is_err());
fn test_eval_mul() {
interp.eval("(* 2 3 4)").unwrap(),
vec![Value::Number(Fraction::from_integer(24))]
fn test_eval_mul_identity() {
interp.eval("(*)").unwrap(),
vec![Value::Number(Fraction::from_integer(1))]
fn test_eval_mul_fractions() {
interp.eval("(* 0.5 0.5)").unwrap(),
vec![Value::Number(Fraction::new(1, 4))]
fn test_eval_div_reciprocal() {
// ADR-0028: an all-integer (Index) reciprocal divides as an index — `1/4`
// truncates toward zero to `0`. A fractional reciprocal needs a Scalar
// operand (e.g. `(/ 4.0)`).
interp.eval("(/ 4)").unwrap(),
vec![Value::Number(Fraction::from_integer(0))]
fn test_eval_div_reciprocal_scalar() {
// The Scalar reciprocal: a fractional operand (denom ≠ 1) keeps rational
// division — `(/ 4/3)` → `3/4`.
interp.eval("(/ 4/3)").unwrap(),
vec![Value::Number(Fraction::new(3, 4))]
fn test_eval_div_binary() {
// ADR-0028: all-integer (Index) division truncates toward zero (`10/3 → 3`),
// matching `i32.div_s`. A `10/3` Scalar needs a fractional literal.
interp.eval("(/ 10 3)").unwrap(),
vec![Value::Number(Fraction::from_integer(3))]
fn test_eval_div_variadic() {
interp.eval("(/ 120 2 3 4)").unwrap(),
vec![Value::Number(Fraction::from_integer(5))]
fn test_eval_div_by_zero_error() {
assert!(interp.eval("(/ 1 0)").is_err());
fn test_eval_div_no_args_error() {
assert!(interp.eval("(/)").is_err());
fn test_eval_div_reciprocal_zero_error() {
assert!(interp.eval("(/ 0)").is_err());
fn test_eval_mod() {
interp.eval("(mod 10 3)").unwrap(),
fn test_eval_mod_negative() {
// ADR-0028: integer MOD lowers to `i32.rem_s` (remainder takes the sign of
// the dividend), so `(mod -1 5) → -1`, not the Euclidean `4`. The negative
// result decodes as a signed `Number` (the `i64.extend_i32_s` fix), not its
// unsigned 2^32 image.
interp.eval("(mod -1 5)").unwrap(),
vec![Value::Number(Fraction::from_integer(-1))]
fn test_eval_mod_arity_error() {
assert!(interp.eval("(mod 10)").is_err());
assert!(interp.eval("(mod 10 3 2)").is_err());