use crate::{eval_with_host, Host, InterpError};
use rune_ast::Noun;
pub enum StepResult {
Done(Noun),
Yield {
tag: u64,
selector: Noun,
continuation: Continuation,
},
Err(InterpError),
}
pub struct Continuation {
pub subject: Noun,
pub formula: Noun,
}
pub fn eval_step(subject: &Noun, formula: &Noun) -> StepResult {
if let Noun::Cell(op, rest) = formula {
if matches!(op.as_ref(), Noun::Atom(16)) {
return step_hint(subject, rest);
}
}
match crate::eval(subject, formula) {
Ok(n) => StepResult::Done(n),
Err(e) => StepResult::Err(e),
}
}
pub fn resume(cont: Continuation, event: Noun) -> StepResult {
let new_subject = Noun::cell(event, cont.subject);
eval_step(&new_subject, &cont.formula)
}
pub fn run_with_events(
subject: &Noun,
formula: &Noun,
source: &mut MockEventSource,
) -> Result<Noun, InterpError> {
let mut step = eval_step(subject, formula);
loop {
match step {
StepResult::Done(n) => return Ok(n),
StepResult::Err(e) => return Err(e),
StepResult::Yield { continuation, .. } => {
let event = source.next().unwrap_or(Noun::Atom(0));
step = resume(continuation, event);
}
}
}
}
pub fn run_cell(
base: &Noun,
gate: &Noun,
init_state: Noun,
host: &mut dyn Host,
source: &mut MockEventSource,
) -> Result<Noun, InterpError> {
let mut state = init_state;
for event in source.by_ref() {
let subject = Noun::cell(event, Noun::cell(state.clone(), base.clone()));
state = eval_with_host(&subject, gate, host)?;
}
Ok(state)
}
pub struct MockEventSource {
events: std::collections::VecDeque<Noun>,
}
impl MockEventSource {
pub fn new(events: Vec<Noun>) -> Self {
MockEventSource { events: events.into() }
}
}
impl Iterator for MockEventSource {
type Item = Noun;
fn next(&mut self) -> Option<Noun> {
self.events.pop_front()
}
}
fn step_hint(subject: &Noun, rest: &Noun) -> StepResult {
let (hint_meta, body) = match pair(rest) {
Ok(p) => p,
Err(e) => return StepResult::Err(e),
};
let (tag_noun, selector_f) = match pair(&hint_meta) {
Ok(p) => p,
Err(e) => return StepResult::Err(e),
};
let tag = match tag_noun {
Noun::Atom(t) => t,
_ => 0,
};
let selector = match crate::eval(subject, &selector_f) {
Ok(s) => s,
Err(e) => return StepResult::Err(e),
};
StepResult::Yield {
tag,
selector,
continuation: Continuation { subject: subject.clone(), formula: body },
}
}
fn pair(noun: &Noun) -> Result<(Noun, Noun), InterpError> {
match noun {
Noun::Cell(h, t) => Ok((*h.clone(), *t.clone())),
_ => Err(InterpError { message: "event: expected cell".into() }),
}
}
#[cfg(test)]
mod tests {
use super::*;
use rune_ast::Noun::Atom;
fn a(n: u64) -> Noun { Noun::Atom(n) }
fn c(h: Noun, t: Noun) -> Noun { Noun::cell(h, t) }
#[test]
fn pure_formula_completes() {
let formula = c(a(1), a(42));
let result = eval_step(&a(0), &formula);
assert!(matches!(result, StepResult::Done(Atom(42))));
}
#[test]
fn hint_yields_then_resumes() {
let hint_meta = c(a(99), c(a(1), a(0)));
let body = c(a(1), a(42));
let formula = c(a(16), c(hint_meta, body));
let step = eval_step(&a(0), &formula);
match step {
StepResult::Yield { tag, continuation, .. } => {
assert_eq!(tag, 99);
let resumed = resume(continuation, a(7));
assert!(matches!(resumed, StepResult::Done(Atom(42))));
}
_ => panic!("expected Yield"),
}
}
#[test]
fn run_with_events_drives_loop() {
let hint_meta = c(c(a(1), a(1)), c(a(1), a(0)));
let body = c(a(1), a(55));
let formula = c(a(16), c(hint_meta, body));
let mut src = MockEventSource::new(vec![a(0)]);
let result = run_with_events(&a(0), &formula, &mut src).unwrap();
assert_eq!(result, a(55));
}
#[test]
fn event_injected_at_head_of_subject() {
let hint_meta = c(c(a(1), a(0)), c(a(1), a(0)));
let body = c(a(0), a(2)); let formula = c(a(16), c(hint_meta, body));
let mut src = MockEventSource::new(vec![a(99)]);
let result = run_with_events(&a(0), &formula, &mut src).unwrap();
assert_eq!(result, a(99));
}
#[test]
fn run_cell_reacts_per_event() {
use rune_ast::act;
struct Rec { emitted: Vec<Noun> }
impl Host for Rec {
fn perform(&mut self, _act: u64, args: &Noun, _caps: &Noun) -> Result<Noun, InterpError> {
self.emitted.push(args.clone());
Ok(Noun::Atom(0))
}
}
let gate = c(a(16), c(
c(a(act::EMIT), c(a(0), a(2))),
c(a(5), c(c(a(0), a(14)), c(a(1), a(1)))),
));
let mut host = Rec { emitted: vec![] };
let mut src = MockEventSource::new(vec![a(10), a(20), a(30)]);
let final_state = run_cell(&a(0), &gate, a(0), &mut host, &mut src).unwrap();
assert_eq!(host.emitted, vec![a(10), a(20), a(30)]); assert_eq!(final_state, a(3)); }
}