Files
rlox/tests/resolution.rs
Giulio Agostini 842216729b wip:
Add type system and refator for having only epression
2026-07-06 10:43:17 +02:00

70 lines
2.2 KiB
Rust

//! End-to-end pipeline tests: lexer -> parser -> resolver -> interpreter.
//!
//! These verify chapter-11 resolution wired into execution: a variable's
//! resolved scope distance is used for lookup (`get_at`), and resolution errors
//! abort before running.
use rlox::backend::interpreter::{EvaluateInterpreter, Interpreter};
use rlox::common::base_value::BaseValue;
use rlox::frontend::lexer::Lexer;
use rlox::frontend::parser::Parser;
use rlox::middleend::variable_resolution::Resolver;
/// Run the whole pipeline, returning the value of the last statement.
fn run(src: &str) -> Result<BaseValue, String> {
let tokens = Lexer::new(src.to_string(), 0)
.scans_tokens()
.map_err(|e| e.to_string())?;
let ast = Parser::new(tokens).parse().map_err(|e| e.to_string())?;
let locals = Resolver::resolve_program(&ast)
.map_err(|errors| errors.into_iter().next().unwrap().to_string())?;
let mut interpreter = Interpreter::new();
interpreter.set_locals(locals);
let mut last = BaseValue::Nil;
for stmt in ast {
last = interpreter.evaluate(stmt).map_err(|e| e.to_string())?;
}
Ok(last)
}
#[test]
fn block_local_variables_evaluate() {
assert_eq!(run("do x := 10; x + 5; end").unwrap().to_string(), "15");
}
#[test]
fn resolved_nested_closure_reads_captured_local() {
// `count` is read from inside a nested function, two scopes up; the
// resolver records distance 2 and the interpreter uses `get_at(2, ..)`.
let src = "\
make :: fn (): Any do
count := 10;
get :: fn (): Number do
return count;
end;
return get;
end;
g := make();
g();
";
assert_eq!(run(src).unwrap().to_string(), "10");
}
#[test]
fn use_before_initializer_is_rejected() {
let err = run("do x := x; end").expect_err("should be a resolution error");
assert!(err.contains("its own initializer"));
}
#[test]
fn top_level_return_is_rejected() {
let err = run("return 1;").expect_err("should be a resolution error");
assert!(err.contains("top-level"));
}
#[test]
fn duplicate_declaration_in_block_is_rejected() {
let err = run("do x := 1; x := 2; end").expect_err("should be a resolution error");
assert!(err.contains("Already a variable"));
}