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 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}