surreal/instruction.rs
1//! Bytecode instructions for the VM.
2
3use crate::Pid;
4
5// ========== Bit Syntax Types ==========
6
7/// Type specifier for a binary segment
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum BitType {
10 /// Integer (default)
11 Integer,
12 /// IEEE 754 float (32 or 64 bits)
13 Float,
14 /// Raw binary/bytes
15 Binary,
16 /// UTF-8 encoded codepoint
17 Utf8,
18}
19
20/// Endianness for multi-byte values
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum Endianness {
23 /// Big endian (network byte order, default)
24 Big,
25 /// Little endian
26 Little,
27}
28
29/// Signedness for integer values
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum Signedness {
32 /// Unsigned (default)
33 Unsigned,
34 /// Signed (two's complement)
35 Signed,
36}
37
38/// A segment specification for binary construction or matching
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct BitSegment {
41 /// Type of the segment
42 pub bit_type: BitType,
43 /// Size in bits (None means use default for type or "rest" for binary)
44 pub size: Option<u32>,
45 /// Endianness for multi-byte values
46 pub endianness: Endianness,
47 /// Signedness for integers
48 pub signedness: Signedness,
49}
50
51impl Default for BitSegment {
52 fn default() -> Self {
53 BitSegment {
54 bit_type: BitType::Integer,
55 size: Some(8), // default is 8 bits for integer
56 endianness: Endianness::Big,
57 signedness: Signedness::Unsigned,
58 }
59 }
60}
61
62impl BitSegment {
63 /// Create a default integer segment with specified size in bits
64 pub fn integer(bits: u32) -> Self {
65 BitSegment {
66 bit_type: BitType::Integer,
67 size: Some(bits),
68 endianness: Endianness::Big,
69 signedness: Signedness::Unsigned,
70 }
71 }
72
73 /// Create a binary segment that matches the rest
74 pub fn binary_rest() -> Self {
75 BitSegment {
76 bit_type: BitType::Binary,
77 size: None, // rest of binary
78 endianness: Endianness::Big,
79 signedness: Signedness::Unsigned,
80 }
81 }
82
83 /// Create a binary segment with fixed byte size
84 pub fn binary(bytes: u32) -> Self {
85 BitSegment {
86 bit_type: BitType::Binary,
87 size: Some(bytes * 8),
88 endianness: Endianness::Big,
89 signedness: Signedness::Unsigned,
90 }
91 }
92
93 /// Create a 32-bit float segment
94 pub fn float32() -> Self {
95 BitSegment {
96 bit_type: BitType::Float,
97 size: Some(32),
98 endianness: Endianness::Big,
99 signedness: Signedness::Unsigned,
100 }
101 }
102
103 /// Create a 64-bit float segment
104 pub fn float64() -> Self {
105 BitSegment {
106 bit_type: BitType::Float,
107 size: Some(64),
108 endianness: Endianness::Big,
109 signedness: Signedness::Unsigned,
110 }
111 }
112
113 /// Set endianness to little
114 pub fn little(mut self) -> Self {
115 self.endianness = Endianness::Little;
116 self
117 }
118
119 /// Set signedness to signed
120 pub fn signed(mut self) -> Self {
121 self.signedness = Signedness::Signed;
122 self
123 }
124}
125
126/// Source of a segment value for binary construction
127#[derive(Debug, Clone)]
128pub enum SegmentSource {
129 /// Immediate integer value
130 Int(i64),
131 /// Value from register
132 Reg(Register),
133}
134
135/// Bytecode instructions
136#[derive(Debug, Clone)]
137pub enum Instruction {
138 /// Process is done
139 End,
140
141 /// Do some work (costs `amount` reductions)
142 Work { amount: u32 },
143
144 /// Spawn a new process running `code`, store child PID in register
145 Spawn {
146 code: Vec<Instruction>,
147 dest: Register,
148 },
149
150 /// Spawn a new process and atomically link to it
151 SpawnLink {
152 code: Vec<Instruction>,
153 dest: Register,
154 },
155
156 /// Send a message to a process
157 Send { to: Source, msg: String },
158
159 /// Receive a message matching a pattern, block if none available
160 /// For now, just receives any user message into a register
161 Receive { dest: Register },
162
163 /// Receive with timeout (in reductions). If no message arrives
164 /// before timeout expires, receives "TIMEOUT" instead.
165 ReceiveTimeout { dest: Register, timeout: u32 },
166
167 /// Link to another process (bidirectional crash notification)
168 Link { target: Source },
169
170 /// Remove a bidirectional link to another process
171 Unlink { target: Source },
172
173 /// Monitor another process (one-way crash notification)
174 /// Returns a monitor reference in dest for later demonitoring
175 Monitor { target: Source, dest: Register },
176
177 /// Cancel a monitor by its reference
178 Demonitor { monitor_ref: Register },
179
180 /// Register current process with a name
181 Register { name: String },
182
183 /// Unregister a name
184 Unregister { name: String },
185
186 /// Look up a registered name, store PID (or None) in register
187 WhereIs { name: String, dest: Register },
188
189 /// Print a value (for debugging)
190 Print { source: Source },
191
192 /// Crash the process (for testing links)
193 Crash,
194
195 /// Exit the process with a reason (from register)
196 /// If reason is :normal, linked processes are not signaled (unless they trap_exit)
197 /// If reason is abnormal, linked processes receive exit signal
198 Exit { reason: Register },
199
200 /// Set the trap_exit flag for this process
201 /// When true, exit signals from linked processes become {:EXIT, Pid, Reason} messages
202 /// When false (default), abnormal exit signals kill this process
203 TrapExit { enable: bool },
204
205 // ========== Arithmetic & Logic ==========
206 /// Load an immediate integer into a register
207 LoadInt { value: i64, dest: Register },
208
209 /// Move (copy) any value from source register to dest register
210 Move { source: Register, dest: Register },
211
212 /// Add two operands, store result in dest
213 Add {
214 a: Operand,
215 b: Operand,
216 dest: Register,
217 },
218
219 /// Subtract b from a, store result in dest
220 Sub {
221 a: Operand,
222 b: Operand,
223 dest: Register,
224 },
225
226 /// Multiply two operands, store result in dest
227 Mul {
228 a: Operand,
229 b: Operand,
230 dest: Register,
231 },
232
233 /// Divide a by b, store result in dest (integer division)
234 Div {
235 a: Operand,
236 b: Operand,
237 dest: Register,
238 },
239
240 /// Modulo a by b, store result in dest
241 Mod {
242 a: Operand,
243 b: Operand,
244 dest: Register,
245 },
246
247 // ========== Comparisons ==========
248 /// Compare equal: dest = (a == b) ? 1 : 0
249 Eq {
250 a: Operand,
251 b: Operand,
252 dest: Register,
253 },
254
255 /// Compare not equal: dest = (a != b) ? 1 : 0
256 Ne {
257 a: Operand,
258 b: Operand,
259 dest: Register,
260 },
261
262 /// Compare less than: dest = (a < b) ? 1 : 0
263 Lt {
264 a: Operand,
265 b: Operand,
266 dest: Register,
267 },
268
269 /// Compare less than or equal: dest = (a <= b) ? 1 : 0
270 Lte {
271 a: Operand,
272 b: Operand,
273 dest: Register,
274 },
275
276 /// Compare greater than: dest = (a > b) ? 1 : 0
277 Gt {
278 a: Operand,
279 b: Operand,
280 dest: Register,
281 },
282
283 /// Compare greater than or equal: dest = (a >= b) ? 1 : 0
284 Gte {
285 a: Operand,
286 b: Operand,
287 dest: Register,
288 },
289
290 // ========== Control Flow ==========
291 /// Unconditional jump to instruction index
292 Jump { target: usize },
293
294 /// Jump to target if condition is truthy (non-zero integer)
295 JumpIf { cond: Operand, target: usize },
296
297 /// Jump to target if condition is falsy (zero or non-integer)
298 JumpUnless { cond: Operand, target: usize },
299
300 /// Call a function: push return address onto call stack, jump to target
301 Call { target: usize },
302
303 /// Return from function: pop return address from call stack, jump to it
304 /// If call stack is empty, ends the process
305 Return,
306
307 // ========== Module Function Calls ==========
308 /// Call a function by MFA (Module:Function/Arity)
309 /// Arguments must be in R0..R(arity-1) before call
310 /// Return value will be in R0
311 CallMFA {
312 module: String,
313 function: String,
314 arity: u8,
315 },
316
317 /// Call a local function in the current module
318 /// More efficient than CallMFA when calling within same module
319 CallLocal { function: String, arity: u8 },
320
321 /// Tail call MFA - call without pushing return frame
322 TailCallMFA {
323 module: String,
324 function: String,
325 arity: u8,
326 },
327
328 /// Tail call local - call without pushing return frame
329 TailCallLocal { function: String, arity: u8 },
330
331 /// Create a function reference and store in register
332 MakeFun {
333 module: String,
334 function: String,
335 arity: u8,
336 dest: Register,
337 },
338
339 /// Apply a function reference (from register) to arguments
340 /// Fun must be a Value::Fun or Value::Closure in the specified register
341 /// Arguments in R0..R(arity-1)
342 Apply { fun: Register, arity: u8 },
343
344 /// Create a closure capturing values from registers
345 /// The closure references module:function but captures current register values
346 /// When applied, captured values are placed after explicit arguments
347 MakeClosure {
348 module: String,
349 function: String,
350 arity: u8,
351 captures: Vec<Register>,
352 dest: Register,
353 },
354
355 /// Spawn a process running module:function/arity
356 /// Arguments for the function must be in R0..R(arity-1)
357 SpawnMFA {
358 module: String,
359 function: String,
360 arity: u8,
361 dest: Register,
362 },
363
364 /// Spawn and link, running module:function/arity
365 SpawnLinkMFA {
366 module: String,
367 function: String,
368 arity: u8,
369 dest: Register,
370 },
371
372 // ========== Stack Operations ==========
373 /// Push a value onto the data stack
374 Push { source: Operand },
375
376 /// Pop a value from the data stack into a register
377 /// Crashes if stack is empty
378 Pop { dest: Register },
379
380 // ========== Atoms & Tuples ==========
381 /// Load an atom into a register
382 LoadAtom { name: String, dest: Register },
383
384 /// Create a tuple from the top `arity` stack elements
385 /// Elements are popped in reverse order (first pushed = first element)
386 MakeTuple { arity: u8, dest: Register },
387
388 /// Get an element from a tuple by index (0-based)
389 /// Crashes if not a tuple or index out of bounds
390 TupleElement {
391 tuple: Register,
392 index: u8,
393 dest: Register,
394 },
395
396 /// Get the arity (size) of a tuple
397 /// Crashes if not a tuple
398 TupleArity { tuple: Register, dest: Register },
399
400 // ========== Pattern Matching ==========
401 /// Match a value against a pattern
402 /// On success: binds variables and continues to next instruction
403 /// On failure: jumps to fail_target
404 Match {
405 source: Register,
406 pattern: Pattern,
407 fail_target: usize,
408 },
409
410 /// Receive with pattern matching
411 /// Scans mailbox for first message matching any clause pattern.
412 /// On match: removes message, binds variables, jumps to clause target.
413 /// No match: blocks until a message arrives.
414 /// Timeout: if set and expires, jumps to timeout_target.
415 ReceiveMatch {
416 /// List of (pattern, jump_target) clauses
417 clauses: Vec<(Pattern, usize)>,
418 /// Optional timeout in reductions
419 timeout: Option<u32>,
420 /// Where to jump on timeout
421 timeout_target: usize,
422 },
423
424 // ========== Lists ==========
425 /// Create a list from the top `length` stack elements
426 /// Elements are popped in reverse order (first pushed = first element)
427 MakeList { length: u8, dest: Register },
428
429 /// Cons: prepend an element to a list [elem | list]
430 /// Crashes if tail is not a list
431 Cons {
432 head: Register,
433 tail: Register,
434 dest: Register,
435 },
436
437 /// Get the head (first element) of a list
438 /// Crashes if not a list or empty
439 ListHead { list: Register, dest: Register },
440
441 /// Get the tail (rest) of a list
442 /// Crashes if not a list or empty
443 ListTail { list: Register, dest: Register },
444
445 /// Check if a list is empty, store 1 (true) or 0 (false)
446 ListIsEmpty { list: Register, dest: Register },
447
448 /// Get the length of a list (O(n))
449 ListLength { list: Register, dest: Register },
450
451 /// Append two lists (a ++ b)
452 /// Crashes if either is not a list
453 ListAppend {
454 a: Register,
455 b: Register,
456 dest: Register,
457 },
458
459 /// Reverse a list
460 /// Crashes if not a list
461 ListReverse { list: Register, dest: Register },
462
463 /// Get the nth element of a list (0-based)
464 /// Crashes if not a list or index out of bounds
465 ListNth {
466 list: Register,
467 n: Register,
468 dest: Register,
469 },
470
471 /// Check if an element is a member of a list
472 /// Stores 1 (true) or 0 (false)
473 ListMember {
474 elem: Register,
475 list: Register,
476 dest: Register,
477 },
478
479 // ========== Type Checking ==========
480 /// Check if value is an integer
481 /// Stores 1 (true) or 0 (false)
482 IsInteger { source: Register, dest: Register },
483
484 /// Check if value is an atom
485 /// Stores 1 (true) or 0 (false)
486 IsAtom { source: Register, dest: Register },
487
488 /// Check if value is a tuple
489 /// Stores 1 (true) or 0 (false)
490 IsTuple { source: Register, dest: Register },
491
492 /// Check if value is a list (including empty list)
493 /// Stores 1 (true) or 0 (false)
494 IsList { source: Register, dest: Register },
495
496 /// Check if value is a PID
497 /// Stores 1 (true) or 0 (false)
498 IsPid { source: Register, dest: Register },
499
500 /// Check if value is a function (Fun or Closure)
501 /// Stores 1 (true) or 0 (false)
502 IsFunction { source: Register, dest: Register },
503
504 /// Check if value is a string/binary
505 /// Stores 1 (true) or 0 (false)
506 IsString { source: Register, dest: Register },
507
508 /// Check if value is a map
509 /// Stores 1 (true) or 0 (false)
510 IsMap { source: Register, dest: Register },
511
512 // ========== Process Dictionary ==========
513 /// Store value in process dictionary, returns old value (or None) in dest
514 PutDict {
515 key: Register,
516 value: Register,
517 dest: Register,
518 },
519
520 /// Get value from process dictionary, stores None if key not found
521 GetDict { key: Register, dest: Register },
522
523 /// Remove key from process dictionary, returns old value (or None) in dest
524 EraseDict { key: Register, dest: Register },
525
526 /// Get all keys from process dictionary as a list
527 GetDictKeys { dest: Register },
528
529 // ========== Maps ==========
530 /// Create a map from key-value pairs on the stack
531 /// Pops `count` pairs (2*count values: k1, v1, k2, v2, ...) from stack
532 MakeMap { count: u8, dest: Register },
533
534 /// Get value from map by key, crashes if key not found
535 MapGet {
536 map: Register,
537 key: Register,
538 dest: Register,
539 },
540
541 /// Get value from map by key, returns default if not found
542 MapGetDefault {
543 map: Register,
544 key: Register,
545 default: Register,
546 dest: Register,
547 },
548
549 /// Insert/update key-value in map, returns new map (maps are immutable)
550 MapPut {
551 map: Register,
552 key: Register,
553 value: Register,
554 dest: Register,
555 },
556
557 /// Remove key from map, returns new map (maps are immutable)
558 MapRemove {
559 map: Register,
560 key: Register,
561 dest: Register,
562 },
563
564 /// Check if key exists in map, stores 1 (true) or 0 (false)
565 MapHas {
566 map: Register,
567 key: Register,
568 dest: Register,
569 },
570
571 /// Get number of entries in map
572 MapSize { map: Register, dest: Register },
573
574 /// Get all keys from map as a list
575 MapKeys { map: Register, dest: Register },
576
577 /// Get all values from map as a list
578 MapValues { map: Register, dest: Register },
579
580 // ========== Binaries ==========
581 /// Create a binary from literal bytes
582 MakeBinary { bytes: Vec<u8>, dest: Register },
583
584 /// Get the size (byte length) of a binary
585 BinarySize { bin: Register, dest: Register },
586
587 /// Get a byte at index (0-based)
588 /// Crashes if not a binary or index out of bounds
589 BinaryAt {
590 bin: Register,
591 index: Register,
592 dest: Register,
593 },
594
595 /// Extract a slice from a binary
596 /// Crashes if not a binary or range out of bounds
597 BinarySlice {
598 bin: Register,
599 start: Register,
600 len: Register,
601 dest: Register,
602 },
603
604 /// Concatenate two binaries
605 /// Crashes if either is not a binary
606 BinaryConcat {
607 a: Register,
608 b: Register,
609 dest: Register,
610 },
611
612 /// Check if value is a binary
613 /// Stores 1 (true) or 0 (false)
614 IsBinary { source: Register, dest: Register },
615
616 /// Convert a string to a binary (UTF-8 encoded)
617 StringToBinary { source: Register, dest: Register },
618
619 /// Convert a binary to a string (assumes UTF-8)
620 /// Crashes if binary is not valid UTF-8
621 BinaryToString { source: Register, dest: Register },
622
623 // ========== Bit Syntax ==========
624 /// Construct a binary from segments
625 /// Each segment specifies a value, type, size, endianness, and signedness
626 /// Usage: push segment values to stack, then call with segment specs
627 BinaryConstructSegments {
628 /// Segment specifications (in order)
629 segments: Vec<(SegmentSource, BitSegment)>,
630 /// Destination register for the constructed binary
631 dest: Register,
632 },
633
634 /// Start matching a binary - prepares for segment extraction
635 /// Stores match state (current position) internally
636 BinaryMatchStart {
637 /// Source binary to match
638 source: Register,
639 },
640
641 /// Match a segment from the binary at current position
642 /// Advances the match position by the segment size
643 /// Jumps to fail_target if match fails (not enough bytes, etc.)
644 BinaryMatchSegment {
645 /// Segment specification
646 segment: BitSegment,
647 /// Destination register for extracted value
648 dest: Register,
649 /// Jump target if match fails
650 fail_target: usize,
651 },
652
653 /// Get the remaining bytes after matching
654 /// Returns the rest of the binary as a new binary value
655 BinaryMatchRest {
656 /// Destination register for remaining bytes
657 dest: Register,
658 },
659
660 /// Get an integer from a binary at bit offset with specified segment
661 /// For random access bit extraction
662 BinaryGetInteger {
663 /// Source binary
664 bin: Register,
665 /// Bit offset (from start)
666 bit_offset: Register,
667 /// Segment specification
668 segment: BitSegment,
669 /// Destination register
670 dest: Register,
671 },
672
673 /// Put an integer into a binary at bit offset with specified segment
674 /// Creates a new binary with the value inserted
675 BinaryPutInteger {
676 /// Source binary
677 bin: Register,
678 /// Bit offset (from start)
679 bit_offset: Register,
680 /// Value to insert
681 value: Register,
682 /// Segment specification
683 segment: BitSegment,
684 /// Destination register
685 dest: Register,
686 },
687
688 // ========== References ==========
689 /// Create a new unique reference
690 MakeRef { dest: Register },
691
692 /// Check if value is a reference
693 /// Stores 1 (true) or 0 (false)
694 IsRef { source: Register, dest: Register },
695
696 // ========== Floats ==========
697 /// Load a floating-point immediate into a register
698 LoadFloat { value: f64, dest: Register },
699
700 /// Check if value is a float
701 /// Stores 1 (true) or 0 (false)
702 IsFloat { source: Register, dest: Register },
703
704 /// Convert integer to float
705 IntToFloat { source: Register, dest: Register },
706
707 /// Convert float to integer (truncates toward zero)
708 FloatToInt { source: Register, dest: Register },
709
710 /// Floor: round down to nearest integer (as float)
711 Floor { source: Register, dest: Register },
712
713 /// Ceil: round up to nearest integer (as float)
714 Ceil { source: Register, dest: Register },
715
716 /// Round: round to nearest integer (as float)
717 Round { source: Register, dest: Register },
718
719 /// Trunc: truncate toward zero (as float)
720 Trunc { source: Register, dest: Register },
721
722 /// Square root
723 Sqrt { source: Register, dest: Register },
724
725 /// Absolute value (works for both int and float)
726 Abs { source: Register, dest: Register },
727
728 /// Power: base^exponent (both operands, result is float)
729 Pow {
730 base: Register,
731 exp: Register,
732 dest: Register,
733 },
734
735 // ========== Timers ==========
736 /// Send a message to a process after a delay (in reductions)
737 /// Returns a timer reference in dest for cancellation
738 SendAfter {
739 delay: u32,
740 to: Source,
741 msg: Register,
742 dest: Register,
743 },
744
745 /// Start a timer that sends {:timeout, ref, msg} to self after delay
746 /// Returns the timer reference in dest
747 StartTimer {
748 delay: u32,
749 msg: Register,
750 dest: Register,
751 },
752
753 /// Cancel a pending timer by its reference
754 /// Returns the remaining time if timer was active, or :ok if already fired
755 CancelTimer { timer_ref: Register, dest: Register },
756
757 /// Read the remaining time on a timer (0 if already fired or cancelled)
758 ReadTimer { timer_ref: Register, dest: Register },
759
760 // ========== IO ==========
761 /// Print a value to stdout with newline
762 PrintLn { source: Register },
763
764 /// Read a line from stdin into register as String
765 /// Returns :eof atom if end of input
766 ReadLine { dest: Register },
767
768 /// Read entire file contents as binary
769 /// Returns {:ok, binary} or {:error, reason}
770 FileRead { path: Register, dest: Register },
771
772 /// Write binary/string to file
773 /// Returns :ok or {:error, reason}
774 FileWrite {
775 path: Register,
776 content: Register,
777 dest: Register,
778 },
779
780 /// Check if file exists
781 /// Returns 1 (true) or 0 (false)
782 FileExists { path: Register, dest: Register },
783
784 /// Delete a file
785 /// Returns :ok or {:error, reason}
786 FileDelete { path: Register, dest: Register },
787
788 // ========== System Info ==========
789 /// Get own PID into register
790 SelfPid { dest: Register },
791
792 /// Get list of all process PIDs
793 ProcessList { dest: Register },
794
795 /// Get count of live processes
796 ProcessCount { dest: Register },
797
798 /// Check if a process is alive
799 /// Returns 1 (true) or 0 (false)
800 IsAlive { pid: Register, dest: Register },
801
802 /// Get process info as tuple
803 /// Returns {status, mailbox_len, links_count, monitors_count, trap_exit}
804 ProcessInfo { pid: Register, dest: Register },
805
806 /// Get list of loaded module names
807 ModuleList { dest: Register },
808
809 /// Check if function is exported
810 /// Returns 1 (true) or 0 (false)
811 FunctionExported {
812 module: Register,
813 function: Register,
814 arity: Register,
815 dest: Register,
816 },
817
818 // ========== Exception Handling ==========
819 /// Begin a try block. Pushes exception handler onto try stack.
820 /// catch_target: where to jump on exception
821 /// after_target: optional cleanup block (always runs)
822 Try {
823 catch_target: usize,
824 after_target: Option<usize>,
825 },
826
827 /// End a try block successfully. Pops handler from try stack.
828 /// If there's an after block, jumps to it.
829 EndTry,
830
831 /// Throw an exception. Unwinds to nearest catch handler.
832 /// class: :error, :exit, or :throw
833 /// reason: the exception reason (from register)
834 Throw { class: Register, reason: Register },
835
836 /// Get the current exception as {class, reason, stacktrace} tuple
837 /// Used in catch blocks to access exception details
838 GetException { dest: Register },
839
840 /// Clear the current exception (after handling)
841 ClearException,
842
843 /// Re-raise the current exception (in catch block)
844 Reraise,
845}
846
847/// An operand for arithmetic/comparison operations
848#[derive(Debug, Clone)]
849pub enum Operand {
850 /// Read from a register
851 Reg(Register),
852 /// Immediate integer value
853 Int(i64),
854}
855
856/// Where to read a value from
857#[derive(Debug, Clone)]
858pub enum Source {
859 /// A register
860 Reg(Register),
861 /// The process's own PID
862 Self_,
863 /// The parent process's PID (if any)
864 Parent,
865 /// A literal PID
866 Pid(Pid),
867 /// A named process from the registry
868 Named(String),
869}
870
871/// Register index (processes have a small set of registers)
872#[derive(Debug, Clone, Copy)]
873pub struct Register(pub u8);
874
875/// A pattern for matching values
876#[derive(Debug, Clone)]
877pub enum Pattern {
878 /// Match any value, ignore it
879 Wildcard,
880
881 /// Match any value, bind it to a register
882 Variable(Register),
883
884 /// Match a specific integer
885 Int(i64),
886
887 /// Match a specific atom
888 Atom(String),
889
890 /// Match a specific string
891 String(String),
892
893 /// Match a specific binary
894 Binary(Vec<u8>),
895
896 /// Match a tuple with specific arity and element patterns
897 Tuple(Vec<Pattern>),
898
899 /// Match an empty list []
900 ListEmpty,
901
902 /// Match a non-empty list [head | tail]
903 ListCons {
904 head: Box<Pattern>,
905 tail: Box<Pattern>,
906 },
907
908 /// Match a map containing specific key-value patterns
909 /// The map may contain additional keys not in the pattern
910 Map(Vec<(Pattern, Pattern)>),
911}