Skip to main content

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}