haskelujah_runtime/stack_chirho.rs
1// For God so loved the world that he gave his only begotten Son, that whoever
2// believes in him should not perish but have eternal life. — John 3:16
3
4//! # Evaluation stack
5//!
6//! The STG machine uses a stack of continuation frames. When evaluating
7//! an expression, the stack records what to do with the result:
8//!
9//! - `ApplyChirho` — apply the result to pending arguments
10//! - `UpdateChirho` — update a thunk with the result (lazy evaluation)
11//! - `CaseChirho` — scrutinise the result and choose a branch
12//! - `PrimOpChirho` — apply a primitive operation to the result
13
14use crate::value_chirho::{HeapAddrChirho, ValueChirho};
15
16/// A continuation frame on the evaluation stack.
17#[derive(Debug, Clone, PartialEq)]
18pub enum FrameChirho {
19 /// Apply the WHNF result to these pending arguments.
20 /// Used when we evaluate the function part of an application.
21 ApplyChirho { args_chirho: Vec<ValueChirho> },
22
23 /// Update the thunk at this address with the WHNF result.
24 /// Pushed before entering a thunk body.
25 UpdateChirho { thunk_addr_chirho: HeapAddrChirho },
26
27 /// Scrutinise the WHNF result. The `alts_chirho` index selects
28 /// which case branch to take based on the constructor tag.
29 /// The `default_chirho` is the fallback branch index.
30 CaseChirho {
31 /// Index into the code table for each constructor alt.
32 /// Keyed by DataConTagChirho.
33 alt_entries_chirho: Vec<(u16, u32)>,
34 /// Index into the code table for the default branch, if any.
35 default_entry_chirho: Option<u32>,
36 /// Saved arg registers from before the case (restored for alts
37 /// with no binders; prepended with constructor fields for alts
38 /// with binders).
39 saved_arg_regs_chirho: Vec<ValueChirho>,
40 },
41
42 /// Literal case dispatch waiting for the scrutinee to be forced.
43 /// Pushed when `CaseLitChirho` encounters a `HeapPtrChirho` scrutinee.
44 CaseLitChirho {
45 /// Literal alternatives: `(value, code_entry)`.
46 alt_entries_chirho: Vec<(ValueChirho, u32)>,
47 /// Default branch, if any.
48 default_entry_chirho: Option<u32>,
49 /// Saved arg registers to restore after forcing the scrutinee thunk.
50 saved_arg_regs_chirho: Vec<ValueChirho>,
51 },
52
53 /// A primitive operation waiting for its arguments to be forced.
54 PrimOpChirho {
55 op_chirho: PrimOpKindChirho,
56 /// Accumulated forced operands (already in WHNF / unboxed).
57 args_so_far_chirho: Vec<ValueChirho>,
58 /// Arguments still to be forced (may contain HeapPtrChirho thunks).
59 pending_args_chirho: Vec<ValueChirho>,
60 /// How many more arguments are needed.
61 remaining_chirho: u16,
62 },
63
64 /// Exception handler: catch# pushes this before evaluating the body.
65 /// If the body succeeds, the frame is popped and the result is returned.
66 /// If RuntimeErrorChirho propagates, the stack is unwound to this frame
67 /// and the handler is applied to the error message string.
68 CatchChirho {
69 /// Heap address of the handler closure (handler :: String -> IO a)
70 handler_addr_chirho: HeapAddrChirho,
71 /// Saved arg registers to restore when invoking the handler
72 saved_arg_regs_chirho: Vec<ValueChirho>,
73 },
74
75 /// try# frame: try# pushes this before evaluating the body IO action.
76 /// On success the result value is wrapped in a `Right` constructor on the
77 /// heap and returned. On RuntimeErrorChirho the stack is unwound to this
78 /// frame and the error message string is wrapped in `Left`.
79 /// The resulting heap address (pointing to `Left msg` or `Right val`) is
80 /// returned as the IO (Either String a) value.
81 TryFrameChirho {
82 /// Saved arg registers to restore on the error path.
83 saved_arg_regs_chirho: Vec<ValueChirho>,
84 },
85}
86
87/// Kinds of primitive operations.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89pub enum PrimOpKindChirho {
90 AddIntChirho,
91 SubIntChirho,
92 MulIntChirho,
93 DivIntChirho,
94 ModIntChirho,
95 NegIntChirho,
96 EqIntChirho,
97 NeIntChirho,
98 LtIntChirho,
99 LeIntChirho,
100 GtIntChirho,
101 GeIntChirho,
102 AddFloatChirho,
103 SubFloatChirho,
104 MulFloatChirho,
105 DivFloatChirho,
106 LtFloatChirho,
107 GtFloatChirho,
108 EqCharChirho,
109 OrdCharChirho,
110 PutStrLnChirho,
111 PutStrChirho,
112 /// Print a single character: putChar :: Char -> IO ()
113 PutCharChirho,
114 /// Monadic bind (>>=) for IO: execute first action, pass result to continuation
115 BindIOChirho,
116 /// Monadic return for IO: wrap a value in IO
117 ReturnIOChirho,
118 /// Monadic then (>>) for IO: execute first action, ignore result, execute second
119 ThenIOChirho,
120 /// Read a line from stdin: getLine :: IO String
121 GetLineChirho,
122 /// Read a single character from stdin: getChar :: IO Char
123 GetCharChirho,
124 /// Read entire file contents: readFile :: FilePath -> IO String
125 ReadFileChirho,
126 /// Write string to file: writeFile :: FilePath -> String -> IO ()
127 WriteFileChirho,
128 /// Append string to file: appendFile :: FilePath -> String -> IO ()
129 AppendFileChirho,
130 /// Runtime error: error# :: String -> a (halts evaluation)
131 ErrorChirho,
132 /// Undefined value: undefined# :: a (halts evaluation)
133 UndefinedChirho,
134 /// Evaluate first arg to WHNF, return second: seq# :: a -> b -> b
135 SeqChirho,
136 /// Force to WHNF and return in IO: evaluate :: a -> IO a
137 EvaluateChirho,
138 /// Force to NF and return: force :: NFData a => a -> a
139 ForceChirho,
140 /// Identity function: id# :: a -> a (returns first arg unchanged)
141 IdChirho,
142 /// Convert an Int to its String representation: showInt# :: Int -> String
143 ShowIntChirho,
144 /// Convert a Bool to its String representation: showBool# :: Bool -> String
145 ShowBoolChirho,
146 /// Boolean negation: not# :: Bool -> Bool
147 NotBoolChirho,
148 /// String concatenation: ++# :: String -> String -> String
149 AppendStrChirho,
150 /// String equality: eqStr# :: String -> String -> Bool
151 EqStrChirho,
152 /// String less-than: ltStr# :: String -> String -> Bool
153 LtStrChirho,
154 /// String compare: compareStr# :: String -> String -> Ordering
155 CompareStrChirho,
156 /// String length: lengthStr# :: String -> Int
157 LengthStrChirho,
158 /// Show a String: showStr# :: String -> String (wraps in quotes)
159 ShowStrChirho,
160 /// Float equality: eqFloat# :: Double -> Double -> Bool
161 EqFloatChirho,
162 /// Negate float: negateFloat# :: Double -> Double
163 NegFloatChirho,
164 /// Show a Double: showFloat# :: Double -> String
165 ShowFloatChirho,
166 /// Reciprocal: recip# :: Double -> Double
167 RecipFloatChirho,
168 /// enumFromTo# :: Int -> Int -> [Int] — build a list [from..to]
169 EnumFromToChirho,
170 /// enumFrom# :: Int -> [Int] — build an infinite list [from..] (capped at from+10000)
171 EnumFromChirho,
172 /// enumFromThen# :: Int -> Int -> [Int] — build infinite list [from,then..] (capped)
173 EnumFromThenChirho,
174 /// enumFromThenTo# :: Int -> Int -> Int -> [Int] — build [from,then..to]
175 EnumFromThenToChirho,
176 /// showList# :: [a] -> String — format a list for display
177 ShowListChirho,
178 /// readInt# :: String -> Int — parse an integer from a string
179 ReadIntChirho,
180 /// readFloat# :: String -> Double — parse a double from a string
181 ReadFloatChirho,
182 /// readBool# :: String -> Bool — parse a boolean from a string
183 ReadBoolChirho,
184 /// wordsStr# :: String -> [String] — split on whitespace
185 WordsStrChirho,
186 /// unwordsStr# :: [String] -> String — join with single spaces
187 UnwordsStrChirho,
188 /// takeStr# :: Int -> String -> String — take first N chars
189 TakeStrChirho,
190 /// dropStr# :: Int -> String -> String — drop first N chars
191 DropStrChirho,
192 /// concatStr# :: [String] -> String — concatenate all strings in a list
193 ConcatStrChirho,
194 /// intercalateStr# :: String -> [String] -> String — join with separator
195 IntercalateStrChirho,
196 /// fromIntegral# :: Int -> Double — convert integer to floating point
197 FromIntegralChirho,
198 /// ceiling# :: Double -> Int — round up to nearest integer
199 CeilingChirho,
200 /// floor# :: Double -> Int — round down to nearest integer
201 FloorChirho,
202 /// round# :: Double -> Int — round to nearest integer (banker's rounding)
203 RoundChirho,
204 /// truncate# :: Double -> Int — truncate toward zero
205 TruncateChirho,
206 /// compare# :: Int -> Int -> Ordering — compare two integers
207 CompareIntChirho,
208 /// compareChar# :: Char -> Char -> Ordering — compare two characters
209 CompareCharChirho,
210 /// compareFloat# :: Double -> Double -> Ordering — compare two doubles
211 CompareFloatChirho,
212 /// quot# :: Int -> Int -> Int — truncating integer division toward zero
213 QuotIntChirho,
214 /// rem# :: Int -> Int -> Int — remainder of truncating division
215 RemIntChirho,
216 /// chr# :: Int -> Char — convert code point to character
217 ChrChirho,
218 /// ord# :: Char -> Int — convert character to code point
219 OrdChirho,
220 /// isDigit# :: Char -> Bool — test if character is a digit
221 IsDigitChirho,
222 /// isAlpha# :: Char -> Bool — test if character is alphabetic
223 IsAlphaChirho,
224 /// isAlphaNum# :: Char -> Bool — test if character is alphanumeric
225 IsAlphaNumChirho,
226 /// isUpper# :: Char -> Bool — test if character is uppercase
227 IsUpperChirho,
228 /// isLower# :: Char -> Bool — test if character is lowercase
229 IsLowerChirho,
230 /// isSpace# :: Char -> Bool — test if character is whitespace
231 IsSpaceChirho,
232 /// toLower# :: Char -> Char — convert to lowercase
233 ToLowerChirho,
234 /// toUpper# :: Char -> Char — convert to uppercase
235 ToUpperChirho,
236 /// digitToInt# :: Char -> Int — convert digit char to int
237 DigitToIntChirho,
238 /// intToDigit# :: Int -> Char — convert int to digit char
239 IntToDigitChirho,
240
241 // ── Floating math primops ──
242 /// sin# :: Double -> Double
243 SinFloatChirho,
244 /// cos# :: Double -> Double
245 CosFloatChirho,
246 /// tan# :: Double -> Double
247 TanFloatChirho,
248 /// asin# :: Double -> Double
249 AsinFloatChirho,
250 /// acos# :: Double -> Double
251 AcosFloatChirho,
252 /// atan# :: Double -> Double
253 AtanFloatChirho,
254 /// exp# :: Double -> Double
255 ExpFloatChirho,
256 /// log# :: Double -> Double
257 LogFloatChirho,
258 /// sqrt# :: Double -> Double
259 SqrtFloatChirho,
260 /// pi# :: -> Double (nullary constant, dispatched as unary ignoring arg)
261 PiFloatChirho,
262
263 // ── Power/exponentiation primops ──
264 /// ^# :: Int -> Int -> Int — integer exponentiation
265 PowIntChirho,
266 /// **# :: Double -> Double -> Double — floating-point exponentiation
267 PowFloatChirho,
268
269 // ── Show for compound types ──
270 /// showMaybe# :: Maybe a -> String — show a Maybe value
271 ShowMaybeChirho,
272 /// showTuple2# :: (a, b) -> String — show a 2-tuple
273 ShowTuple2Chirho,
274 /// showEither# :: Either a b -> String — show an Either value
275 ShowEitherChirho,
276 /// showOrdering# :: Ordering -> String — show an Ordering value
277 ShowOrderingChirho,
278
279 // ── Additional IO operations ──
280 /// getContents :: IO String — read all stdin as a single String
281 GetContentsChirho,
282 /// interact :: (String -> String) -> IO () — apply function to stdin, write result to stdout
283 InteractChirho,
284 /// print :: Show a => a -> IO () — show value then putStrLn
285 PrintChirho,
286
287 // ── String operations ──
288 /// lines# :: String -> [String] — split string by newlines
289 LinesChirho,
290 /// unlines# :: [String] -> String — join strings with newlines
291 UnlinesChirho,
292
293 // ── IORef operations ──
294 /// newIORef# :: a -> IO (IORef a) — create a new mutable reference
295 NewIORefChirho,
296 /// readIORef# :: IORef a -> IO a — read the current value
297 ReadIORefChirho,
298 /// writeIORef# :: IORef a -> a -> IO () — overwrite the value
299 WriteIORefChirho,
300 /// modifyIORef# :: IORef a -> (a -> a) -> IO () — apply function to value
301 ModifyIORefChirho,
302
303 // ── ST monad operations ──
304 /// newSTRef# :: a -> ST s (STRef s a) — create a new mutable reference in ST
305 NewSTRefChirho,
306 /// readSTRef# :: STRef s a -> ST s a — read a mutable reference in ST
307 ReadSTRefChirho,
308 /// writeSTRef# :: STRef s a -> a -> ST s () — write to a mutable reference in ST
309 WriteSTRefChirho,
310 /// modifySTRef# :: STRef s a -> (a -> a) -> ST s () — apply function to ST ref value
311 ModifySTRefChirho,
312 /// runST# :: (forall s. ST s a) -> a — execute an ST computation purely
313 RunSTChirho,
314
315 // ── Exception handling ──
316 /// catch# :: IO a -> (String -> IO a) -> IO a — run with exception handler
317 CatchChirho,
318 /// throw# :: String -> a — throw an exception (synonym for error)
319 ThrowChirho,
320 /// try# :: IO a -> IO (Either String a) — run and return Left on error, Right on success
321 TryChirho,
322 /// bracket# :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
323 /// Acquire resource, run body, release resource even on exception.
324 BracketChirho,
325 /// finally# :: IO a -> IO b -> IO a
326 /// Run action and cleanup, ensuring cleanup runs even on exception.
327 FinallyChirho,
328
329 // ── Data.Map runtime primops ──
330 /// mapEmpty# :: Map k v — create an empty map
331 MapEmptyChirho,
332 /// mapSingleton# :: k -> v -> Map k v — create a single-element map
333 MapSingletonChirho,
334 /// mapInsert# :: k -> v -> Map k v -> Map k v — insert or overwrite key
335 MapInsertChirho,
336 /// mapLookup# :: k -> Map k v -> Maybe v — lookup by key (returns heap Maybe)
337 MapLookupChirho,
338 /// mapDelete# :: k -> Map k v -> Map k v — remove key from map
339 MapDeleteChirho,
340 /// mapMember# :: k -> Map k v -> Bool — test if key is in map
341 MapMemberChirho,
342 /// mapSize# :: Map k v -> Int — number of key-value pairs
343 MapSizeChirho,
344 /// mapFromList# :: [(k, v)] -> Map k v — build map from association list
345 MapFromListChirho,
346 /// mapToList# :: Map k v -> [(k, v)] — convert map to sorted association list
347 MapToListChirho,
348 /// mapKeys# :: Map k v -> [k] — extract sorted keys as a list
349 MapKeysChirho,
350 /// mapElems# :: Map k v -> [v] — extract values (in key order) as a list
351 MapElemsChirho,
352 /// mapNull# :: Map k v -> Bool — test if map is empty
353 MapNullChirho,
354 /// mapMap# :: (v -> w) -> Map k v -> Map k w — apply function to all values
355 MapMapChirho,
356 /// mapFoldlWithKey# :: (b -> k -> v -> b) -> b -> Map k v -> b — strict left fold
357 MapFoldlWithKeyChirho,
358 /// mapFoldrWithKey# :: (k -> v -> b -> b) -> b -> Map k v -> b — right fold
359 MapFoldrWithKeyChirho,
360 /// mapUnion# :: Map k v -> Map k v -> Map k v — left-biased union
361 MapUnionChirho,
362 /// mapDifference# :: Map k v -> Map k w -> Map k v — keys in left not in right
363 MapDifferenceChirho,
364 /// mapIntersection# :: Map k v -> Map k w -> Map k v — keys in both maps
365 MapIntersectionChirho,
366 /// mapInsertWith# :: (v -> v -> v) -> k -> v -> Map k v -> Map k v — insert with combiner
367 MapInsertWithChirho,
368 /// mapFindWithDefault# :: v -> k -> Map k v -> v — lookup with default value
369 MapFindWithDefaultChirho,
370 /// mapAdjust# :: (v -> v) -> k -> Map k v -> Map k v — update value at key
371 MapAdjustChirho,
372 /// mapUnionWith# :: (v -> v -> v) -> Map k v -> Map k v -> Map k v — union with combiner
373 MapUnionWithChirho,
374 /// mapFilter# :: (v -> Bool) -> Map k v -> Map k v — keep entries matching predicate
375 MapFilterChirho,
376 /// mapFilterWithKey# :: (k -> v -> Bool) -> Map k v -> Map k v — keep entries matching key-value predicate
377 MapFilterWithKeyChirho,
378
379 // ── Data.Set runtime primops ──
380 /// setEmpty# :: Set a — create an empty set
381 SetEmptyChirho,
382 /// setSingleton# :: a -> Set a — create a single-element set
383 SetSingletonChirho,
384 /// setInsert# :: Ord a => a -> Set a -> Set a — insert element (dedup)
385 SetInsertChirho,
386 /// setMember# :: Ord a => a -> Set a -> Bool — test if element is in set
387 SetMemberChirho,
388 /// setDelete# :: Ord a => a -> Set a -> Set a — remove element
389 SetDeleteChirho,
390 /// setSize# :: Set a -> Int — number of elements
391 SetSizeChirho,
392 /// setFromList# :: Ord a => [a] -> Set a — build set from list
393 SetFromListChirho,
394 /// setToList# :: Set a -> [a] — convert set to sorted list
395 SetToListChirho,
396 /// setUnion# :: Ord a => Set a -> Set a -> Set a — union of two sets
397 SetUnionChirho,
398 /// setIntersection# :: Ord a => Set a -> Set a -> Set a — elements in both sets
399 SetIntersectionChirho,
400 /// setDifference# :: Ord a => Set a -> Set a -> Set a — elements in first not in second
401 SetDifferenceChirho,
402 /// setNull# :: Set a -> Bool — test if set is empty
403 SetNullChirho,
404 /// setMap# :: Ord b => (a -> b) -> Set a -> Set b — map over set elements
405 SetMapChirho,
406 /// setFilter# :: (a -> Bool) -> Set a -> Set a — filter elements
407 SetFilterChirho,
408 /// setFoldr# :: (a -> b -> b) -> b -> Set a -> b — right fold over set
409 SetFoldrChirho,
410
411 // ── STM (Software Transactional Memory) operations ──
412 /// newTVar# :: a -> STM (TVar a) — create a new TVar with initial value
413 NewTVarChirho,
414 /// readTVar# :: TVar a -> STM a — read the current value of a TVar
415 ReadTVarChirho,
416 /// writeTVar# :: TVar a -> a -> STM () — write a new value to a TVar
417 WriteTVarChirho,
418 /// atomically# :: STM a -> IO a — execute an STM transaction
419 AtomicallyChirho,
420 /// retry# :: STM a — abort and retry transaction when a TVar changes
421 RetryChirho,
422 /// orElse# :: STM a -> STM a -> STM a — try first, if it retries try second
423 OrElseChirho,
424}
425
426/// The evaluation stack.
427#[derive(Debug)]
428pub struct StackChirho {
429 frames_chirho: Vec<FrameChirho>,
430 /// Maximum stack depth reached (for statistics).
431 max_depth_chirho: usize,
432}
433
434impl StackChirho {
435 /// Create a new empty stack.
436 pub fn new_chirho() -> Self {
437 Self {
438 frames_chirho: Vec::new(),
439 max_depth_chirho: 0,
440 }
441 }
442
443 /// Push a continuation frame.
444 pub fn push_chirho(&mut self, frame_chirho: FrameChirho) {
445 self.frames_chirho.push(frame_chirho);
446 if self.frames_chirho.len() > self.max_depth_chirho {
447 self.max_depth_chirho = self.frames_chirho.len();
448 }
449 }
450
451 /// Pop the top continuation frame, if any.
452 pub fn pop_chirho(&mut self) -> Option<FrameChirho> {
453 self.frames_chirho.pop()
454 }
455
456 /// Peek at the top frame without popping.
457 pub fn peek_chirho(&self) -> Option<&FrameChirho> {
458 self.frames_chirho.last()
459 }
460
461 /// Whether the stack is empty.
462 pub fn is_empty_chirho(&self) -> bool {
463 self.frames_chirho.is_empty()
464 }
465
466 /// Current stack depth.
467 pub fn depth_chirho(&self) -> usize {
468 self.frames_chirho.len()
469 }
470
471 /// Maximum stack depth reached during execution.
472 pub fn max_depth_chirho(&self) -> usize {
473 self.max_depth_chirho
474 }
475
476 /// Borrow all frames (for GC root extraction).
477 pub fn frames_chirho(&self) -> &[FrameChirho] {
478 &self.frames_chirho
479 }
480
481 /// Unwind the stack looking for a `CatchChirho` or `TryFrameChirho` frame.
482 /// Returns the frame if found, popping all frames above it (including the
483 /// handler frame itself). Returns `None` if no such frame exists.
484 pub fn unwind_to_catch_chirho(&mut self) -> Option<FrameChirho> {
485 // Search from the top of the stack downward for either handler kind.
486 let catch_pos_chirho = self.frames_chirho.iter().rposition(|f_chirho| {
487 matches!(
488 f_chirho,
489 FrameChirho::CatchChirho { .. } | FrameChirho::TryFrameChirho { .. }
490 )
491 });
492 if let Some(pos_chirho) = catch_pos_chirho {
493 // Pop everything above and including the handler frame
494 self.frames_chirho.truncate(pos_chirho + 1);
495 self.frames_chirho.pop() // the handler frame itself
496 } else {
497 None
498 }
499 }
500}
501
502#[cfg(test)]
503mod tests_chirho {
504 use super::*;
505
506 #[test]
507 fn push_pop_chirho() {
508 let mut stack_chirho = StackChirho::new_chirho();
509 assert!(stack_chirho.is_empty_chirho());
510
511 stack_chirho.push_chirho(FrameChirho::UpdateChirho {
512 thunk_addr_chirho: HeapAddrChirho(0),
513 });
514 assert_eq!(stack_chirho.depth_chirho(), 1);
515
516 stack_chirho.push_chirho(FrameChirho::ApplyChirho {
517 args_chirho: vec![ValueChirho::IntChirho(42)],
518 });
519 assert_eq!(stack_chirho.depth_chirho(), 2);
520
521 let top_chirho = stack_chirho.pop_chirho().unwrap();
522 assert!(matches!(top_chirho, FrameChirho::ApplyChirho { .. }));
523
524 let next_chirho = stack_chirho.pop_chirho().unwrap();
525 assert!(matches!(next_chirho, FrameChirho::UpdateChirho { .. }));
526
527 assert!(stack_chirho.is_empty_chirho());
528 }
529
530 #[test]
531 fn max_depth_tracked_chirho() {
532 let mut stack_chirho = StackChirho::new_chirho();
533 for _ in 0..5 {
534 stack_chirho.push_chirho(FrameChirho::UpdateChirho {
535 thunk_addr_chirho: HeapAddrChirho(0),
536 });
537 }
538 stack_chirho.pop_chirho();
539 stack_chirho.pop_chirho();
540
541 assert_eq!(stack_chirho.depth_chirho(), 3);
542 assert_eq!(stack_chirho.max_depth_chirho(), 5);
543 }
544
545 #[test]
546 fn peek_chirho() {
547 let mut stack_chirho = StackChirho::new_chirho();
548 assert!(stack_chirho.peek_chirho().is_none());
549
550 stack_chirho.push_chirho(FrameChirho::ApplyChirho {
551 args_chirho: vec![],
552 });
553 assert!(matches!(
554 stack_chirho.peek_chirho(),
555 Some(FrameChirho::ApplyChirho { .. })
556 ));
557 // peek doesn't consume
558 assert_eq!(stack_chirho.depth_chirho(), 1);
559 }
560
561 #[test]
562 fn case_frame_chirho() {
563 let frame_chirho = FrameChirho::CaseChirho {
564 alt_entries_chirho: vec![(0, 100), (1, 101)],
565 default_entry_chirho: Some(999),
566 saved_arg_regs_chirho: vec![],
567 };
568 if let FrameChirho::CaseChirho {
569 alt_entries_chirho,
570 default_entry_chirho,
571 ..
572 } = &frame_chirho
573 {
574 assert_eq!(alt_entries_chirho.len(), 2);
575 assert_eq!(*default_entry_chirho, Some(999));
576 }
577 }
578
579 #[test]
580 fn primop_frame_chirho() {
581 let frame_chirho = FrameChirho::PrimOpChirho {
582 op_chirho: PrimOpKindChirho::AddIntChirho,
583 args_so_far_chirho: vec![ValueChirho::IntChirho(10)],
584 pending_args_chirho: vec![ValueChirho::IntChirho(5)],
585 remaining_chirho: 1,
586 };
587 if let FrameChirho::PrimOpChirho {
588 op_chirho,
589 args_so_far_chirho,
590 pending_args_chirho,
591 remaining_chirho,
592 } = &frame_chirho
593 {
594 assert_eq!(*op_chirho, PrimOpKindChirho::AddIntChirho);
595 assert_eq!(args_so_far_chirho.len(), 1);
596 assert_eq!(pending_args_chirho.len(), 1);
597 assert_eq!(*remaining_chirho, 1);
598 }
599 }
600}