Skip to main content

amaru_uplc/machine/
cek.rs

1use bumpalo::collections::Vec as BumpVec;
2
3use crate::{
4    arena::Arena,
5    binder::Eval,
6    machine::{
7        context::Context, cost_model::builtin_costs::BuiltinCostModel, env::Env,
8        state::MachineState,
9    },
10    term::Term,
11};
12
13use super::{
14    cost_model::StepKind,
15    discharge,
16    info::MachineInfo,
17    runtime::{BuiltinSemantics, Runtime},
18    value::Value,
19    CostModel, ExBudget, MachineError,
20};
21
22pub struct Machine<'a, B: BuiltinCostModel> {
23    pub(super) arena: &'a Arena,
24    ex_budget: ExBudget,
25    unbudgeted_steps: [u8; 10],
26    pub(super) costs: CostModel<B>,
27    slippage: u8,
28    pub(super) logs: Vec<String>,
29    pub(super) semantics: BuiltinSemantics,
30}
31
32impl<'a, B: BuiltinCostModel> Machine<'a, B> {
33    pub fn new(
34        arena: &'a Arena,
35        initial_budget: ExBudget,
36        costs: CostModel<B>,
37        semantics: BuiltinSemantics,
38    ) -> Self {
39        Machine {
40            arena,
41            ex_budget: initial_budget,
42            unbudgeted_steps: [0; 10],
43            costs,
44            slippage: 200,
45            logs: Vec::new(),
46            semantics,
47        }
48    }
49
50    pub fn info(self) -> MachineInfo {
51        MachineInfo {
52            consumed_budget: self.ex_budget,
53            logs: self.logs,
54        }
55    }
56
57    pub fn run<V>(&mut self, term: &'a Term<'a, V>) -> Result<&'a Term<'a, V>, MachineError<'a, V>>
58    where
59        V: Eval<'a>,
60    {
61        self.spend_budget(self.costs.machine_startup)?;
62
63        let initial_context = Context::no_frame(self.arena);
64
65        let mut state =
66            MachineState::compute(self.arena, initial_context, Env::new_in(self.arena), term);
67
68        loop {
69            let step = match state {
70                MachineState::Compute(context, env, term) => self.compute(context, env, term),
71                MachineState::Return(context, value) => self.return_compute(context, value),
72                MachineState::Done(term) => {
73                    return Ok(term);
74                }
75            };
76
77            state = step?;
78        }
79    }
80
81    pub fn compute<V>(
82        &mut self,
83        context: &'a Context<'a, V>,
84        env: &'a Env<'a, V>,
85        term: &'a Term<'a, V>,
86    ) -> Result<&'a mut MachineState<'a, V>, MachineError<'a, V>>
87    where
88        V: Eval<'a>,
89    {
90        match term {
91            Term::Var(name) => {
92                self.step_and_maybe_spend(StepKind::Var)?;
93
94                let value = env
95                    .lookup(name.index())
96                    .ok_or(MachineError::OpenTermEvaluated(term))?;
97
98                let state = MachineState::return_(self.arena, context, value);
99
100                Ok(state)
101            }
102            Term::Lambda { parameter, body } => {
103                self.step_and_maybe_spend(StepKind::Lambda)?;
104
105                let value = Value::lambda(self.arena, *parameter, body, env);
106
107                let state = MachineState::return_(self.arena, context, value);
108
109                Ok(state)
110            }
111            Term::Apply { function, argument } => {
112                self.step_and_maybe_spend(StepKind::Apply)?;
113
114                let frame = Context::frame_await_fun_term(self.arena, env, argument, context);
115
116                let state = MachineState::compute(self.arena, frame, env, function);
117
118                Ok(state)
119            }
120            Term::Delay(body) => {
121                self.step_and_maybe_spend(StepKind::Delay)?;
122
123                let value = Value::delay(self.arena, body, env);
124
125                let state = MachineState::return_(self.arena, context, value);
126
127                Ok(state)
128            }
129            Term::Force(body) => {
130                self.step_and_maybe_spend(StepKind::Force)?;
131
132                let frame = Context::frame_force(self.arena, context);
133
134                let state = MachineState::compute(self.arena, frame, env, body);
135
136                Ok(state)
137            }
138            Term::Constr { tag, fields } => {
139                self.step_and_maybe_spend(StepKind::Constr)?;
140
141                if let Some((first, terms)) = fields.split_first() {
142                    let frame = Context::frame_constr_empty(self.arena, env, *tag, terms, context);
143
144                    let state = MachineState::compute(self.arena, frame, env, first);
145
146                    Ok(state)
147                } else {
148                    let value = Value::constr_empty(self.arena, *tag);
149
150                    let state = MachineState::return_(self.arena, context, value);
151
152                    Ok(state)
153                }
154            }
155            Term::Case { constr, branches } => {
156                self.step_and_maybe_spend(StepKind::Case)?;
157
158                let frame = Context::frame_cases(self.arena, env, branches, context);
159
160                let state = MachineState::compute(self.arena, frame, env, constr);
161
162                Ok(state)
163            }
164            Term::Constant(constant) => {
165                self.step_and_maybe_spend(StepKind::Constant)?;
166
167                let value = Value::con(self.arena, constant);
168
169                let state = MachineState::return_(self.arena, context, value);
170
171                Ok(state)
172            }
173            Term::Builtin(fun) => {
174                self.step_and_maybe_spend(StepKind::Builtin)?;
175
176                let runtime = Runtime::new(self.arena, fun);
177
178                let value = Value::builtin(self.arena, runtime);
179
180                let state = MachineState::return_(self.arena, context, value);
181
182                Ok(state)
183            }
184            Term::Error => Err(MachineError::ExplicitErrorTerm),
185        }
186    }
187
188    pub fn return_compute<V>(
189        &mut self,
190        context: &'a Context<'a, V>,
191        value: &'a Value<'a, V>,
192    ) -> Result<&'a mut MachineState<'a, V>, MachineError<'a, V>>
193    where
194        V: Eval<'a>,
195    {
196        match context {
197            Context::FrameAwaitFunTerm(arg_env, argument, context) => {
198                let context = Context::frame_await_arg(self.arena, value, context);
199
200                let state = MachineState::compute(self.arena, context, arg_env, argument);
201
202                Ok(state)
203            }
204            Context::FrameAwaitArg(function, context) => {
205                self.apply_evaluate(context, function, value)
206            }
207            Context::FrameAwaitFunValue(argument, context) => {
208                self.apply_evaluate(context, value, argument)
209            }
210            Context::FrameForce(context) => self.force_evaluate(context, value),
211            Context::FrameConstr(env, tag, terms, values, context) => {
212                let mut new_values =
213                    BumpVec::with_capacity_in(values.len() + 1, self.arena.as_bump());
214
215                for value in values.iter() {
216                    new_values.push(*value);
217                }
218
219                new_values.push(value);
220
221                let values = self.arena.alloc(new_values);
222
223                if let Some((first, terms)) = terms.split_first() {
224                    let frame =
225                        Context::frame_constr(self.arena, env, *tag, terms, values, context);
226
227                    let state = MachineState::compute(self.arena, frame, env, first);
228
229                    Ok(state)
230                } else {
231                    let value = Value::constr(self.arena, *tag, values);
232
233                    let state = MachineState::return_(self.arena, context, value);
234
235                    Ok(state)
236                }
237            }
238            Context::FrameCases(env, branches, context) => match value {
239                Value::Constr(tag, fields) => {
240                    // kind of awkward tbh cause tag is already usize so how can it be bigger
241                    // than the max
242                    // if *tag > usize::MAX {
243                    //     return Err(MachineError::MaxConstrTagExceeded(value));
244                    // }
245
246                    if let Some(branch) = branches.get(*tag) {
247                        let frame = self.transfer_arg_stack(fields, context);
248
249                        let state = MachineState::compute(self.arena, frame, env, branch);
250
251                        Ok(state)
252                    } else {
253                        Err(MachineError::MissingCaseBranch(branches, value))
254                    }
255                }
256                v => Err(MachineError::NonConstrScrutinized(v)),
257            },
258            Context::NoFrame => {
259                if self.unbudgeted_steps[9] > 0 {
260                    self.spend_unbudgeted_steps()?;
261                }
262
263                let term = discharge::value_as_term(self.arena, value);
264
265                let state = MachineState::done(self.arena, term);
266
267                Ok(state)
268            }
269        }
270    }
271
272    fn force_evaluate<V>(
273        &mut self,
274        context: &'a Context<'a, V>,
275        value: &'a Value<'a, V>,
276    ) -> Result<&'a mut MachineState<'a, V>, MachineError<'a, V>>
277    where
278        V: Eval<'a>,
279    {
280        match value {
281            Value::Delay(term, env) => Ok(MachineState::compute(self.arena, context, env, term)),
282            Value::Builtin(runtime) => {
283                if runtime.needs_force() {
284                    let value = if runtime.is_ready() {
285                        self.call(runtime)?
286                    } else {
287                        Value::builtin(self.arena, runtime.force(self.arena))
288                    };
289
290                    let state = MachineState::return_(self.arena, context, value);
291
292                    Ok(state)
293                } else {
294                    let term = discharge::value_as_term(self.arena, value);
295
296                    Err(MachineError::BuiltinTermArgumentExpected(term))
297                }
298            }
299            rest => Err(MachineError::NonPolymorphicInstantiation(rest)),
300        }
301    }
302
303    fn apply_evaluate<V>(
304        &mut self,
305        context: &'a Context<'a, V>,
306        function: &'a Value<'a, V>,
307        argument: &'a Value<'a, V>,
308    ) -> Result<&'a mut MachineState<'a, V>, MachineError<'a, V>>
309    where
310        V: Eval<'a>,
311    {
312        match function {
313            Value::Lambda { body, env, .. } => {
314                let new_env = env.push(self.arena, argument);
315
316                let state = MachineState::compute(self.arena, context, new_env, body);
317
318                Ok(state)
319            }
320            Value::Builtin(runtime) => {
321                if !runtime.needs_force() && runtime.is_arrow() {
322                    let runtime = runtime.push(self.arena, argument);
323
324                    let value = if runtime.is_ready() {
325                        self.eval_builtin_app(runtime)?
326                    } else {
327                        Value::builtin(self.arena, runtime)
328                    };
329
330                    let state = MachineState::return_(self.arena, context, value);
331
332                    Ok(state)
333                } else {
334                    let term = discharge::value_as_term(self.arena, function);
335
336                    Err(MachineError::UnexpectedBuiltinTermArgument(term))
337                }
338            }
339            rest => Err(MachineError::NonFunctionApplication(argument, rest)),
340        }
341    }
342
343    fn eval_builtin_app<V>(
344        &mut self,
345        runtime: &'a Runtime<'a, V>,
346    ) -> Result<&'a Value<'a, V>, MachineError<'a, V>>
347    where
348        V: Eval<'a>,
349    {
350        self.call(runtime)
351    }
352
353    fn transfer_arg_stack<V>(
354        &mut self,
355        fields: &'a [&'a Value<'a, V>],
356        context: &'a Context<'a, V>,
357    ) -> &'a Context<'a, V>
358    where
359        V: Eval<'a>,
360    {
361        let mut c = context;
362
363        for field in fields.iter().rev() {
364            c = Context::frame_await_fun_value(self.arena, *field, c);
365        }
366
367        c
368    }
369
370    fn step_and_maybe_spend<V>(&mut self, step: StepKind) -> Result<(), MachineError<'a, V>>
371    where
372        V: Eval<'a>,
373    {
374        let index = step as usize;
375
376        self.unbudgeted_steps[index] += 1;
377        self.unbudgeted_steps[9] += 1;
378
379        if self.unbudgeted_steps[9] >= self.slippage {
380            self.spend_unbudgeted_steps()?;
381        }
382
383        Ok(())
384    }
385
386    fn spend_unbudgeted_steps<V>(&mut self) -> Result<(), MachineError<'a, V>>
387    where
388        V: Eval<'a>,
389    {
390        for step_kind in 0..self.unbudgeted_steps.len() - 1 {
391            let mut unspent_step_budget = self.costs.machine_costs.get(step_kind);
392
393            unspent_step_budget.occurrences(self.unbudgeted_steps[step_kind] as i64);
394
395            self.spend_budget(unspent_step_budget)?;
396
397            self.unbudgeted_steps[step_kind] = 0;
398        }
399
400        self.unbudgeted_steps[9] = 0;
401
402        Ok(())
403    }
404
405    pub(super) fn spend_budget<V>(
406        &mut self,
407        spend_budget: ExBudget,
408    ) -> Result<(), MachineError<'a, V>>
409    where
410        V: Eval<'a>,
411    {
412        self.ex_budget.mem -= spend_budget.mem;
413        self.ex_budget.cpu -= spend_budget.cpu;
414
415        if self.ex_budget.mem < 0 || self.ex_budget.cpu < 0 {
416            Err(MachineError::OutOfExError(self.ex_budget))
417        } else {
418            Ok(())
419        }
420    }
421}