Introduce Value::Cell for mutable upvalues, allowing closures to

modify captured variables.
Implement `capture_upvalue` and `get_value`/`set_value` methods in the
`VM` to handle these mutable upvalues.

Refactor number parsing to support integers and floats

Separate the `TokenKind::Number` into `TokenKind::Integer` and
`TokenKind::Float`.
Update the lexer to distinguish between integer and float literals.
Update the parser to correctly map these new token kinds to their
respective `Value` types.

Add integration tests for parsing integers and floats, and for closure
modification of captured variables.
This commit is contained in:
Michael Schimmel
2026-02-17 12:35:39 +01:00
parent 12791a7f8f
commit ce166f39e3
6 changed files with 289 additions and 16 deletions
+91
View File
@@ -0,0 +1,91 @@
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use crate::ast::parser::Parser;
use crate::ast::compiler::binder::Binder;
use crate::ast::vm::VM;
use crate::ast::types::Value;
use crate::ast::nodes::UntypedKind;
#[test]
fn test_parse_integer_constant() {
let source = "123";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Int(val)) = ast.kind {
assert_eq!(val, 123);
} else {
panic!("Expected Integer constant, got {:?}", ast.kind);
}
}
#[test]
fn test_parse_negative_integer() {
let source = "-42";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Int(val)) = ast.kind {
assert_eq!(val, -42);
} else {
panic!("Expected Integer constant, got {:?}", ast.kind);
}
}
#[test]
fn test_parse_float_constant() {
let source = "123.45";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Float(val)) = ast.kind {
assert_eq!(val, 123.45);
} else {
panic!("Expected Float constant, got {:?}", ast.kind);
}
}
#[test]
fn test_parse_negative_float() {
let source = "-10.5";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Float(val)) = ast.kind {
assert_eq!(val, -10.5);
} else {
panic!("Expected Float constant, got {:?}", ast.kind);
}
}
#[test]
fn test_closure_modification_from_source() {
let source = r#"
(do
(def x 10)
(def f (fn [] (assign x 20)))
(f)
x
)
"#;
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
let globals = Arc::new(Mutex::new(std::collections::HashMap::new()));
let mut binder = Binder::new(globals.clone());
let bound_ast = binder.bind(&ast).expect("Failed to bind");
let vm_globals = Arc::new(Mutex::new(Vec::new()));
let mut vm = VM::new(vm_globals);
let result = vm.run(&bound_ast);
match result {
Ok(Value::Int(val)) => assert_eq!(val, 20, "Closure should modify outer variable"),
Ok(val) => panic!("Expected Int(20), got {:?}", val),
Err(e) => panic!("VM Error: {}", e),
}
}
}