Skip to main content

yield_error/
yield_error.rs

1//! End-to-end demonstration of error propagation through yield.
2//!
3//! The script declares a Result-shaped enum and the yield expression
4//! evaluates to a value of that enum's type at each resume. The host
5//! calls `Vm::resume(Value::Enum(...Ok...))` for success and
6//! `Vm::resume_err(Value::Enum(...Err...))` for failure. Both are
7//! routed through the same operand-stack mechanism; `resume_err` is
8//! a documentation alias that signals intent. The script handles
9//! both cases by pattern matching.
10//!
11//! Idiomatic patterns for B7. The script may also use script-defined
12//! `Option<T>`-shaped enums or any other variant union appropriate
13//! to the dialogue surface. The host honors the script's declared
14//! type by constructing the corresponding `Value::Enum` payload.
15//!
16//! WCET note. Error propagation introduces no new runtime mechanism
17//! beyond the existing yield/resume cycle. The bytecode does not
18//! change. The match-arm dispatch is bounded by the number of arms
19//! at compile time, so WCET analysis applies unchanged.
20//!
21//! Run with: `cargo run --example yield_error`
22
23use keleusma::compiler::compile;
24use keleusma::lexer::tokenize;
25use keleusma::parser::parse;
26use keleusma::vm::{DEFAULT_ARENA_CAPACITY, Vm, VmState};
27use keleusma::{Arena, Value};
28
29fn main() {
30    let src = r#"
31        enum Reply { Ok(i64), Err }
32        loop main(input: Reply) -> i64 {
33            let reply = yield 0;
34            match reply {
35                Reply::Ok(v) => v,
36                Reply::Err => -1,
37            }
38        }
39    "#;
40    let tokens = tokenize(src).expect("lex");
41    let program = parse(&tokens).expect("parse");
42    let module = compile(&program).expect("compile");
43    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
44    let mut vm = Vm::new(module, &arena).expect("verify");
45
46    let seed = Value::Enum {
47        type_name: String::from("Reply"),
48        variant: String::from("Ok"),
49        fields: alloc_vec(Value::Int(0)),
50    };
51    match vm.call(&[seed]).expect("call") {
52        VmState::Yielded(v) => println!("first yield: {:?}", v),
53        other => panic!("expected yield, got {:?}", other),
54    }
55
56    println!("simulating successful host reply Ok(42)");
57    let success = Value::Enum {
58        type_name: String::from("Reply"),
59        variant: String::from("Ok"),
60        fields: alloc_vec(Value::Int(42)),
61    };
62    match vm.resume(success).expect("resume ok") {
63        VmState::Reset => println!("script reset after Ok arm"),
64        other => panic!("expected reset, got {:?}", other),
65    }
66
67    println!("starting next iteration");
68    let seed2 = Value::Enum {
69        type_name: String::from("Reply"),
70        variant: String::from("Ok"),
71        fields: alloc_vec(Value::Int(0)),
72    };
73    match vm.resume(seed2).expect("resume seed") {
74        VmState::Yielded(_) => println!("yielded (waiting for reply)"),
75        other => panic!("expected yield, got {:?}", other),
76    }
77
78    println!("simulating failure host reply Err");
79    let err = Value::Enum {
80        type_name: String::from("Reply"),
81        variant: String::from("Err"),
82        fields: alloc::vec::Vec::new(),
83    };
84    match vm.resume_err(err).expect("resume_err") {
85        VmState::Reset => println!("script reset after Err arm"),
86        other => panic!("expected reset on err, got {:?}", other),
87    }
88
89    println!("yield error propagation executed end to end");
90}
91
92extern crate alloc;
93
94fn alloc_vec(v: Value) -> alloc::vec::Vec<Value> {
95    alloc::vec![v]
96}