feat: implement built-in State, Reader, and Fail effects
Add runtime support for the State, Reader, and Fail effects that were already defined in the type system. These effects can now be used in effectful code blocks. Changes: - Add builtin_state and builtin_reader fields to Interpreter - Implement State.get and State.put in handle_builtin_effect - Implement Reader.ask in handle_builtin_effect - Add Reader effect definition to types.rs - Add Reader to built-in effects list in typechecker - Add set_state/get_state/set_reader/get_reader methods - Add 6 new tests for built-in effects - Add examples/builtin_effects.lux demonstrating usage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
94
src/main.rs
94
src/main.rs
@@ -1156,6 +1156,100 @@ c")"#;
|
||||
assert!(result.unwrap_err().contains("pure but has effects"));
|
||||
}
|
||||
|
||||
// Built-in effect tests
|
||||
mod effect_tests {
|
||||
use crate::interpreter::{Interpreter, Value};
|
||||
use crate::parser::Parser;
|
||||
use crate::typechecker::TypeChecker;
|
||||
|
||||
fn run_with_effects(source: &str, initial_state: Value, reader_value: Value) -> Result<(String, String), String> {
|
||||
let program = Parser::parse_source(source).map_err(|e| e.to_string())?;
|
||||
let mut checker = TypeChecker::new();
|
||||
checker.check_program(&program).map_err(|errors| {
|
||||
errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n")
|
||||
})?;
|
||||
let mut interp = Interpreter::new();
|
||||
interp.set_state(initial_state);
|
||||
interp.set_reader(reader_value);
|
||||
let result = interp.run(&program).map_err(|e| e.to_string())?;
|
||||
let final_state = interp.get_state();
|
||||
Ok((format!("{}", result), format!("{}", final_state)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_get() {
|
||||
let source = r#"
|
||||
fn getValue(): Int with {State} = State.get()
|
||||
let result = run getValue() with {}
|
||||
"#;
|
||||
let (result, _) = run_with_effects(source, Value::Int(42), Value::Unit).unwrap();
|
||||
assert_eq!(result, "42");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_put() {
|
||||
let source = r#"
|
||||
fn setValue(x: Int): Unit with {State} = State.put(x)
|
||||
let result = run setValue(100) with {}
|
||||
"#;
|
||||
let (_, final_state) = run_with_effects(source, Value::Int(0), Value::Unit).unwrap();
|
||||
assert_eq!(final_state, "100");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_get_and_put() {
|
||||
let source = r#"
|
||||
fn increment(): Int with {State} = {
|
||||
let current = State.get()
|
||||
State.put(current + 1)
|
||||
State.get()
|
||||
}
|
||||
let result = run increment() with {}
|
||||
"#;
|
||||
let (result, final_state) = run_with_effects(source, Value::Int(10), Value::Unit).unwrap();
|
||||
assert_eq!(result, "11");
|
||||
assert_eq!(final_state, "11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reader_ask() {
|
||||
let source = r#"
|
||||
fn getConfig(): String with {Reader} = Reader.ask()
|
||||
let result = run getConfig() with {}
|
||||
"#;
|
||||
let (result, _) = run_with_effects(source, Value::Unit, Value::String("config_value".to_string())).unwrap();
|
||||
// Value's Display includes quotes for strings
|
||||
assert_eq!(result, "\"config_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fail_effect() {
|
||||
let source = r#"
|
||||
fn failing(): Int with {Fail} = Fail.fail("oops")
|
||||
let result = run failing() with {}
|
||||
"#;
|
||||
let result = run_with_effects(source, Value::Unit, Value::Unit);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("oops"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_combined_effects() {
|
||||
let source = r#"
|
||||
fn compute(): Int with {State, Reader} = {
|
||||
let config = Reader.ask()
|
||||
let current = State.get()
|
||||
State.put(current + 1)
|
||||
current
|
||||
}
|
||||
let result = run compute() with {}
|
||||
"#;
|
||||
let (result, final_state) = run_with_effects(source, Value::Int(5), Value::String("test".to_string())).unwrap();
|
||||
assert_eq!(result, "5");
|
||||
assert_eq!(final_state, "6");
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic rendering tests
|
||||
mod diagnostic_tests {
|
||||
use crate::diagnostics::{render_diagnostic_plain, Diagnostic, Severity};
|
||||
|
||||
Reference in New Issue
Block a user