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