logicaffeine_compile/frame_eval.rs
1//! FrameEval — evaluate a LOGOS expression against a paused frame's bindings.
2//!
3//! ONE evaluator behind every debug surface: conditional breakpoints, the debug
4//! REPL, DAP `evaluate`, watch expressions, and the Studio Prove tab. It reuses
5//! the tree-walker — the differential oracle for the VM — so the answer
6//! provably agrees with program semantics: it IS program semantics. Read-only:
7//! it observes a frame, never mutates it.
8
9/// One name→value binding in the paused frame. The value is given as LOGOS
10/// source (a literal or expression the tree-walker evaluates to seed the name).
11pub struct Binding<'a> {
12 pub name: &'a str,
13 pub value: &'a str,
14}
15
16/// Evaluate `expr` (LOGOS expression source) against `frame` (the paused
17/// bindings), returning its rendered value. Errors are the tree-walker's own,
18/// so a bad expression fails exactly as it would in a program.
19pub fn frame_eval(frame: &[Binding<'_>], expr: &str) -> Result<String, String> {
20 let mut src = String::from("## Main\n");
21 for b in frame {
22 src.push_str(&format!(" Let {} be {}.\n", b.name, b.value));
23 }
24 src.push_str(&format!(" Show {expr}.\n"));
25 let outcome = crate::compile::tw_outcome(&src);
26 match outcome.error {
27 Some(e) => Err(e),
28 None => Ok(outcome.output),
29 }
30}
31
32/// Evaluate a yes/no question against a frame, returning an English `Yes.`/`No.`
33/// (the debug REPL's `Is …?`). `Is X <pred>?` de-inverts to the condition
34/// `X is <pred>`, evaluated through the tree-walker's own `If`.
35pub fn frame_answer(frame: &[Binding<'_>], question: &str) -> Result<String, String> {
36 let clause = question.trim().trim_end_matches('?').trim();
37 // A question (`Is X <pred>`) de-inverts to `X is <pred>`; a declarative
38 // condition passes through unchanged.
39 let condition = if let Some(rest) = clause
40 .strip_prefix("Is ")
41 .or_else(|| clause.strip_prefix("is "))
42 {
43 match rest.split_once(' ') {
44 Some((subject, predicate)) => format!("{subject} is {predicate}"),
45 None => rest.to_string(),
46 }
47 } else {
48 clause.to_string()
49 };
50
51 let mut src = String::from("## Main\n");
52 for b in frame {
53 src.push_str(&format!(" Let {} be {}.\n", b.name, b.value));
54 }
55 src.push_str(&format!(
56 " If {condition}:\n Show \"yes\".\n Otherwise:\n Show \"no\".\n"
57 ));
58 let outcome = crate::compile::tw_outcome(&src);
59 match outcome.error {
60 Some(e) => Err(e),
61 None => Ok(match outcome.output.as_str() {
62 "yes" => "Yes.".to_string(),
63 "no" => "No.".to_string(),
64 other => other.to_string(),
65 }),
66 }
67}