@@ -2,6 +2,7 @@ use bumpalo::{collections::Vec as BumpVec, Bump};
22
33use crate :: {
44 binder:: Eval ,
5+ constant:: Constant ,
56 machine:: { context:: Context , env:: Env , state:: MachineState } ,
67 term:: Term ,
78} ;
@@ -15,6 +16,9 @@ use super::{
1516 CostModel , ExBudget , MachineError ,
1617} ;
1718
19+ pub const CONS_BRANCH : usize = 0 ;
20+ pub const NILS_BRANCH : usize = 1 ;
21+
1822pub struct Machine < ' a > {
1923 pub ( super ) arena : & ' a Bump ,
2024 ex_budget : ExBudget ,
@@ -248,6 +252,106 @@ impl<'a> Machine<'a> {
248252 Err ( MachineError :: MissingCaseBranch ( branches, value) )
249253 }
250254 }
255+ Value :: Con ( constant) => match constant {
256+ Constant :: Integer ( scrutinee) => match usize:: try_from ( * scrutinee) {
257+ Ok ( scrutinee_usize) => branches
258+ . get ( scrutinee_usize)
259+ . map ( |branch| MachineState :: compute ( self . arena , context, env, branch) )
260+ . ok_or ( MachineError :: MissingCaseBranch ( branches, value) ) ,
261+ Err ( _) => Err ( MachineError :: ExplicitErrorTerm ) ,
262+ } ,
263+ Constant :: Boolean ( scrutinee) => {
264+ if branches. len ( ) > 2 {
265+ return Err ( MachineError :: TooManyCaseBranches ( branches, value) ) ;
266+ }
267+ branches
268+ . get ( * scrutinee as usize )
269+ . map ( |branch| MachineState :: compute ( self . arena , context, env, branch) )
270+ . ok_or ( MachineError :: MissingCaseBranch ( branches, value) )
271+ }
272+ Constant :: Unit => {
273+ if branches. len ( ) > 1 {
274+ return Err ( MachineError :: TooManyCaseBranches ( branches, value) ) ;
275+ }
276+ branches
277+ . get ( CONS_BRANCH )
278+ . map ( |branch| MachineState :: compute ( self . arena , context, env, branch) )
279+ . ok_or ( MachineError :: MissingCaseBranch ( branches, value) )
280+ }
281+ // Caseing on pairs expects a single branch that takes two arguments for each values of the pair.
282+ Constant :: ProtoPair ( _, _, left_constant, right_constant) => {
283+ if branches. len ( ) > 1 {
284+ return Err ( MachineError :: TooManyCaseBranches ( branches, value) ) ;
285+ }
286+ branches
287+ . get ( CONS_BRANCH )
288+ . map ( |branch| {
289+ let right_value: & Value < ' _ , V > =
290+ Value :: con ( self . arena , right_constant) ;
291+ let right_frame: & Context < ' _ , V > = Context :: frame_await_fun_value (
292+ self . arena ,
293+ right_value,
294+ context,
295+ ) ;
296+ let left_value = Value :: con ( self . arena , left_constant) ;
297+ let left_frame = Context :: frame_await_fun_value (
298+ self . arena ,
299+ left_value,
300+ right_frame,
301+ ) ;
302+ MachineState :: compute ( self . arena , left_frame, env, branch)
303+ } )
304+ . ok_or ( MachineError :: MissingCaseBranch ( branches, value) )
305+ }
306+ // When matching (case-ing) on a builtin list, exactly one or two branches are allowed:
307+ // - With a single branch, it is assumed the list is non-empty; the branch receives the head and tail as arguments.
308+ // If the list is actually empty, script evaluation will fail.
309+ // - With two branches, the nils branch is selected for the empty list (receiving no arguments),
310+ // and the cons branch is selected for a non-empty list (receiving the head and tail as arguments).
311+ //
312+ // Note: In the Haskell implementation, when a list contains only a single element,
313+ // the tail argument passed to the branch is an empty list.
314+ Constant :: ProtoList ( list_type, list) => {
315+ if branches. len ( ) > 2 {
316+ return Err ( MachineError :: TooManyCaseBranches ( branches, value) ) ;
317+ }
318+ if list. is_empty ( ) {
319+ branches
320+ . get ( NILS_BRANCH )
321+ . map ( |branch| {
322+ let frame = self . transfer_arg_stack ( & [ ] , context) ;
323+ MachineState :: compute ( self . arena , frame, env, branch)
324+ } )
325+ . ok_or ( MachineError :: MissingCaseBranch ( branches, value) )
326+ } else if let Some ( ( head, tail) ) = list. split_first ( ) {
327+ branches
328+ . get ( CONS_BRANCH )
329+ . map ( |branch| {
330+ let tail_value = if tail. is_empty ( ) {
331+ let empty_list_const =
332+ Constant :: proto_list ( self . arena , list_type, & [ ] ) ;
333+ Value :: con ( self . arena , empty_list_const)
334+ } else {
335+ Value :: con ( self . arena , tail. last ( ) . unwrap ( ) )
336+ } ;
337+
338+ let tail_frame = Context :: frame_await_fun_value (
339+ self . arena , tail_value, context,
340+ ) ;
341+ let head_value = Value :: con ( self . arena , head) ;
342+ let head_frame = Context :: frame_await_fun_value (
343+ self . arena , head_value, tail_frame,
344+ ) ;
345+
346+ MachineState :: compute ( self . arena , head_frame, env, branch)
347+ } )
348+ . ok_or ( MachineError :: MissingCaseBranch ( branches, value) )
349+ } else {
350+ Err ( MachineError :: ExplicitErrorTerm )
351+ }
352+ }
353+ _ => Err ( MachineError :: ExplicitErrorTerm ) ,
354+ } ,
251355 v => Err ( MachineError :: NonConstrScrutinized ( v) ) ,
252356 } ,
253357 Context :: NoFrame => {
0 commit comments