Skip to main content

sim_kernel/eval/
runtime.rs

1use crate::{
2    env::Cx,
3    error::{Error, Result},
4    eval::{Demand, Phase},
5    expr::Expr,
6    id::ClassId,
7    value::Value,
8};
9
10fn class_id_of(cx: &mut Cx, value: &Value) -> Result<Option<ClassId>> {
11    let class = value.object().class(cx)?;
12    Ok(class.object().as_class().map(|class| class.id()))
13}
14
15/// Forces `value` to satisfy `demand` using the kernel's default rules.
16///
17/// This is the reference [`force`](crate::EvalPolicy::force) implementation:
18/// it unwraps thunks, coerces to expression or boolean forms, and checks
19/// class membership as the [`Demand`] requires. The standard eval policies
20/// delegate to it; libraries may reuse or replace it.
21pub fn force_default(cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
22    match demand {
23        Demand::Never => Ok(value),
24        Demand::Expr => {
25            let expr = value.object().as_expr(cx)?;
26            cx.factory().expr(expr)
27        }
28        Demand::Value => {
29            if let Some(thunk) = value.object().as_thunk() {
30                thunk.force(cx, demand)
31            } else {
32                Ok(value)
33            }
34        }
35        Demand::Bool => {
36            let forced = if let Some(thunk) = value.object().as_thunk() {
37                thunk.force(cx, demand)?
38            } else {
39                value
40            };
41            let truth = forced.object().truth(cx)?;
42            cx.factory().bool(truth)
43        }
44        Demand::Class(expected) => {
45            let forced = if let Some(thunk) = value.object().as_thunk() {
46                thunk.force(cx, demand)?
47            } else {
48                value
49            };
50            let found = class_id_of(cx, &forced)?.ok_or(Error::TypeMismatch {
51                expected: "class-backed value",
52                found: "non-class-backed value",
53            })?;
54            if found == expected {
55                Ok(forced)
56            } else {
57                Err(Error::WrongClass { expected, found })
58            }
59        }
60        Demand::Shape(_) => {
61            if let Some(thunk) = value.object().as_thunk() {
62                thunk.force(cx, demand)
63            } else {
64                Ok(value)
65            }
66        }
67    }
68}
69
70/// Evaluates `expr` to a value using the kernel's default tree-walk.
71///
72/// This is the reference [`eval_expr`](crate::EvalPolicy::eval_expr)
73/// implementation: it expands macros for [`Phase::Eval`](crate::Phase::Eval),
74/// then walks the [`Expr`] graph, resolving symbols and dispatching calls.
75/// The standard eval policies delegate to it; libraries may reuse or replace
76/// it with their own evaluation semantics.
77pub fn eval_expr_default(cx: &mut Cx, expr: Expr) -> Result<Value> {
78    let expr = cx.expand_macros(Phase::Eval, expr)?;
79    match expr {
80        Expr::Nil => cx.factory().nil(),
81        Expr::Bool(value) => cx.factory().bool(value),
82        Expr::Number(number) => cx.factory().number_literal(number.domain, number.canonical),
83        Expr::Symbol(symbol) => {
84            if let Some(value) = cx.env().get(&symbol) {
85                Ok(value.clone())
86            } else if let Ok(function) = cx.resolve_function(&symbol) {
87                Ok(function)
88            } else if let Ok(class) = cx.resolve_class(&symbol) {
89                Ok(class)
90            } else if let Ok(shape) = cx.resolve_shape(&symbol) {
91                Ok(shape)
92            } else if let Ok(value) = cx.resolve_value(&symbol) {
93                Ok(value)
94            } else {
95                cx.resolve_unbound_symbol(symbol)
96            }
97        }
98        Expr::Local(symbol) => cx.factory().expr(Expr::Local(symbol)),
99        Expr::String(value) => cx.factory().string(value),
100        Expr::Bytes(value) => cx.factory().bytes(value),
101        Expr::List(items) => {
102            let values = items
103                .into_iter()
104                .map(|item| eval_expr_default(cx, item))
105                .collect::<Result<Vec<_>>>()?;
106            cx.factory().list(values)
107        }
108        Expr::Vector(items) => {
109            let values = items
110                .into_iter()
111                .map(|item| eval_expr_default(cx, item))
112                .collect::<Result<Vec<_>>>()?;
113            cx.factory().list(values)
114        }
115        Expr::Map(entries) => {
116            let entries = entries
117                .into_iter()
118                .map(|(key, value)| {
119                    let key_expr = eval_expr_default(cx, key)?.object().as_expr(cx)?;
120                    let Expr::Symbol(key_symbol) = key_expr else {
121                        return Err(Error::TypeMismatch {
122                            expected: "symbol key",
123                            found: "non-symbol key",
124                        });
125                    };
126                    Ok((key_symbol, eval_expr_default(cx, value)?))
127                })
128                .collect::<Result<Vec<_>>>()?;
129            cx.factory().table(entries)
130        }
131        Expr::Set(items) => {
132            let values = items
133                .into_iter()
134                .map(|item| eval_expr_default(cx, item))
135                .collect::<Result<Vec<_>>>()?;
136            cx.factory().list(values)
137        }
138        Expr::Call { operator, args } => {
139            if let Expr::Symbol(name) = operator.as_ref()
140                && !cx.symbol_is_bound(name)
141            {
142                cx.resolve_unbound_call(name.clone(), args)
143            } else {
144                let callable = cx.eval_expr(*operator)?;
145                cx.call_exprs(callable, args)
146            }
147        }
148        Expr::Block(items) => {
149            let mut last = cx.factory().nil()?;
150            for item in items {
151                last = eval_expr_default(cx, item)?;
152            }
153            Ok(last)
154        }
155        Expr::Quote { expr, .. } => cx.factory().expr(*expr),
156        Expr::Annotated { expr, .. } => eval_expr_default(cx, *expr),
157        Expr::Extension { .. }
158        | Expr::Infix { .. }
159        | Expr::Prefix { .. }
160        | Expr::Postfix { .. } => cx.factory().expr(expr),
161    }
162}