use vyre::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
use vyre_reference::value::Value;
fn run(program: &Program, inputs: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
let values: Vec<Value> = inputs.into_iter().map(Value::from).collect();
let outputs = vyre_reference::reference_eval(program, &values).expect("program must execute");
outputs.into_iter().map(|v| v.to_bytes()).collect()
}
fn store_u32_program(body: Vec<Node>) -> Program {
Program::wrapped(
vec![BufferDecl::storage("out", 0, BufferAccess::ReadWrite, DataType::U32).with_count(1)],
[1, 1, 1],
body,
)
}
#[test]
fn assign_mutates_prior_let_in_same_scope() {
let body = vec![
Node::let_bind("acc", Expr::u32(10)),
Node::assign("acc", Expr::add(Expr::var("acc"), Expr::u32(5))),
Node::store("out", Expr::u32(0), Expr::var("acc")),
];
let out = run(&store_u32_program(body), vec![vec![0u8; 4]]);
let val = u32::from_le_bytes(out[0][..4].try_into().unwrap());
assert_eq!(val, 15, "assign must mutate the prior let binding");
}
#[test]
fn assign_accumulates_across_loop_iterations() {
let body = vec![
Node::let_bind("acc", Expr::u32(0)),
Node::loop_for(
"i",
Expr::u32(0),
Expr::u32(4),
vec![Node::assign(
"acc",
Expr::add(Expr::var("acc"), Expr::var("i")),
)],
),
Node::store("out", Expr::u32(0), Expr::var("acc")),
];
let out = run(&store_u32_program(body), vec![vec![0u8; 4]]);
let val = u32::from_le_bytes(out[0][..4].try_into().unwrap());
assert_eq!(val, 6, "loop-scope assign must accumulate into outer let");
}
#[test]
fn assign_from_inside_region_mutates_outer_let() {
let inner = vec![Node::assign(
"acc",
Expr::add(Expr::var("acc"), Expr::u32(42)),
)];
let region = Node::Region {
generator: "test".into(),
source_region: None,
body: std::sync::Arc::new(inner),
};
let body = vec![
Node::let_bind("acc", Expr::u32(100)),
region,
Node::store("out", Expr::u32(0), Expr::var("acc")),
];
let out = run(&store_u32_program(body), vec![vec![0u8; 4]]);
let val = u32::from_le_bytes(out[0][..4].try_into().unwrap());
assert_eq!(
val, 142,
"Region transparency: outer let must observe inner assign"
);
}
#[test]
fn shadowing_is_rejected_by_validator_v008() {
let inner = vec![
Node::let_bind("acc", Expr::u32(99)),
Node::store("out", Expr::u32(0), Expr::var("acc")),
];
let body = vec![Node::let_bind("acc", Expr::u32(1)), Node::Block(inner)];
let program = store_u32_program(body);
let errors = vyre_foundation::validate::validate(&program);
assert!(
errors
.iter()
.any(|e| e.message().contains("duplicate local binding")),
"shadowing must fail validation (V008), got {:?}",
errors.iter().map(|e| e.message()).collect::<Vec<_>>()
);
}
#[test]
fn assign_survives_nested_block_scope() {
let inner = vec![Node::assign(
"acc",
Expr::add(Expr::var("acc"), Expr::u32(7)),
)];
let body = vec![
Node::let_bind("acc", Expr::u32(100)),
Node::Block(inner),
Node::store("out", Expr::u32(0), Expr::var("acc")),
];
let out = run(&store_u32_program(body), vec![vec![0u8; 4]]);
let val = u32::from_le_bytes(out[0][..4].try_into().unwrap());
assert_eq!(val, 107, "assigns to outer let persist across block scope");
}