Skip to main content

sim_lib_view_math/
symbolic.rs

1//! Symbolic-expression tree lens.
2//!
3//! A symbolic expression is a SIM `Expr` (calls, symbols, numbers). The lens
4//! renders it as a `scene/tree`: each operator is a branch labelled by its head,
5//! each leaf an atom. It never evaluates the expression; it shows its structure.
6
7use sim_kernel::{Expr, Symbol};
8use sim_lib_scene::node;
9
10/// The symbolic lens id.
11pub const SYMBOLIC_LENS: &str = "view:math-symbolic";
12
13/// Render a symbolic expression as a `scene/tree`.
14pub fn symbolic_tree(expr: &Expr) -> Expr {
15    match expr {
16        Expr::Call { operator, args } => {
17            let mut nodes = vec![leaf(&format!("op: {}", render_head(operator)))];
18            nodes.extend(args.iter().map(symbolic_tree));
19            branch(&render_head(operator), nodes)
20        }
21        Expr::Infix {
22            operator,
23            left,
24            right,
25        } => branch(
26            &operator.to_string(),
27            vec![symbolic_tree(left), symbolic_tree(right)],
28        ),
29        Expr::Prefix { operator, arg } | Expr::Postfix { operator, arg } => {
30            branch(&operator.to_string(), vec![symbolic_tree(arg)])
31        }
32        Expr::List(items) | Expr::Vector(items) => {
33            branch("seq", items.iter().map(symbolic_tree).collect())
34        }
35        Expr::Symbol(symbol) | Expr::Local(symbol) => leaf(&symbol.as_qualified_str()),
36        Expr::Number(number) => leaf(&number.canonical),
37        Expr::String(text) => leaf(&format!("{text:?}")),
38        Expr::Bool(flag) => leaf(&flag.to_string()),
39        Expr::Nil => leaf("nil"),
40        other => leaf(&format!("{other:?}")),
41    }
42}
43
44fn render_head(operator: &Expr) -> String {
45    match operator {
46        Expr::Symbol(symbol) => symbol.as_qualified_str(),
47        other => format!("{other:?}"),
48    }
49}
50
51fn branch(label: &str, nodes: Vec<Expr>) -> Expr {
52    node(
53        "tree",
54        vec![
55            ("label", Expr::String(label.to_owned())),
56            ("nodes", Expr::List(nodes)),
57        ],
58    )
59}
60
61fn leaf(text: &str) -> Expr {
62    node("text", vec![("text", Expr::String(text.to_owned()))])
63}
64
65/// A small symbolic builder for tests and demos: `(operator arg ...)`.
66pub fn call(operator: &str, args: Vec<Expr>) -> Expr {
67    Expr::Call {
68        operator: Box::new(Expr::Symbol(Symbol::new(operator))),
69        args,
70    }
71}