1use std::collections::{HashMap, VecDeque};
4
5use num_bigint::BigInt;
6
7use crate::{
8 CallFrame, Instruction, Message, Module, Operand, Pattern, Pid, Process, ProcessStatus,
9 Register, Source, SystemMsg, TryFrame, Value,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum StepResult {
15 Busy,
17 Idle,
19}
20
21#[derive(Debug)]
23enum ExecResult {
24 Continue(u32),
26 #[allow(dead_code)]
28 Yield(u32),
29 Jump(usize, u32),
31 Wait,
33 Done,
35 Crash,
37 Exit(Value),
39 Throw(Value, Value),
41}
42
43fn log(msg: &str) {
45 #[cfg(target_arch = "wasm32")]
46 web_sys::console::log_1(&msg.into());
47
48 #[cfg(not(target_arch = "wasm32"))]
49 println!("{}", msg);
50}
51
52#[derive(Debug, Clone)]
54pub struct Timer {
55 pub timer_ref: u64,
57 pub target: Pid,
59 pub message: Value,
61 pub remaining: u32,
63}
64
65#[derive(Debug)]
67pub struct Scheduler {
68 pub processes: HashMap<Pid, Process>,
69 pub ready_queue: VecDeque<Pid>,
70 pub next_pid: u64,
71 pub registry: HashMap<String, Pid>,
73 pub modules: HashMap<String, Module>,
75 pub output: Vec<String>,
77 pub next_ref: u64,
79 pub timers: Vec<Timer>,
81}
82
83impl Scheduler {
84 pub fn new() -> Self {
85 Self {
86 processes: HashMap::new(),
87 ready_queue: VecDeque::new(),
88 next_pid: 0,
89 registry: HashMap::new(),
90 modules: HashMap::new(),
91 output: Vec::new(),
92 next_ref: 0,
93 timers: Vec::new(),
94 }
95 }
96
97 pub fn load_module(&mut self, module: Module) -> Result<(), String> {
99 if self.modules.contains_key(&module.name) {
100 return Err(format!("Module {} already loaded", module.name));
101 }
102 self.modules.insert(module.name.clone(), module);
103 Ok(())
104 }
105
106 fn get_instruction(&self, process: &Process) -> Option<Instruction> {
108 if let Some(ref module_name) = process.current_module {
109 let module = self.modules.get(module_name)?;
111 module.code.get(process.pc).cloned()
112 } else if let Some(ref code) = process.inline_code {
113 code.get(process.pc).cloned()
115 } else {
116 None
117 }
118 }
119
120 fn get_code_len(&self, process: &Process) -> usize {
122 if let Some(ref module_name) = process.current_module {
123 self.modules
124 .get(module_name)
125 .map(|m| m.code.len())
126 .unwrap_or(0)
127 } else if let Some(ref code) = process.inline_code {
128 code.len()
129 } else {
130 0
131 }
132 }
133
134 pub fn take_output(&mut self) -> Vec<String> {
136 std::mem::take(&mut self.output)
137 }
138
139 pub fn spawn(&mut self, code: Vec<Instruction>) -> Pid {
141 self.spawn_with_parent(code, None)
142 }
143
144 pub fn spawn_with_parent(&mut self, code: Vec<Instruction>, parent: Option<Pid>) -> Pid {
146 let pid = Pid(self.next_pid);
147 self.next_pid += 1;
148
149 let process = Process::new(pid, parent, code);
150 self.processes.insert(pid, process);
151 self.ready_queue.push_back(pid);
152
153 pid
154 }
155
156 pub fn step(&mut self, budget: u32) -> StepResult {
158 let mut remaining = budget;
159
160 self.tick_timeouts();
162
163 self.tick_timers(budget);
165
166 while remaining > 0 {
167 let Some(pid) = self.ready_queue.pop_front() else {
169 if self.has_pending_timeouts() || !self.timers.is_empty() {
171 return StepResult::Busy; }
173 return StepResult::Idle;
174 };
175
176 let used = self.run_process(pid, remaining);
178 remaining = remaining.saturating_sub(used);
179 }
180
181 if self.ready_queue.is_empty() && !self.has_pending_timeouts() && self.timers.is_empty() {
182 StepResult::Idle
183 } else {
184 StepResult::Busy
185 }
186 }
187
188 fn has_pending_timeouts(&self) -> bool {
190 self.processes
191 .values()
192 .any(|p| p.status == ProcessStatus::Waiting && p.timeout.is_some())
193 }
194
195 fn tick_timeouts(&mut self) {
197 let mut to_wake = Vec::new();
198
199 for (pid, process) in &mut self.processes {
200 if process.status == ProcessStatus::Waiting {
201 if let Some(ref mut t) = process.timeout {
202 if *t > 0 {
203 *t -= 1;
204 }
205 if *t == 0 {
206 process.status = ProcessStatus::Ready;
207 to_wake.push(*pid);
208 }
209 }
210 }
211 }
212
213 for pid in to_wake {
214 self.ready_queue.push_back(pid);
215 }
216 }
217
218 fn tick_timers(&mut self, elapsed: u32) {
220 let mut fired = Vec::new();
221 let mut i = 0;
222
223 while i < self.timers.len() {
224 let timer = &mut self.timers[i];
225 timer.remaining = timer.remaining.saturating_sub(elapsed);
226
227 if timer.remaining == 0 {
228 fired.push(self.timers.remove(i));
230 } else {
231 i += 1;
232 }
233 }
234
235 for timer in fired {
237 if let Some(process) = self.processes.get_mut(&timer.target) {
238 let msg_str = format!("{:?}", timer.message);
240 process.mailbox.push_back(Message::User(msg_str));
241
242 if process.status == ProcessStatus::Waiting {
244 process.status = ProcessStatus::Ready;
245 self.ready_queue.push_back(timer.target);
246 }
247 }
248 }
249 }
250
251 fn run_process(&mut self, pid: Pid, budget: u32) -> u32 {
254 let mut used = 0;
255
256 while used < budget {
257 let Some(process) = self.processes.get(&pid) else {
258 break;
259 };
260
261 let code_len = self.get_code_len(process);
262 if process.pc >= code_len {
263 self.finish_process_with_reason(
265 pid,
266 ProcessStatus::Done,
267 Value::Atom("normal".to_string()),
268 );
269 break;
270 }
271
272 let Some(instruction) = self.get_instruction(process) else {
274 self.finish_process_with_reason(
275 pid,
276 ProcessStatus::Crashed,
277 Value::Atom("crashed".to_string()),
278 );
279 break;
280 };
281
282 match self.execute(pid, instruction) {
283 ExecResult::Continue(cost) => {
284 used += cost;
285 if let Some(p) = self.processes.get_mut(&pid) {
286 p.pc += 1;
287 }
288 }
289 ExecResult::Yield(cost) => {
290 used += cost;
291 if let Some(p) = self.processes.get_mut(&pid) {
292 p.pc += 1;
293 }
294 self.ready_queue.push_back(pid);
296 break;
297 }
298 ExecResult::Jump(target, cost) => {
299 used += cost;
300 if let Some(p) = self.processes.get_mut(&pid) {
301 p.pc = target;
302 }
303 }
304 ExecResult::Wait => {
305 if let Some(p) = self.processes.get_mut(&pid) {
307 p.status = ProcessStatus::Waiting;
308 }
309 break;
310 }
311 ExecResult::Done => {
312 self.finish_process_with_reason(
314 pid,
315 ProcessStatus::Done,
316 Value::Atom("normal".to_string()),
317 );
318 break;
319 }
320 ExecResult::Crash => {
321 self.finish_process_with_reason(
323 pid,
324 ProcessStatus::Crashed,
325 Value::Atom("crashed".to_string()),
326 );
327 break;
328 }
329 ExecResult::Exit(reason) => {
330 let status = if reason == Value::Atom("normal".to_string()) {
332 ProcessStatus::Done
333 } else {
334 ProcessStatus::Crashed
335 };
336 self.finish_process_with_reason(pid, status, reason);
337 break;
338 }
339 ExecResult::Throw(class, reason) => {
340 let Some(process) = self.processes.get_mut(&pid) else {
342 break;
343 };
344
345 if let Some(frame) = process.try_stack.pop() {
346 let stacktrace = vec![format!("pc:{}", process.pc)];
348
349 process.current_exception = Some((class, reason, stacktrace));
351
352 process.call_stack.truncate(frame.call_stack_depth);
354 process.stack.truncate(frame.stack_depth);
355
356 process.pc = frame.catch_target;
358 used += 1;
359 } else {
361 let exit_reason = Value::Tuple(vec![class, reason]);
363 self.finish_process_with_reason(pid, ProcessStatus::Crashed, exit_reason);
364 break;
365 }
366 }
367 }
368 }
369
370 if used >= budget {
372 if let Some(p) = self.processes.get(&pid) {
373 let code_len = self.get_code_len(p);
374 if p.status == ProcessStatus::Ready && p.pc < code_len {
375 self.ready_queue.push_back(pid);
376 }
377 }
378 }
379
380 used
381 }
382
383 fn execute(&mut self, pid: Pid, instruction: Instruction) -> ExecResult {
384 match instruction {
385 Instruction::End => ExecResult::Done,
386
387 Instruction::Work { amount } => ExecResult::Continue(amount),
388
389 Instruction::Spawn { code, dest } => {
390 let child_pid = self.spawn_with_parent(code, Some(pid));
391 if let Some(p) = self.processes.get_mut(&pid) {
392 p.registers[dest.0 as usize] = Value::Pid(child_pid);
393 }
394 ExecResult::Continue(1)
395 }
396
397 Instruction::SpawnLink { code, dest } => {
398 let child_pid = self.spawn_with_parent(code, Some(pid));
399
400 if let Some(parent) = self.processes.get_mut(&pid) {
402 parent.registers[dest.0 as usize] = Value::Pid(child_pid);
403 if !parent.links.contains(&child_pid) {
404 parent.links.push(child_pid);
405 }
406 }
407 if let Some(child) = self.processes.get_mut(&child_pid) {
408 if !child.links.contains(&pid) {
409 child.links.push(pid);
410 }
411 }
412
413 ExecResult::Continue(1)
414 }
415
416 Instruction::Send { to, msg } => {
417 let target_pid = self.resolve_pid(pid, &to);
418 if let Some(target_pid) = target_pid {
419 if let Some(target) = self.processes.get_mut(&target_pid) {
420 target.mailbox.push_back(Message::User(msg));
421 if target.status == ProcessStatus::Waiting {
423 target.status = ProcessStatus::Ready;
424 self.ready_queue.push_back(target_pid);
425 }
426 }
427 }
428 ExecResult::Continue(1)
429 }
430
431 Instruction::Receive { dest } => {
432 let Some(process) = self.processes.get_mut(&pid) else {
433 return ExecResult::Crash;
434 };
435
436 if let Some(msg) = process.mailbox.pop_front() {
438 let value = Self::message_to_value(msg);
439 process.registers[dest.0 as usize] = value;
440 process.timeout = None;
441 ExecResult::Continue(1)
442 } else {
443 ExecResult::Wait
444 }
445 }
446
447 Instruction::ReceiveTimeout { dest, timeout } => {
448 let Some(process) = self.processes.get_mut(&pid) else {
449 return ExecResult::Crash;
450 };
451
452 if let Some(msg) = process.mailbox.pop_front() {
454 let value = Self::message_to_value(msg);
455 process.registers[dest.0 as usize] = value;
456 process.timeout = None;
457 ExecResult::Continue(1)
458 } else if process.timeout == Some(0) {
459 process.registers[dest.0 as usize] = Value::String("TIMEOUT".into());
461 process.timeout = None;
462 ExecResult::Continue(1)
463 } else {
464 if process.timeout.is_none() {
466 process.timeout = Some(timeout);
467 }
468 ExecResult::Wait
469 }
470 }
471
472 Instruction::Link { target } => {
473 let Some(target_pid) = self.resolve_pid(pid, &target) else {
474 return ExecResult::Crash;
475 };
476
477 if let Some(p) = self.processes.get_mut(&pid) {
479 if !p.links.contains(&target_pid) {
480 p.links.push(target_pid);
481 }
482 }
483 if let Some(t) = self.processes.get_mut(&target_pid) {
484 if !t.links.contains(&pid) {
485 t.links.push(pid);
486 }
487 }
488
489 ExecResult::Continue(1)
490 }
491
492 Instruction::Unlink { target } => {
493 let Some(target_pid) = self.resolve_pid(pid, &target) else {
494 return ExecResult::Crash;
495 };
496
497 if let Some(p) = self.processes.get_mut(&pid) {
499 p.links.retain(|&linked| linked != target_pid);
500 }
501 if let Some(t) = self.processes.get_mut(&target_pid) {
502 t.links.retain(|&linked| linked != pid);
503 }
504
505 ExecResult::Continue(1)
506 }
507
508 Instruction::Monitor { target, dest } => {
509 let Some(target_pid) = self.resolve_pid(pid, &target) else {
510 return ExecResult::Crash;
511 };
512
513 let monitor_ref = self.next_ref;
515 self.next_ref += 1;
516
517 if let Some(p) = self.processes.get_mut(&pid) {
519 p.monitors.push((monitor_ref, target_pid));
520 }
521
522 if let Some(t) = self.processes.get_mut(&target_pid) {
524 t.monitored_by.push((monitor_ref, pid));
525 }
526
527 if let Some(p) = self.processes.get_mut(&pid) {
529 p.registers[dest.0 as usize] = Value::Ref(monitor_ref);
530 }
531
532 ExecResult::Continue(1)
533 }
534
535 Instruction::Demonitor { monitor_ref } => {
536 let Some(process) = self.processes.get(&pid) else {
537 return ExecResult::Crash;
538 };
539 let Value::Ref(ref_id) = process.registers[monitor_ref.0 as usize] else {
540 return ExecResult::Crash;
541 };
542 let ref_id = ref_id;
543
544 let target_pid = if let Some(p) = self.processes.get_mut(&pid) {
546 let pos = p.monitors.iter().position(|(r, _)| *r == ref_id);
547 if let Some(idx) = pos {
548 let (_, target) = p.monitors.remove(idx);
549 Some(target)
550 } else {
551 None
552 }
553 } else {
554 None
555 };
556
557 if let Some(target_pid) = target_pid {
559 if let Some(t) = self.processes.get_mut(&target_pid) {
560 t.monitored_by.retain(|(r, _)| *r != ref_id);
561 }
562 }
563
564 ExecResult::Continue(1)
565 }
566
567 Instruction::Register { name } => {
568 self.registry.insert(name, pid);
570 ExecResult::Continue(1)
571 }
572
573 Instruction::Unregister { name } => {
574 self.registry.remove(&name);
576 ExecResult::Continue(1)
577 }
578
579 Instruction::WhereIs { name, dest } => {
580 let value = self
581 .registry
582 .get(&name)
583 .map(|p| Value::Pid(*p))
584 .unwrap_or(Value::None);
585
586 if let Some(p) = self.processes.get_mut(&pid) {
587 p.registers[dest.0 as usize] = value;
588 }
589 ExecResult::Continue(1)
590 }
591
592 Instruction::Print { source } => {
593 if let Some(process) = self.processes.get(&pid) {
594 let value = self.resolve_source(process, &source);
595 let msg = format!("[Pid({})] {:?}", pid.0, value);
596 log(&msg);
597 self.output.push(msg);
598 }
599 ExecResult::Continue(1)
600 }
601
602 Instruction::Crash => ExecResult::Crash,
603
604 Instruction::Exit { reason } => {
605 if let Some(p) = self.processes.get(&pid) {
606 let exit_reason = p.registers[reason.0 as usize].clone();
607 ExecResult::Exit(exit_reason)
608 } else {
609 ExecResult::Crash
610 }
611 }
612
613 Instruction::TrapExit { enable } => {
614 if let Some(p) = self.processes.get_mut(&pid) {
615 p.trap_exit = enable;
616 }
617 ExecResult::Continue(1)
618 }
619
620 Instruction::LoadInt { value, dest } => {
622 if let Some(p) = self.processes.get_mut(&pid) {
623 p.registers[dest.0 as usize] = Value::Int(value);
624 }
625 ExecResult::Continue(1)
626 }
627
628 Instruction::Move { source, dest } => {
629 if let Some(p) = self.processes.get_mut(&pid) {
630 let value = p.registers[source.0 as usize].clone();
631 p.registers[dest.0 as usize] = value;
632 }
633 ExecResult::Continue(1)
634 }
635
636 Instruction::Add { a, b, dest } => self.bigint_arith_op(
637 pid,
638 &a,
639 &b,
640 dest,
641 |x, y| x.checked_add(y).map(Value::Int),
642 |x, y| x + y,
643 ),
644
645 Instruction::Sub { a, b, dest } => self.bigint_arith_op(
646 pid,
647 &a,
648 &b,
649 dest,
650 |x, y| x.checked_sub(y).map(Value::Int),
651 |x, y| x - y,
652 ),
653
654 Instruction::Mul { a, b, dest } => self.bigint_arith_op(
655 pid,
656 &a,
657 &b,
658 dest,
659 |x, y| x.checked_mul(y).map(Value::Int),
660 |x, y| x * y,
661 ),
662
663 Instruction::Div { a, b, dest } => {
664 let Some(process) = self.processes.get(&pid) else {
665 return ExecResult::Crash;
666 };
667 let av = self.resolve_operand_value(process, &a);
668 let bv = self.resolve_operand_value(process, &b);
669 match (av, bv) {
670 (Some(x), Some(y)) => {
671 if y.is_zero() {
672 return ExecResult::Crash; }
674 let result = match (x.to_bigint(), y.to_bigint()) {
675 (Some(a), Some(b)) => Value::from_bigint(a / b),
676 _ => return ExecResult::Crash,
677 };
678 if let Some(p) = self.processes.get_mut(&pid) {
679 p.registers[dest.0 as usize] = result;
680 }
681 ExecResult::Continue(1)
682 }
683 _ => ExecResult::Crash,
684 }
685 }
686
687 Instruction::Mod { a, b, dest } => {
688 let Some(process) = self.processes.get(&pid) else {
689 return ExecResult::Crash;
690 };
691 let av = self.resolve_operand_value(process, &a);
692 let bv = self.resolve_operand_value(process, &b);
693 match (av, bv) {
694 (Some(x), Some(y)) => {
695 if y.is_zero() {
696 return ExecResult::Crash; }
698 let result = match (x.to_bigint(), y.to_bigint()) {
699 (Some(a), Some(b)) => Value::from_bigint(a % b),
700 _ => return ExecResult::Crash,
701 };
702 if let Some(p) = self.processes.get_mut(&pid) {
703 p.registers[dest.0 as usize] = result;
704 }
705 ExecResult::Continue(1)
706 }
707 _ => ExecResult::Crash,
708 }
709 }
710
711 Instruction::Eq { a, b, dest } => self.bigint_cmp_op(pid, &a, &b, dest, |x, y| x == y),
713
714 Instruction::Ne { a, b, dest } => self.bigint_cmp_op(pid, &a, &b, dest, |x, y| x != y),
715
716 Instruction::Lt { a, b, dest } => self.bigint_cmp_op(pid, &a, &b, dest, |x, y| x < y),
717
718 Instruction::Lte { a, b, dest } => self.bigint_cmp_op(pid, &a, &b, dest, |x, y| x <= y),
719
720 Instruction::Gt { a, b, dest } => self.bigint_cmp_op(pid, &a, &b, dest, |x, y| x > y),
721
722 Instruction::Gte { a, b, dest } => self.bigint_cmp_op(pid, &a, &b, dest, |x, y| x >= y),
723
724 Instruction::Jump { target } => ExecResult::Jump(target, 1),
726
727 Instruction::JumpIf { cond, target } => {
728 let Some(process) = self.processes.get(&pid) else {
729 return ExecResult::Crash;
730 };
731 let val = self.resolve_operand(process, &cond);
732 match val {
733 Some(n) if n != 0 => ExecResult::Jump(target, 1),
734 _ => ExecResult::Continue(1), }
736 }
737
738 Instruction::JumpUnless { cond, target } => {
739 let Some(process) = self.processes.get(&pid) else {
740 return ExecResult::Crash;
741 };
742 let val = self.resolve_operand(process, &cond);
743 match val {
744 Some(0) | None => ExecResult::Jump(target, 1), _ => ExecResult::Continue(1), }
747 }
748
749 Instruction::Call { target } => {
750 if let Some(p) = self.processes.get_mut(&pid) {
751 p.call_stack.push(CallFrame {
753 module: p.current_module.clone(),
754 return_pc: p.pc + 1,
755 });
756 }
757 ExecResult::Jump(target, 1)
758 }
759
760 Instruction::Return => {
761 let Some(process) = self.processes.get_mut(&pid) else {
762 return ExecResult::Crash;
763 };
764 if let Some(frame) = process.call_stack.pop() {
765 process.current_module = frame.module;
767 ExecResult::Jump(frame.return_pc, 1)
768 } else {
769 ExecResult::Done
771 }
772 }
773
774 Instruction::CallMFA {
775 module,
776 function,
777 arity,
778 } => {
779 let Some(mod_ref) = self.modules.get(&module) else {
781 log(&format!("CallMFA: module {} not found", module));
782 return ExecResult::Crash;
783 };
784
785 let Some(func) = mod_ref.get_function(&function, arity) else {
787 log(&format!(
788 "CallMFA: function {}:{}/{} not found",
789 module, function, arity
790 ));
791 return ExecResult::Crash;
792 };
793
794 let entry = func.entry;
795
796 if let Some(p) = self.processes.get_mut(&pid) {
798 p.call_stack.push(CallFrame {
799 module: p.current_module.clone(),
800 return_pc: p.pc + 1,
801 });
802 p.current_module = Some(module.clone());
803 }
804 ExecResult::Jump(entry, 1)
805 }
806
807 Instruction::CallLocal { function, arity } => {
808 let Some(process) = self.processes.get(&pid) else {
809 return ExecResult::Crash;
810 };
811
812 let Some(ref module_name) = process.current_module else {
814 log("CallLocal: not in a module");
815 return ExecResult::Crash;
816 };
817
818 let Some(mod_ref) = self.modules.get(module_name) else {
820 return ExecResult::Crash;
821 };
822
823 let Some(func) = mod_ref.get_function(&function, arity) else {
824 log(&format!(
825 "CallLocal: function {}:{}/{} not found",
826 module_name, function, arity
827 ));
828 return ExecResult::Crash;
829 };
830
831 let entry = func.entry;
832
833 if let Some(p) = self.processes.get_mut(&pid) {
835 p.call_stack.push(CallFrame {
836 module: p.current_module.clone(),
837 return_pc: p.pc + 1,
838 });
839 }
840 ExecResult::Jump(entry, 1)
841 }
842
843 Instruction::TailCallMFA {
844 module,
845 function,
846 arity,
847 } => {
848 let Some(mod_ref) = self.modules.get(&module) else {
850 log(&format!("TailCallMFA: module {} not found", module));
851 return ExecResult::Crash;
852 };
853
854 let Some(func) = mod_ref.get_function(&function, arity) else {
856 log(&format!(
857 "TailCallMFA: function {}:{}/{} not found",
858 module, function, arity
859 ));
860 return ExecResult::Crash;
861 };
862
863 let entry = func.entry;
864
865 if let Some(p) = self.processes.get_mut(&pid) {
867 p.current_module = Some(module.clone());
868 }
869 ExecResult::Jump(entry, 1)
870 }
871
872 Instruction::TailCallLocal { function, arity } => {
873 let Some(process) = self.processes.get(&pid) else {
874 return ExecResult::Crash;
875 };
876
877 let Some(ref module_name) = process.current_module else {
879 log("TailCallLocal: not in a module");
880 return ExecResult::Crash;
881 };
882
883 let Some(mod_ref) = self.modules.get(module_name) else {
885 return ExecResult::Crash;
886 };
887
888 let Some(func) = mod_ref.get_function(&function, arity) else {
889 log(&format!(
890 "TailCallLocal: function {}:{}/{} not found",
891 module_name, function, arity
892 ));
893 return ExecResult::Crash;
894 };
895
896 let entry = func.entry;
897
898 ExecResult::Jump(entry, 1)
900 }
901
902 Instruction::MakeFun {
903 module,
904 function,
905 arity,
906 dest,
907 } => {
908 let Some(mod_ref) = self.modules.get(&module) else {
910 log(&format!("MakeFun: module {} not found", module));
911 return ExecResult::Crash;
912 };
913
914 if mod_ref.get_function(&function, arity).is_none() {
915 log(&format!(
916 "MakeFun: function {}:{}/{} not found",
917 module, function, arity
918 ));
919 return ExecResult::Crash;
920 }
921
922 if let Some(p) = self.processes.get_mut(&pid) {
923 p.registers[dest.0 as usize] = Value::Fun {
924 module: module.clone(),
925 function: function.clone(),
926 arity,
927 };
928 }
929 ExecResult::Continue(1)
930 }
931
932 Instruction::MakeClosure {
933 module,
934 function,
935 arity,
936 captures,
937 dest,
938 } => {
939 let total_arity = arity + captures.len() as u8;
941 let Some(mod_ref) = self.modules.get(&module) else {
942 log(&format!("MakeClosure: module {} not found", module));
943 return ExecResult::Crash;
944 };
945
946 if mod_ref.get_function(&function, total_arity).is_none() {
947 log(&format!(
948 "MakeClosure: function {}:{}/{} not found",
949 module, function, total_arity
950 ));
951 return ExecResult::Crash;
952 }
953
954 let Some(process) = self.processes.get(&pid) else {
956 return ExecResult::Crash;
957 };
958
959 let captured: Vec<Value> = captures
960 .iter()
961 .map(|r| process.registers[r.0 as usize].clone())
962 .collect();
963
964 if let Some(p) = self.processes.get_mut(&pid) {
965 p.registers[dest.0 as usize] = Value::Closure {
966 module: module.clone(),
967 function: function.clone(),
968 arity,
969 captured,
970 };
971 }
972 ExecResult::Continue(1)
973 }
974
975 Instruction::Apply { fun, arity } => {
976 let Some(process) = self.processes.get(&pid) else {
977 return ExecResult::Crash;
978 };
979
980 match &process.registers[fun.0 as usize] {
982 Value::Fun {
983 module,
984 function,
985 arity: fun_arity,
986 } => {
987 if *fun_arity != arity {
988 log(&format!(
989 "Apply: arity mismatch, expected {}, got {}",
990 fun_arity, arity
991 ));
992 return ExecResult::Crash;
993 }
994
995 let Some(mod_ref) = self.modules.get(module) else {
997 log(&format!("Apply: module {} not found", module));
998 return ExecResult::Crash;
999 };
1000
1001 let Some(func) = mod_ref.get_function(function, arity) else {
1002 log(&format!(
1003 "Apply: function {}:{}/{} not found",
1004 module, function, arity
1005 ));
1006 return ExecResult::Crash;
1007 };
1008
1009 let entry = func.entry;
1010 let module = module.clone();
1011
1012 if let Some(p) = self.processes.get_mut(&pid) {
1014 p.call_stack.push(CallFrame {
1015 module: p.current_module.clone(),
1016 return_pc: p.pc + 1,
1017 });
1018 p.current_module = Some(module);
1019 }
1020 ExecResult::Jump(entry, 1)
1021 }
1022
1023 Value::Closure {
1024 module,
1025 function,
1026 arity: closure_arity,
1027 captured,
1028 } => {
1029 if *closure_arity != arity {
1030 log(&format!(
1031 "Apply: closure arity mismatch, expected {}, got {}",
1032 closure_arity, arity
1033 ));
1034 return ExecResult::Crash;
1035 }
1036
1037 let total_arity = arity + captured.len() as u8;
1039
1040 let Some(mod_ref) = self.modules.get(module) else {
1042 log(&format!("Apply: module {} not found", module));
1043 return ExecResult::Crash;
1044 };
1045
1046 let Some(func) = mod_ref.get_function(function, total_arity) else {
1047 log(&format!(
1048 "Apply: function {}:{}/{} not found",
1049 module, function, total_arity
1050 ));
1051 return ExecResult::Crash;
1052 };
1053
1054 let entry = func.entry;
1055 let module = module.clone();
1056 let captured = captured.clone();
1057
1058 if let Some(p) = self.processes.get_mut(&pid) {
1061 for (i, val) in captured.into_iter().enumerate() {
1062 p.registers[arity as usize + i] = val;
1063 }
1064 p.call_stack.push(CallFrame {
1065 module: p.current_module.clone(),
1066 return_pc: p.pc + 1,
1067 });
1068 p.current_module = Some(module);
1069 }
1070 ExecResult::Jump(entry, 1)
1071 }
1072
1073 _ => {
1074 log("Apply: not a function or closure");
1075 ExecResult::Crash
1076 }
1077 }
1078 }
1079
1080 Instruction::SpawnMFA {
1081 module,
1082 function,
1083 arity,
1084 dest,
1085 } => {
1086 let Some(mod_ref) = self.modules.get(&module) else {
1088 log(&format!("SpawnMFA: module {} not found", module));
1089 return ExecResult::Crash;
1090 };
1091
1092 if !mod_ref.is_exported(&function, arity) {
1094 log(&format!(
1095 "SpawnMFA: function {}:{}/{} not exported",
1096 module, function, arity
1097 ));
1098 return ExecResult::Crash;
1099 }
1100
1101 let Some(func) = mod_ref.get_function(&function, arity) else {
1102 log(&format!(
1103 "SpawnMFA: function {}:{}/{} not found",
1104 module, function, arity
1105 ));
1106 return ExecResult::Crash;
1107 };
1108
1109 let entry = func.entry;
1110
1111 let Some(parent) = self.processes.get(&pid) else {
1113 return ExecResult::Crash;
1114 };
1115 let args: Vec<Value> = (0..arity)
1116 .map(|i| parent.registers[i as usize].clone())
1117 .collect();
1118
1119 let child_pid = Pid(self.next_pid);
1121 self.next_pid += 1;
1122
1123 let mut child =
1124 Process::new_with_module(child_pid, Some(pid), module.clone(), entry);
1125
1126 for (i, arg) in args.into_iter().enumerate() {
1128 child.registers[i] = arg;
1129 }
1130
1131 self.processes.insert(child_pid, child);
1132 self.ready_queue.push_back(child_pid);
1133
1134 if let Some(p) = self.processes.get_mut(&pid) {
1136 p.registers[dest.0 as usize] = Value::Pid(child_pid);
1137 }
1138
1139 ExecResult::Continue(1)
1140 }
1141
1142 Instruction::SpawnLinkMFA {
1143 module,
1144 function,
1145 arity,
1146 dest,
1147 } => {
1148 let Some(mod_ref) = self.modules.get(&module) else {
1150 log(&format!("SpawnLinkMFA: module {} not found", module));
1151 return ExecResult::Crash;
1152 };
1153
1154 if !mod_ref.is_exported(&function, arity) {
1156 log(&format!(
1157 "SpawnLinkMFA: function {}:{}/{} not exported",
1158 module, function, arity
1159 ));
1160 return ExecResult::Crash;
1161 }
1162
1163 let Some(func) = mod_ref.get_function(&function, arity) else {
1164 log(&format!(
1165 "SpawnLinkMFA: function {}:{}/{} not found",
1166 module, function, arity
1167 ));
1168 return ExecResult::Crash;
1169 };
1170
1171 let entry = func.entry;
1172
1173 let Some(parent) = self.processes.get(&pid) else {
1175 return ExecResult::Crash;
1176 };
1177 let args: Vec<Value> = (0..arity)
1178 .map(|i| parent.registers[i as usize].clone())
1179 .collect();
1180
1181 let child_pid = Pid(self.next_pid);
1183 self.next_pid += 1;
1184
1185 let mut child =
1186 Process::new_with_module(child_pid, Some(pid), module.clone(), entry);
1187
1188 for (i, arg) in args.into_iter().enumerate() {
1190 child.registers[i] = arg;
1191 }
1192
1193 child.links.push(pid);
1195
1196 self.processes.insert(child_pid, child);
1197 self.ready_queue.push_back(child_pid);
1198
1199 if let Some(p) = self.processes.get_mut(&pid) {
1201 p.links.push(child_pid);
1202 p.registers[dest.0 as usize] = Value::Pid(child_pid);
1203 }
1204
1205 ExecResult::Continue(1)
1206 }
1207
1208 Instruction::Push { source } => {
1209 let Some(process) = self.processes.get(&pid) else {
1210 return ExecResult::Crash;
1211 };
1212 let value = match &source {
1213 Operand::Int(n) => Value::Int(*n),
1214 Operand::Reg(r) => process.registers[r.0 as usize].clone(),
1215 };
1216 if let Some(p) = self.processes.get_mut(&pid) {
1217 p.stack.push(value);
1218 }
1219 ExecResult::Continue(1)
1220 }
1221
1222 Instruction::Pop { dest } => {
1223 let Some(process) = self.processes.get_mut(&pid) else {
1224 return ExecResult::Crash;
1225 };
1226 if let Some(value) = process.stack.pop() {
1227 process.registers[dest.0 as usize] = value;
1228 ExecResult::Continue(1)
1229 } else {
1230 ExecResult::Crash
1232 }
1233 }
1234
1235 Instruction::LoadAtom { name, dest } => {
1236 let Some(process) = self.processes.get_mut(&pid) else {
1237 return ExecResult::Crash;
1238 };
1239 process.registers[dest.0 as usize] = Value::Atom(name.clone());
1240 ExecResult::Continue(1)
1241 }
1242
1243 Instruction::MakeTuple { arity, dest } => {
1244 let Some(process) = self.processes.get_mut(&pid) else {
1245 return ExecResult::Crash;
1246 };
1247 let arity = arity as usize;
1248 if process.stack.len() < arity {
1249 return ExecResult::Crash;
1250 }
1251 let elements: Vec<Value> =
1253 process.stack.drain(process.stack.len() - arity..).collect();
1254 process.registers[dest.0 as usize] = Value::Tuple(elements);
1255 ExecResult::Continue(1)
1256 }
1257
1258 Instruction::TupleElement { tuple, index, dest } => {
1259 let Some(process) = self.processes.get_mut(&pid) else {
1260 return ExecResult::Crash;
1261 };
1262 let Value::Tuple(elements) = &process.registers[tuple.0 as usize] else {
1263 return ExecResult::Crash;
1264 };
1265 let idx = index as usize;
1266 if idx >= elements.len() {
1267 return ExecResult::Crash;
1268 }
1269 let value = elements[idx].clone();
1270 process.registers[dest.0 as usize] = value;
1271 ExecResult::Continue(1)
1272 }
1273
1274 Instruction::TupleArity { tuple, dest } => {
1275 let Some(process) = self.processes.get_mut(&pid) else {
1276 return ExecResult::Crash;
1277 };
1278 let Value::Tuple(elements) = &process.registers[tuple.0 as usize] else {
1279 return ExecResult::Crash;
1280 };
1281 let arity = elements.len() as i64;
1282 process.registers[dest.0 as usize] = Value::Int(arity);
1283 ExecResult::Continue(1)
1284 }
1285
1286 Instruction::Match {
1287 source,
1288 pattern,
1289 fail_target,
1290 } => {
1291 let Some(process) = self.processes.get(&pid) else {
1292 return ExecResult::Crash;
1293 };
1294 let value = process.registers[source.0 as usize].clone();
1295
1296 let mut bindings = Vec::new();
1298 if Self::match_pattern(&value, &pattern, &mut bindings) {
1299 if let Some(p) = self.processes.get_mut(&pid) {
1301 for (reg, val) in bindings {
1302 p.registers[reg.0 as usize] = val;
1303 }
1304 }
1305 ExecResult::Continue(1)
1306 } else {
1307 ExecResult::Jump(fail_target, 1)
1309 }
1310 }
1311
1312 Instruction::ReceiveMatch {
1313 clauses,
1314 timeout,
1315 timeout_target,
1316 } => {
1317 let Some(process) = self.processes.get(&pid) else {
1318 return ExecResult::Crash;
1319 };
1320
1321 if process.timeout == Some(0) {
1323 if let Some(p) = self.processes.get_mut(&pid) {
1324 p.timeout = None;
1325 }
1326 return ExecResult::Jump(timeout_target, 1);
1327 }
1328
1329 let mailbox_len = process.mailbox.len();
1331 for i in 0..mailbox_len {
1332 let msg = &process.mailbox[i];
1333 let value = Self::message_to_value(msg.clone());
1334
1335 for (pattern, target) in clauses.iter() {
1337 let mut bindings = Vec::new();
1338 if Self::match_pattern(&value, pattern, &mut bindings) {
1339 if let Some(p) = self.processes.get_mut(&pid) {
1341 p.mailbox.remove(i);
1342 p.timeout = None;
1343 for (reg, val) in bindings {
1344 p.registers[reg.0 as usize] = val;
1345 }
1346 }
1347 return ExecResult::Jump(*target, 1);
1348 }
1349 }
1350 }
1351
1352 if let Some(p) = self.processes.get_mut(&pid) {
1354 if p.timeout.is_none() {
1355 p.timeout = timeout.clone();
1356 }
1357 }
1358 ExecResult::Wait
1359 }
1360
1361 Instruction::MakeList { length, dest } => {
1362 let Some(process) = self.processes.get_mut(&pid) else {
1363 return ExecResult::Crash;
1364 };
1365 let length = length as usize;
1366 if process.stack.len() < length {
1367 return ExecResult::Crash;
1368 }
1369 let elements: Vec<Value> = process
1371 .stack
1372 .drain(process.stack.len() - length..)
1373 .collect();
1374 process.registers[dest.0 as usize] = Value::List(elements);
1375 ExecResult::Continue(1)
1376 }
1377
1378 Instruction::Cons { head, tail, dest } => {
1379 let Some(process) = self.processes.get_mut(&pid) else {
1380 return ExecResult::Crash;
1381 };
1382 let head_val = process.registers[head.0 as usize].clone();
1383 let Value::List(mut elements) = process.registers[tail.0 as usize].clone() else {
1384 return ExecResult::Crash;
1385 };
1386 elements.insert(0, head_val);
1387 process.registers[dest.0 as usize] = Value::List(elements);
1388 ExecResult::Continue(1)
1389 }
1390
1391 Instruction::ListHead { list, dest } => {
1392 let Some(process) = self.processes.get_mut(&pid) else {
1393 return ExecResult::Crash;
1394 };
1395 let Value::List(elements) = &process.registers[list.0 as usize] else {
1396 return ExecResult::Crash;
1397 };
1398 if elements.is_empty() {
1399 return ExecResult::Crash;
1400 }
1401 let head = elements[0].clone();
1402 process.registers[dest.0 as usize] = head;
1403 ExecResult::Continue(1)
1404 }
1405
1406 Instruction::ListTail { list, dest } => {
1407 let Some(process) = self.processes.get_mut(&pid) else {
1408 return ExecResult::Crash;
1409 };
1410 let Value::List(elements) = &process.registers[list.0 as usize] else {
1411 return ExecResult::Crash;
1412 };
1413 if elements.is_empty() {
1414 return ExecResult::Crash;
1415 }
1416 let tail = elements[1..].to_vec();
1417 process.registers[dest.0 as usize] = Value::List(tail);
1418 ExecResult::Continue(1)
1419 }
1420
1421 Instruction::ListIsEmpty { list, dest } => {
1422 let Some(process) = self.processes.get_mut(&pid) else {
1423 return ExecResult::Crash;
1424 };
1425 let Value::List(elements) = &process.registers[list.0 as usize] else {
1426 return ExecResult::Crash;
1427 };
1428 let is_empty = if elements.is_empty() { 1 } else { 0 };
1429 process.registers[dest.0 as usize] = Value::Int(is_empty);
1430 ExecResult::Continue(1)
1431 }
1432
1433 Instruction::ListLength { list, dest } => {
1434 let Some(process) = self.processes.get_mut(&pid) else {
1435 return ExecResult::Crash;
1436 };
1437 let Value::List(elements) = &process.registers[list.0 as usize] else {
1438 return ExecResult::Crash;
1439 };
1440 let length = elements.len() as i64;
1441 process.registers[dest.0 as usize] = Value::Int(length);
1442 ExecResult::Continue(1)
1443 }
1444
1445 Instruction::ListAppend { a, b, dest } => {
1446 let Some(process) = self.processes.get_mut(&pid) else {
1447 return ExecResult::Crash;
1448 };
1449 let Value::List(list_a) = &process.registers[a.0 as usize] else {
1450 return ExecResult::Crash;
1451 };
1452 let Value::List(list_b) = &process.registers[b.0 as usize] else {
1453 return ExecResult::Crash;
1454 };
1455 let mut result = list_a.clone();
1456 result.extend(list_b.clone());
1457 process.registers[dest.0 as usize] = Value::List(result);
1458 ExecResult::Continue(1)
1459 }
1460
1461 Instruction::ListReverse { list, dest } => {
1462 let Some(process) = self.processes.get_mut(&pid) else {
1463 return ExecResult::Crash;
1464 };
1465 let Value::List(elements) = &process.registers[list.0 as usize] else {
1466 return ExecResult::Crash;
1467 };
1468 let reversed: Vec<Value> = elements.iter().rev().cloned().collect();
1469 process.registers[dest.0 as usize] = Value::List(reversed);
1470 ExecResult::Continue(1)
1471 }
1472
1473 Instruction::ListNth { list, n, dest } => {
1474 let Some(process) = self.processes.get_mut(&pid) else {
1475 return ExecResult::Crash;
1476 };
1477 let Value::List(elements) = &process.registers[list.0 as usize] else {
1478 return ExecResult::Crash;
1479 };
1480 let Value::Int(index) = process.registers[n.0 as usize] else {
1481 return ExecResult::Crash;
1482 };
1483 if index < 0 || index as usize >= elements.len() {
1484 return ExecResult::Crash;
1485 }
1486 let elem = elements[index as usize].clone();
1487 process.registers[dest.0 as usize] = elem;
1488 ExecResult::Continue(1)
1489 }
1490
1491 Instruction::ListMember { elem, list, dest } => {
1492 let Some(process) = self.processes.get_mut(&pid) else {
1493 return ExecResult::Crash;
1494 };
1495 let Value::List(elements) = &process.registers[list.0 as usize] else {
1496 return ExecResult::Crash;
1497 };
1498 let search_elem = &process.registers[elem.0 as usize];
1499 let found = elements.contains(search_elem);
1500 process.registers[dest.0 as usize] = Value::Int(if found { 1 } else { 0 });
1501 ExecResult::Continue(1)
1502 }
1503
1504 Instruction::IsInteger { source, dest } => {
1505 let Some(process) = self.processes.get_mut(&pid) else {
1506 return ExecResult::Crash;
1507 };
1508 let is_int = matches!(process.registers[source.0 as usize], Value::Int(_));
1509 process.registers[dest.0 as usize] = Value::Int(if is_int { 1 } else { 0 });
1510 ExecResult::Continue(1)
1511 }
1512
1513 Instruction::IsAtom { source, dest } => {
1514 let Some(process) = self.processes.get_mut(&pid) else {
1515 return ExecResult::Crash;
1516 };
1517 let is_atom = matches!(process.registers[source.0 as usize], Value::Atom(_));
1518 process.registers[dest.0 as usize] = Value::Int(if is_atom { 1 } else { 0 });
1519 ExecResult::Continue(1)
1520 }
1521
1522 Instruction::IsTuple { source, dest } => {
1523 let Some(process) = self.processes.get_mut(&pid) else {
1524 return ExecResult::Crash;
1525 };
1526 let is_tuple = matches!(process.registers[source.0 as usize], Value::Tuple(_));
1527 process.registers[dest.0 as usize] = Value::Int(if is_tuple { 1 } else { 0 });
1528 ExecResult::Continue(1)
1529 }
1530
1531 Instruction::IsList { source, dest } => {
1532 let Some(process) = self.processes.get_mut(&pid) else {
1533 return ExecResult::Crash;
1534 };
1535 let is_list = matches!(process.registers[source.0 as usize], Value::List(_));
1536 process.registers[dest.0 as usize] = Value::Int(if is_list { 1 } else { 0 });
1537 ExecResult::Continue(1)
1538 }
1539
1540 Instruction::IsPid { source, dest } => {
1541 let Some(process) = self.processes.get_mut(&pid) else {
1542 return ExecResult::Crash;
1543 };
1544 let is_pid = matches!(process.registers[source.0 as usize], Value::Pid(_));
1545 process.registers[dest.0 as usize] = Value::Int(if is_pid { 1 } else { 0 });
1546 ExecResult::Continue(1)
1547 }
1548
1549 Instruction::IsFunction { source, dest } => {
1550 let Some(process) = self.processes.get_mut(&pid) else {
1551 return ExecResult::Crash;
1552 };
1553 let is_fun = matches!(
1554 process.registers[source.0 as usize],
1555 Value::Fun { .. } | Value::Closure { .. }
1556 );
1557 process.registers[dest.0 as usize] = Value::Int(if is_fun { 1 } else { 0 });
1558 ExecResult::Continue(1)
1559 }
1560
1561 Instruction::IsString { source, dest } => {
1562 let Some(process) = self.processes.get_mut(&pid) else {
1563 return ExecResult::Crash;
1564 };
1565 let is_string = matches!(process.registers[source.0 as usize], Value::String(_));
1566 process.registers[dest.0 as usize] = Value::Int(if is_string { 1 } else { 0 });
1567 ExecResult::Continue(1)
1568 }
1569
1570 Instruction::PutDict { key, value, dest } => {
1572 let Some(process) = self.processes.get_mut(&pid) else {
1573 return ExecResult::Crash;
1574 };
1575 let key_val = process.registers[key.0 as usize].clone();
1576 let new_val = process.registers[value.0 as usize].clone();
1577 let old_val = process.dictionary.insert(key_val, new_val);
1578 process.registers[dest.0 as usize] = old_val.unwrap_or(Value::None);
1579 ExecResult::Continue(1)
1580 }
1581
1582 Instruction::GetDict { key, dest } => {
1583 let Some(process) = self.processes.get_mut(&pid) else {
1584 return ExecResult::Crash;
1585 };
1586 let key_val = process.registers[key.0 as usize].clone();
1587 let value = process
1588 .dictionary
1589 .get(&key_val)
1590 .cloned()
1591 .unwrap_or(Value::None);
1592 process.registers[dest.0 as usize] = value;
1593 ExecResult::Continue(1)
1594 }
1595
1596 Instruction::EraseDict { key, dest } => {
1597 let Some(process) = self.processes.get_mut(&pid) else {
1598 return ExecResult::Crash;
1599 };
1600 let key_val = process.registers[key.0 as usize].clone();
1601 let old_val = process.dictionary.remove(&key_val);
1602 process.registers[dest.0 as usize] = old_val.unwrap_or(Value::None);
1603 ExecResult::Continue(1)
1604 }
1605
1606 Instruction::GetDictKeys { dest } => {
1607 let Some(process) = self.processes.get_mut(&pid) else {
1608 return ExecResult::Crash;
1609 };
1610 let keys: Vec<Value> = process.dictionary.keys().cloned().collect();
1611 process.registers[dest.0 as usize] = Value::List(keys);
1612 ExecResult::Continue(1)
1613 }
1614
1615 Instruction::MakeMap { count, dest } => {
1617 let Some(process) = self.processes.get_mut(&pid) else {
1618 return ExecResult::Crash;
1619 };
1620 let mut map = std::collections::HashMap::new();
1621 for _ in 0..count {
1623 let Some(value) = process.stack.pop() else {
1624 return ExecResult::Crash;
1625 };
1626 let Some(key) = process.stack.pop() else {
1627 return ExecResult::Crash;
1628 };
1629 map.insert(key, value);
1630 }
1631 process.registers[dest.0 as usize] = Value::Map(map);
1632 ExecResult::Continue(1)
1633 }
1634
1635 Instruction::MapGet { map, key, dest } => {
1636 let Some(process) = self.processes.get_mut(&pid) else {
1637 return ExecResult::Crash;
1638 };
1639 let Value::Map(m) = &process.registers[map.0 as usize] else {
1640 return ExecResult::Crash;
1641 };
1642 let key_val = &process.registers[key.0 as usize];
1643 let Some(value) = m.get(key_val) else {
1644 return ExecResult::Crash; };
1646 let value = value.clone();
1647 process.registers[dest.0 as usize] = value;
1648 ExecResult::Continue(1)
1649 }
1650
1651 Instruction::MapGetDefault {
1652 map,
1653 key,
1654 default,
1655 dest,
1656 } => {
1657 let Some(process) = self.processes.get_mut(&pid) else {
1658 return ExecResult::Crash;
1659 };
1660 let Value::Map(m) = &process.registers[map.0 as usize] else {
1661 return ExecResult::Crash;
1662 };
1663 let key_val = &process.registers[key.0 as usize];
1664 let value = m
1665 .get(key_val)
1666 .cloned()
1667 .unwrap_or_else(|| process.registers[default.0 as usize].clone());
1668 process.registers[dest.0 as usize] = value;
1669 ExecResult::Continue(1)
1670 }
1671
1672 Instruction::MapPut {
1673 map,
1674 key,
1675 value,
1676 dest,
1677 } => {
1678 let Some(process) = self.processes.get_mut(&pid) else {
1679 return ExecResult::Crash;
1680 };
1681 let Value::Map(m) = &process.registers[map.0 as usize] else {
1682 return ExecResult::Crash;
1683 };
1684 let mut new_map = m.clone();
1685 let key_val = process.registers[key.0 as usize].clone();
1686 let val = process.registers[value.0 as usize].clone();
1687 new_map.insert(key_val, val);
1688 process.registers[dest.0 as usize] = Value::Map(new_map);
1689 ExecResult::Continue(1)
1690 }
1691
1692 Instruction::MapRemove { map, key, dest } => {
1693 let Some(process) = self.processes.get_mut(&pid) else {
1694 return ExecResult::Crash;
1695 };
1696 let Value::Map(m) = &process.registers[map.0 as usize] else {
1697 return ExecResult::Crash;
1698 };
1699 let mut new_map = m.clone();
1700 let key_val = &process.registers[key.0 as usize];
1701 new_map.remove(key_val);
1702 process.registers[dest.0 as usize] = Value::Map(new_map);
1703 ExecResult::Continue(1)
1704 }
1705
1706 Instruction::MapHas { map, key, dest } => {
1707 let Some(process) = self.processes.get_mut(&pid) else {
1708 return ExecResult::Crash;
1709 };
1710 let Value::Map(m) = &process.registers[map.0 as usize] else {
1711 return ExecResult::Crash;
1712 };
1713 let key_val = &process.registers[key.0 as usize];
1714 let has = m.contains_key(key_val);
1715 process.registers[dest.0 as usize] = Value::Int(if has { 1 } else { 0 });
1716 ExecResult::Continue(1)
1717 }
1718
1719 Instruction::MapSize { map, dest } => {
1720 let Some(process) = self.processes.get_mut(&pid) else {
1721 return ExecResult::Crash;
1722 };
1723 let Value::Map(m) = &process.registers[map.0 as usize] else {
1724 return ExecResult::Crash;
1725 };
1726 let size = m.len() as i64;
1727 process.registers[dest.0 as usize] = Value::Int(size);
1728 ExecResult::Continue(1)
1729 }
1730
1731 Instruction::MapKeys { map, dest } => {
1732 let Some(process) = self.processes.get_mut(&pid) else {
1733 return ExecResult::Crash;
1734 };
1735 let Value::Map(m) = &process.registers[map.0 as usize] else {
1736 return ExecResult::Crash;
1737 };
1738 let keys: Vec<Value> = m.keys().cloned().collect();
1739 process.registers[dest.0 as usize] = Value::List(keys);
1740 ExecResult::Continue(1)
1741 }
1742
1743 Instruction::MapValues { map, dest } => {
1744 let Some(process) = self.processes.get_mut(&pid) else {
1745 return ExecResult::Crash;
1746 };
1747 let Value::Map(m) = &process.registers[map.0 as usize] else {
1748 return ExecResult::Crash;
1749 };
1750 let values: Vec<Value> = m.values().cloned().collect();
1751 process.registers[dest.0 as usize] = Value::List(values);
1752 ExecResult::Continue(1)
1753 }
1754
1755 Instruction::IsMap { source, dest } => {
1756 let Some(process) = self.processes.get_mut(&pid) else {
1757 return ExecResult::Crash;
1758 };
1759 let is_map = matches!(process.registers[source.0 as usize], Value::Map(_));
1760 process.registers[dest.0 as usize] = Value::Int(if is_map { 1 } else { 0 });
1761 ExecResult::Continue(1)
1762 }
1763
1764 Instruction::MakeBinary { bytes, dest } => {
1766 let Some(process) = self.processes.get_mut(&pid) else {
1767 return ExecResult::Crash;
1768 };
1769 process.registers[dest.0 as usize] = Value::Binary(bytes.clone());
1770 ExecResult::Continue(1)
1771 }
1772
1773 Instruction::BinarySize { bin, dest } => {
1774 let Some(process) = self.processes.get_mut(&pid) else {
1775 return ExecResult::Crash;
1776 };
1777 let Value::Binary(bytes) = &process.registers[bin.0 as usize] else {
1778 return ExecResult::Crash;
1779 };
1780 process.registers[dest.0 as usize] = Value::Int(bytes.len() as i64);
1781 ExecResult::Continue(1)
1782 }
1783
1784 Instruction::BinaryAt { bin, index, dest } => {
1785 let Some(process) = self.processes.get_mut(&pid) else {
1786 return ExecResult::Crash;
1787 };
1788 let Value::Binary(bytes) = &process.registers[bin.0 as usize] else {
1789 return ExecResult::Crash;
1790 };
1791 let Value::Int(idx) = process.registers[index.0 as usize] else {
1792 return ExecResult::Crash;
1793 };
1794 if idx < 0 || idx as usize >= bytes.len() {
1795 return ExecResult::Crash;
1796 }
1797 process.registers[dest.0 as usize] = Value::Int(bytes[idx as usize] as i64);
1798 ExecResult::Continue(1)
1799 }
1800
1801 Instruction::BinarySlice {
1802 bin,
1803 start,
1804 len,
1805 dest,
1806 } => {
1807 let Some(process) = self.processes.get_mut(&pid) else {
1808 return ExecResult::Crash;
1809 };
1810 let Value::Binary(bytes) = &process.registers[bin.0 as usize] else {
1811 return ExecResult::Crash;
1812 };
1813 let Value::Int(start_idx) = process.registers[start.0 as usize] else {
1814 return ExecResult::Crash;
1815 };
1816 let Value::Int(length) = process.registers[len.0 as usize] else {
1817 return ExecResult::Crash;
1818 };
1819 if start_idx < 0 || length < 0 {
1820 return ExecResult::Crash;
1821 }
1822 let start_idx = start_idx as usize;
1823 let length = length as usize;
1824 if start_idx + length > bytes.len() {
1825 return ExecResult::Crash;
1826 }
1827 let slice = bytes[start_idx..start_idx + length].to_vec();
1828 process.registers[dest.0 as usize] = Value::Binary(slice);
1829 ExecResult::Continue(1)
1830 }
1831
1832 Instruction::BinaryConcat { a, b, dest } => {
1833 let Some(process) = self.processes.get_mut(&pid) else {
1834 return ExecResult::Crash;
1835 };
1836 let Value::Binary(bytes_a) = &process.registers[a.0 as usize] else {
1837 return ExecResult::Crash;
1838 };
1839 let Value::Binary(bytes_b) = &process.registers[b.0 as usize] else {
1840 return ExecResult::Crash;
1841 };
1842 let mut result = bytes_a.clone();
1843 result.extend_from_slice(bytes_b);
1844 process.registers[dest.0 as usize] = Value::Binary(result);
1845 ExecResult::Continue(1)
1846 }
1847
1848 Instruction::IsBinary { source, dest } => {
1849 let Some(process) = self.processes.get_mut(&pid) else {
1850 return ExecResult::Crash;
1851 };
1852 let is_binary = matches!(process.registers[source.0 as usize], Value::Binary(_));
1853 process.registers[dest.0 as usize] = Value::Int(if is_binary { 1 } else { 0 });
1854 ExecResult::Continue(1)
1855 }
1856
1857 Instruction::StringToBinary { source, dest } => {
1858 let Some(process) = self.processes.get_mut(&pid) else {
1859 return ExecResult::Crash;
1860 };
1861 let Value::String(s) = &process.registers[source.0 as usize] else {
1862 return ExecResult::Crash;
1863 };
1864 let bytes = s.as_bytes().to_vec();
1865 process.registers[dest.0 as usize] = Value::Binary(bytes);
1866 ExecResult::Continue(1)
1867 }
1868
1869 Instruction::BinaryToString { source, dest } => {
1870 let Some(process) = self.processes.get_mut(&pid) else {
1871 return ExecResult::Crash;
1872 };
1873 let Value::Binary(bytes) = &process.registers[source.0 as usize] else {
1874 return ExecResult::Crash;
1875 };
1876 let Ok(s) = std::str::from_utf8(bytes) else {
1877 return ExecResult::Crash;
1878 };
1879 process.registers[dest.0 as usize] = Value::String(s.to_string());
1880 ExecResult::Continue(1)
1881 }
1882
1883 Instruction::BinaryConstructSegments { segments, dest } => {
1885 let Some(process) = self.processes.get(&pid) else {
1886 return ExecResult::Crash;
1887 };
1888
1889 let mut result = Vec::new();
1890
1891 for (source, segment) in segments {
1892 let value = match source {
1893 crate::SegmentSource::Int(n) => n,
1894 crate::SegmentSource::Reg(r) => {
1895 match &process.registers[r.0 as usize] {
1896 Value::Int(n) => *n,
1897 Value::Binary(bytes) => {
1898 if segment.bit_type == crate::BitType::Binary {
1900 result.extend_from_slice(bytes);
1901 continue;
1902 }
1903 return ExecResult::Crash;
1904 }
1905 _ => return ExecResult::Crash,
1906 }
1907 }
1908 };
1909
1910 match segment.bit_type {
1911 crate::BitType::Integer => {
1912 let size_bits = segment.size.unwrap_or(8);
1913 let size_bytes = (size_bits as usize + 7) / 8;
1914
1915 let bytes = match segment.endianness {
1917 crate::Endianness::Big => {
1918 let mut bytes = Vec::with_capacity(size_bytes);
1919 for i in (0..size_bytes).rev() {
1920 bytes.push(((value >> (i * 8)) & 0xFF) as u8);
1921 }
1922 bytes
1923 }
1924 crate::Endianness::Little => {
1925 let mut bytes = Vec::with_capacity(size_bytes);
1926 for i in 0..size_bytes {
1927 bytes.push(((value >> (i * 8)) & 0xFF) as u8);
1928 }
1929 bytes
1930 }
1931 };
1932 result.extend(bytes);
1933 }
1934 crate::BitType::Float => {
1935 let size_bits = segment.size.unwrap_or(64);
1936 let f = value as f64;
1937 if size_bits == 64 {
1938 let bytes = match segment.endianness {
1939 crate::Endianness::Big => f.to_be_bytes(),
1940 crate::Endianness::Little => f.to_le_bytes(),
1941 };
1942 result.extend_from_slice(&bytes);
1943 } else if size_bits == 32 {
1944 let f32_val = f as f32;
1945 let bytes = match segment.endianness {
1946 crate::Endianness::Big => f32_val.to_be_bytes(),
1947 crate::Endianness::Little => f32_val.to_le_bytes(),
1948 };
1949 result.extend_from_slice(&bytes);
1950 } else {
1951 return ExecResult::Crash;
1952 }
1953 }
1954 crate::BitType::Binary => {
1955 return ExecResult::Crash;
1957 }
1958 crate::BitType::Utf8 => {
1959 if let Some(c) = char::from_u32(value as u32) {
1961 let mut buf = [0u8; 4];
1962 let s = c.encode_utf8(&mut buf);
1963 result.extend_from_slice(s.as_bytes());
1964 } else {
1965 return ExecResult::Crash;
1966 }
1967 }
1968 }
1969 }
1970
1971 let Some(process) = self.processes.get_mut(&pid) else {
1972 return ExecResult::Crash;
1973 };
1974 process.registers[dest.0 as usize] = Value::Binary(result);
1975 ExecResult::Continue(1)
1976 }
1977
1978 Instruction::BinaryMatchStart { source } => {
1979 let Some(process) = self.processes.get_mut(&pid) else {
1980 return ExecResult::Crash;
1981 };
1982 let Value::Binary(bytes) = &process.registers[source.0 as usize] else {
1983 return ExecResult::Crash;
1984 };
1985 let bytes_clone = bytes.clone();
1986 process.binary_match_state = Some((bytes_clone, 0));
1987 ExecResult::Continue(1)
1988 }
1989
1990 Instruction::BinaryMatchSegment {
1991 segment,
1992 dest,
1993 fail_target,
1994 } => {
1995 let Some(process) = self.processes.get_mut(&pid) else {
1996 return ExecResult::Crash;
1997 };
1998
1999 let Some((ref bytes, ref mut bit_pos)) = process.binary_match_state else {
2000 return ExecResult::Jump(fail_target, 1);
2001 };
2002
2003 let size_bits = match segment.size {
2004 Some(s) => s as usize,
2005 None => {
2006 if segment.bit_type == crate::BitType::Binary {
2008 (bytes.len() * 8).saturating_sub(*bit_pos)
2009 } else {
2010 return ExecResult::Jump(fail_target, 1);
2011 }
2012 }
2013 };
2014
2015 let remaining_bits = bytes.len() * 8 - *bit_pos;
2017 if size_bits > remaining_bits {
2018 return ExecResult::Jump(fail_target, 1);
2019 }
2020
2021 if *bit_pos % 8 != 0 || size_bits % 8 != 0 {
2023 return ExecResult::Jump(fail_target, 1);
2025 }
2026
2027 let byte_pos = *bit_pos / 8;
2028 let size_bytes = size_bits / 8;
2029
2030 let value = match segment.bit_type {
2031 crate::BitType::Integer => {
2032 let mut n: i64 = 0;
2033 match segment.endianness {
2034 crate::Endianness::Big => {
2035 for i in 0..size_bytes {
2036 n = (n << 8) | (bytes[byte_pos + i] as i64);
2037 }
2038 }
2039 crate::Endianness::Little => {
2040 for i in (0..size_bytes).rev() {
2041 n = (n << 8) | (bytes[byte_pos + i] as i64);
2042 }
2043 }
2044 }
2045 if segment.signedness == crate::Signedness::Signed && size_bytes < 8 {
2047 let sign_bit = 1i64 << (size_bits - 1);
2048 if n & sign_bit != 0 {
2049 n |= !((1i64 << size_bits) - 1);
2050 }
2051 }
2052 Value::Int(n)
2053 }
2054 crate::BitType::Float => {
2055 if size_bits == 64 {
2056 let mut arr = [0u8; 8];
2057 arr.copy_from_slice(&bytes[byte_pos..byte_pos + 8]);
2058 let f = match segment.endianness {
2059 crate::Endianness::Big => f64::from_be_bytes(arr),
2060 crate::Endianness::Little => f64::from_le_bytes(arr),
2061 };
2062 Value::Float(f)
2063 } else if size_bits == 32 {
2064 let mut arr = [0u8; 4];
2065 arr.copy_from_slice(&bytes[byte_pos..byte_pos + 4]);
2066 let f = match segment.endianness {
2067 crate::Endianness::Big => f32::from_be_bytes(arr) as f64,
2068 crate::Endianness::Little => f32::from_le_bytes(arr) as f64,
2069 };
2070 Value::Float(f)
2071 } else {
2072 return ExecResult::Jump(fail_target, 1);
2073 }
2074 }
2075 crate::BitType::Binary => {
2076 Value::Binary(bytes[byte_pos..byte_pos + size_bytes].to_vec())
2077 }
2078 crate::BitType::Utf8 => {
2079 let slice = &bytes[byte_pos..];
2081 if let Some(c) = std::str::from_utf8(slice)
2082 .ok()
2083 .and_then(|s| s.chars().next())
2084 {
2085 let char_len = c.len_utf8();
2086 if char_len <= slice.len() {
2087 *bit_pos += char_len * 8;
2089 process.registers[dest.0 as usize] = Value::Int(c as i64);
2090 return ExecResult::Continue(1);
2091 }
2092 }
2093 return ExecResult::Jump(fail_target, 1);
2094 }
2095 };
2096
2097 *bit_pos += size_bits;
2098 process.registers[dest.0 as usize] = value;
2099 ExecResult::Continue(1)
2100 }
2101
2102 Instruction::BinaryMatchRest { dest } => {
2103 let Some(process) = self.processes.get_mut(&pid) else {
2104 return ExecResult::Crash;
2105 };
2106
2107 let Some((ref bytes, bit_pos)) = process.binary_match_state else {
2108 return ExecResult::Crash;
2109 };
2110
2111 if bit_pos % 8 != 0 {
2113 return ExecResult::Crash;
2114 }
2115
2116 let byte_pos = bit_pos / 8;
2117 let rest = bytes[byte_pos..].to_vec();
2118 process.registers[dest.0 as usize] = Value::Binary(rest);
2119 ExecResult::Continue(1)
2120 }
2121
2122 Instruction::BinaryGetInteger {
2123 bin,
2124 bit_offset,
2125 segment,
2126 dest,
2127 } => {
2128 let Some(process) = self.processes.get(&pid) else {
2129 return ExecResult::Crash;
2130 };
2131
2132 let Value::Binary(bytes) = &process.registers[bin.0 as usize] else {
2133 return ExecResult::Crash;
2134 };
2135 let Value::Int(offset) = &process.registers[bit_offset.0 as usize] else {
2136 return ExecResult::Crash;
2137 };
2138
2139 let bit_pos = *offset as usize;
2140 let size_bits = segment.size.unwrap_or(8) as usize;
2141
2142 if bit_pos % 8 != 0 || size_bits % 8 != 0 {
2144 return ExecResult::Crash;
2145 }
2146
2147 let byte_pos = bit_pos / 8;
2148 let size_bytes = size_bits / 8;
2149
2150 if byte_pos + size_bytes > bytes.len() {
2151 return ExecResult::Crash;
2152 }
2153
2154 let mut n: i64 = 0;
2155 match segment.endianness {
2156 crate::Endianness::Big => {
2157 for i in 0..size_bytes {
2158 n = (n << 8) | (bytes[byte_pos + i] as i64);
2159 }
2160 }
2161 crate::Endianness::Little => {
2162 for i in (0..size_bytes).rev() {
2163 n = (n << 8) | (bytes[byte_pos + i] as i64);
2164 }
2165 }
2166 }
2167
2168 if segment.signedness == crate::Signedness::Signed && size_bytes < 8 {
2170 let sign_bit = 1i64 << (size_bits - 1);
2171 if n & sign_bit != 0 {
2172 n |= !((1i64 << size_bits) - 1);
2173 }
2174 }
2175
2176 let Some(process) = self.processes.get_mut(&pid) else {
2177 return ExecResult::Crash;
2178 };
2179 process.registers[dest.0 as usize] = Value::Int(n);
2180 ExecResult::Continue(1)
2181 }
2182
2183 Instruction::BinaryPutInteger {
2184 bin,
2185 bit_offset,
2186 value,
2187 segment,
2188 dest,
2189 } => {
2190 let Some(process) = self.processes.get(&pid) else {
2191 return ExecResult::Crash;
2192 };
2193
2194 let Value::Binary(bytes) = &process.registers[bin.0 as usize] else {
2195 return ExecResult::Crash;
2196 };
2197 let Value::Int(offset) = &process.registers[bit_offset.0 as usize] else {
2198 return ExecResult::Crash;
2199 };
2200 let Value::Int(val) = &process.registers[value.0 as usize] else {
2201 return ExecResult::Crash;
2202 };
2203
2204 let bit_pos = *offset as usize;
2205 let size_bits = segment.size.unwrap_or(8) as usize;
2206
2207 if bit_pos % 8 != 0 || size_bits % 8 != 0 {
2209 return ExecResult::Crash;
2210 }
2211
2212 let byte_pos = bit_pos / 8;
2213 let size_bytes = size_bits / 8;
2214
2215 let mut new_bytes = bytes.clone();
2217
2218 while new_bytes.len() < byte_pos + size_bytes {
2220 new_bytes.push(0);
2221 }
2222
2223 match segment.endianness {
2224 crate::Endianness::Big => {
2225 for i in 0..size_bytes {
2226 new_bytes[byte_pos + i] =
2227 ((val >> ((size_bytes - 1 - i) * 8)) & 0xFF) as u8;
2228 }
2229 }
2230 crate::Endianness::Little => {
2231 for i in 0..size_bytes {
2232 new_bytes[byte_pos + i] = ((val >> (i * 8)) & 0xFF) as u8;
2233 }
2234 }
2235 }
2236
2237 let Some(process) = self.processes.get_mut(&pid) else {
2238 return ExecResult::Crash;
2239 };
2240 process.registers[dest.0 as usize] = Value::Binary(new_bytes);
2241 ExecResult::Continue(1)
2242 }
2243
2244 Instruction::MakeRef { dest } => {
2246 let ref_id = self.next_ref;
2247 self.next_ref += 1;
2248 let Some(process) = self.processes.get_mut(&pid) else {
2249 return ExecResult::Crash;
2250 };
2251 process.registers[dest.0 as usize] = Value::Ref(ref_id);
2252 ExecResult::Continue(1)
2253 }
2254
2255 Instruction::IsRef { source, dest } => {
2256 let Some(process) = self.processes.get_mut(&pid) else {
2257 return ExecResult::Crash;
2258 };
2259 let is_ref = matches!(process.registers[source.0 as usize], Value::Ref(_));
2260 process.registers[dest.0 as usize] = Value::Int(if is_ref { 1 } else { 0 });
2261 ExecResult::Continue(1)
2262 }
2263
2264 Instruction::LoadFloat { value, dest } => {
2266 let Some(process) = self.processes.get_mut(&pid) else {
2267 return ExecResult::Crash;
2268 };
2269 process.registers[dest.0 as usize] = Value::Float(value);
2270 ExecResult::Continue(1)
2271 }
2272
2273 Instruction::IsFloat { source, dest } => {
2274 let Some(process) = self.processes.get_mut(&pid) else {
2275 return ExecResult::Crash;
2276 };
2277 let is_float = matches!(process.registers[source.0 as usize], Value::Float(_));
2278 process.registers[dest.0 as usize] = Value::Int(if is_float { 1 } else { 0 });
2279 ExecResult::Continue(1)
2280 }
2281
2282 Instruction::IntToFloat { source, dest } => {
2283 let Some(process) = self.processes.get_mut(&pid) else {
2284 return ExecResult::Crash;
2285 };
2286 let Value::Int(n) = process.registers[source.0 as usize] else {
2287 return ExecResult::Crash;
2288 };
2289 process.registers[dest.0 as usize] = Value::Float(n as f64);
2290 ExecResult::Continue(1)
2291 }
2292
2293 Instruction::FloatToInt { source, dest } => {
2294 let Some(process) = self.processes.get_mut(&pid) else {
2295 return ExecResult::Crash;
2296 };
2297 let Value::Float(f) = process.registers[source.0 as usize] else {
2298 return ExecResult::Crash;
2299 };
2300 process.registers[dest.0 as usize] = Value::Int(f as i64);
2301 ExecResult::Continue(1)
2302 }
2303
2304 Instruction::Floor { source, dest } => {
2305 let Some(process) = self.processes.get_mut(&pid) else {
2306 return ExecResult::Crash;
2307 };
2308 let Value::Float(f) = process.registers[source.0 as usize] else {
2309 return ExecResult::Crash;
2310 };
2311 process.registers[dest.0 as usize] = Value::Float(f.floor());
2312 ExecResult::Continue(1)
2313 }
2314
2315 Instruction::Ceil { source, dest } => {
2316 let Some(process) = self.processes.get_mut(&pid) else {
2317 return ExecResult::Crash;
2318 };
2319 let Value::Float(f) = process.registers[source.0 as usize] else {
2320 return ExecResult::Crash;
2321 };
2322 process.registers[dest.0 as usize] = Value::Float(f.ceil());
2323 ExecResult::Continue(1)
2324 }
2325
2326 Instruction::Round { source, dest } => {
2327 let Some(process) = self.processes.get_mut(&pid) else {
2328 return ExecResult::Crash;
2329 };
2330 let Value::Float(f) = process.registers[source.0 as usize] else {
2331 return ExecResult::Crash;
2332 };
2333 process.registers[dest.0 as usize] = Value::Float(f.round());
2334 ExecResult::Continue(1)
2335 }
2336
2337 Instruction::Trunc { source, dest } => {
2338 let Some(process) = self.processes.get_mut(&pid) else {
2339 return ExecResult::Crash;
2340 };
2341 let Value::Float(f) = process.registers[source.0 as usize] else {
2342 return ExecResult::Crash;
2343 };
2344 process.registers[dest.0 as usize] = Value::Float(f.trunc());
2345 ExecResult::Continue(1)
2346 }
2347
2348 Instruction::Sqrt { source, dest } => {
2349 let Some(process) = self.processes.get_mut(&pid) else {
2350 return ExecResult::Crash;
2351 };
2352 let f = match process.registers[source.0 as usize] {
2353 Value::Float(f) => f,
2354 Value::Int(n) => n as f64,
2355 _ => return ExecResult::Crash,
2356 };
2357 process.registers[dest.0 as usize] = Value::Float(f.sqrt());
2358 ExecResult::Continue(1)
2359 }
2360
2361 Instruction::Abs { source, dest } => {
2362 let Some(process) = self.processes.get_mut(&pid) else {
2363 return ExecResult::Crash;
2364 };
2365 let result = match process.registers[source.0 as usize] {
2366 Value::Float(f) => Value::Float(f.abs()),
2367 Value::Int(n) => Value::Int(n.abs()),
2368 _ => return ExecResult::Crash,
2369 };
2370 process.registers[dest.0 as usize] = result;
2371 ExecResult::Continue(1)
2372 }
2373
2374 Instruction::Pow { base, exp, dest } => {
2375 let Some(process) = self.processes.get_mut(&pid) else {
2376 return ExecResult::Crash;
2377 };
2378 let b = match process.registers[base.0 as usize] {
2379 Value::Float(f) => f,
2380 Value::Int(n) => n as f64,
2381 _ => return ExecResult::Crash,
2382 };
2383 let e = match process.registers[exp.0 as usize] {
2384 Value::Float(f) => f,
2385 Value::Int(n) => n as f64,
2386 _ => return ExecResult::Crash,
2387 };
2388 process.registers[dest.0 as usize] = Value::Float(b.powf(e));
2389 ExecResult::Continue(1)
2390 }
2391
2392 Instruction::SendAfter {
2394 delay,
2395 to,
2396 msg,
2397 dest,
2398 } => {
2399 let Some(process) = self.processes.get(&pid) else {
2400 return ExecResult::Crash;
2401 };
2402 let Some(target_pid) = self.resolve_pid(pid, &to) else {
2403 return ExecResult::Crash;
2405 };
2406 let message = process.registers[msg.0 as usize].clone();
2407
2408 let timer_ref = self.next_ref;
2410 self.next_ref += 1;
2411
2412 self.timers.push(Timer {
2414 timer_ref,
2415 target: target_pid,
2416 message,
2417 remaining: delay,
2418 });
2419
2420 let Some(process) = self.processes.get_mut(&pid) else {
2422 return ExecResult::Crash;
2423 };
2424 process.registers[dest.0 as usize] = Value::Ref(timer_ref);
2425 ExecResult::Continue(1)
2426 }
2427
2428 Instruction::StartTimer { delay, msg, dest } => {
2429 let Some(process) = self.processes.get(&pid) else {
2430 return ExecResult::Crash;
2431 };
2432 let message_value = process.registers[msg.0 as usize].clone();
2433
2434 let timer_ref = self.next_ref;
2436 self.next_ref += 1;
2437
2438 let timeout_msg = Value::Tuple(vec![
2440 Value::Atom("timeout".to_string()),
2441 Value::Ref(timer_ref),
2442 message_value,
2443 ]);
2444
2445 self.timers.push(Timer {
2447 timer_ref,
2448 target: pid,
2449 message: timeout_msg,
2450 remaining: delay,
2451 });
2452
2453 let Some(process) = self.processes.get_mut(&pid) else {
2455 return ExecResult::Crash;
2456 };
2457 process.registers[dest.0 as usize] = Value::Ref(timer_ref);
2458 ExecResult::Continue(1)
2459 }
2460
2461 Instruction::CancelTimer { timer_ref, dest } => {
2462 let Some(process) = self.processes.get(&pid) else {
2463 return ExecResult::Crash;
2464 };
2465 let Value::Ref(ref_id) = process.registers[timer_ref.0 as usize] else {
2466 return ExecResult::Crash;
2467 };
2468
2469 let result =
2471 if let Some(idx) = self.timers.iter().position(|t| t.timer_ref == ref_id) {
2472 let timer = self.timers.remove(idx);
2473 Value::Int(timer.remaining as i64)
2474 } else {
2475 Value::Atom("ok".to_string())
2477 };
2478
2479 let Some(process) = self.processes.get_mut(&pid) else {
2480 return ExecResult::Crash;
2481 };
2482 process.registers[dest.0 as usize] = result;
2483 ExecResult::Continue(1)
2484 }
2485
2486 Instruction::ReadTimer { timer_ref, dest } => {
2487 let Some(process) = self.processes.get(&pid) else {
2488 return ExecResult::Crash;
2489 };
2490 let Value::Ref(ref_id) = process.registers[timer_ref.0 as usize] else {
2491 return ExecResult::Crash;
2492 };
2493
2494 let remaining = self
2496 .timers
2497 .iter()
2498 .find(|t| t.timer_ref == ref_id)
2499 .map(|t| Value::Int(t.remaining as i64))
2500 .unwrap_or(Value::Int(0));
2501
2502 let Some(process) = self.processes.get_mut(&pid) else {
2503 return ExecResult::Crash;
2504 };
2505 process.registers[dest.0 as usize] = remaining;
2506 ExecResult::Continue(1)
2507 }
2508
2509 Instruction::PrintLn { source } => {
2511 let Some(process) = self.processes.get(&pid) else {
2512 return ExecResult::Crash;
2513 };
2514 let value = &process.registers[source.0 as usize];
2515 self.output.push(format!("{:?}", value));
2517 #[cfg(not(test))]
2518 println!("{:?}", value);
2519 ExecResult::Continue(1)
2520 }
2521
2522 Instruction::ReadLine { dest } => {
2523 let Some(process) = self.processes.get_mut(&pid) else {
2524 return ExecResult::Crash;
2525 };
2526 #[cfg(test)]
2528 {
2529 process.registers[dest.0 as usize] = Value::Atom("eof".to_string());
2530 }
2531 #[cfg(not(test))]
2532 {
2533 use std::io::BufRead;
2534 let stdin = std::io::stdin();
2535 let mut line = String::new();
2536 match stdin.lock().read_line(&mut line) {
2537 Ok(0) => {
2538 process.registers[dest.0 as usize] = Value::Atom("eof".to_string());
2539 }
2540 Ok(_) => {
2541 let trimmed = line.trim_end_matches('\n').trim_end_matches('\r');
2543 process.registers[dest.0 as usize] = Value::String(trimmed.to_string());
2544 }
2545 Err(_) => {
2546 process.registers[dest.0 as usize] = Value::Atom("eof".to_string());
2547 }
2548 }
2549 }
2550 ExecResult::Continue(1)
2551 }
2552
2553 Instruction::FileRead { path, dest } => {
2554 let Some(process) = self.processes.get_mut(&pid) else {
2555 return ExecResult::Crash;
2556 };
2557 let path_str = match &process.registers[path.0 as usize] {
2558 Value::String(s) => s.clone(),
2559 _ => return ExecResult::Crash,
2560 };
2561 match std::fs::read(&path_str) {
2562 Ok(bytes) => {
2563 process.registers[dest.0 as usize] =
2564 Value::Tuple(vec![Value::Atom("ok".to_string()), Value::Binary(bytes)]);
2565 }
2566 Err(e) => {
2567 process.registers[dest.0 as usize] = Value::Tuple(vec![
2568 Value::Atom("error".to_string()),
2569 Value::Atom(e.kind().to_string()),
2570 ]);
2571 }
2572 }
2573 ExecResult::Continue(1)
2574 }
2575
2576 Instruction::FileWrite {
2577 path,
2578 content,
2579 dest,
2580 } => {
2581 let Some(process) = self.processes.get_mut(&pid) else {
2582 return ExecResult::Crash;
2583 };
2584 let path_str = match &process.registers[path.0 as usize] {
2585 Value::String(s) => s.clone(),
2586 _ => return ExecResult::Crash,
2587 };
2588 let bytes = match &process.registers[content.0 as usize] {
2589 Value::Binary(b) => b.clone(),
2590 Value::String(s) => s.as_bytes().to_vec(),
2591 _ => return ExecResult::Crash,
2592 };
2593 match std::fs::write(&path_str, bytes) {
2594 Ok(()) => {
2595 process.registers[dest.0 as usize] = Value::Atom("ok".to_string());
2596 }
2597 Err(e) => {
2598 process.registers[dest.0 as usize] = Value::Tuple(vec![
2599 Value::Atom("error".to_string()),
2600 Value::Atom(e.kind().to_string()),
2601 ]);
2602 }
2603 }
2604 ExecResult::Continue(1)
2605 }
2606
2607 Instruction::FileExists { path, dest } => {
2608 let Some(process) = self.processes.get_mut(&pid) else {
2609 return ExecResult::Crash;
2610 };
2611 let path_str = match &process.registers[path.0 as usize] {
2612 Value::String(s) => s.clone(),
2613 _ => return ExecResult::Crash,
2614 };
2615 let exists = std::path::Path::new(&path_str).exists();
2616 process.registers[dest.0 as usize] = Value::Int(if exists { 1 } else { 0 });
2617 ExecResult::Continue(1)
2618 }
2619
2620 Instruction::FileDelete { path, dest } => {
2621 let Some(process) = self.processes.get_mut(&pid) else {
2622 return ExecResult::Crash;
2623 };
2624 let path_str = match &process.registers[path.0 as usize] {
2625 Value::String(s) => s.clone(),
2626 _ => return ExecResult::Crash,
2627 };
2628 match std::fs::remove_file(&path_str) {
2629 Ok(()) => {
2630 process.registers[dest.0 as usize] = Value::Atom("ok".to_string());
2631 }
2632 Err(e) => {
2633 process.registers[dest.0 as usize] = Value::Tuple(vec![
2634 Value::Atom("error".to_string()),
2635 Value::Atom(e.kind().to_string()),
2636 ]);
2637 }
2638 }
2639 ExecResult::Continue(1)
2640 }
2641
2642 Instruction::SelfPid { dest } => {
2644 let Some(process) = self.processes.get_mut(&pid) else {
2645 return ExecResult::Crash;
2646 };
2647 process.registers[dest.0 as usize] = Value::Pid(pid);
2648 ExecResult::Continue(1)
2649 }
2650
2651 Instruction::ProcessList { dest } => {
2652 let pids: Vec<Value> = self.processes.keys().map(|p| Value::Pid(*p)).collect();
2653 let Some(process) = self.processes.get_mut(&pid) else {
2654 return ExecResult::Crash;
2655 };
2656 process.registers[dest.0 as usize] = Value::List(pids);
2657 ExecResult::Continue(1)
2658 }
2659
2660 Instruction::ProcessCount { dest } => {
2661 let count = self.processes.len() as i64;
2662 let Some(process) = self.processes.get_mut(&pid) else {
2663 return ExecResult::Crash;
2664 };
2665 process.registers[dest.0 as usize] = Value::Int(count);
2666 ExecResult::Continue(1)
2667 }
2668
2669 Instruction::IsAlive {
2670 pid: target_pid,
2671 dest,
2672 } => {
2673 let Some(process) = self.processes.get(&pid) else {
2674 return ExecResult::Crash;
2675 };
2676 let target = match &process.registers[target_pid.0 as usize] {
2677 Value::Pid(p) => *p,
2678 _ => return ExecResult::Crash,
2679 };
2680 let alive = self.processes.contains_key(&target);
2681 let Some(process) = self.processes.get_mut(&pid) else {
2682 return ExecResult::Crash;
2683 };
2684 process.registers[dest.0 as usize] = Value::Int(if alive { 1 } else { 0 });
2685 ExecResult::Continue(1)
2686 }
2687
2688 Instruction::ProcessInfo {
2689 pid: target_pid,
2690 dest,
2691 } => {
2692 let Some(process) = self.processes.get(&pid) else {
2693 return ExecResult::Crash;
2694 };
2695 let target = match &process.registers[target_pid.0 as usize] {
2696 Value::Pid(p) => *p,
2697 _ => return ExecResult::Crash,
2698 };
2699 let info = if let Some(target_proc) = self.processes.get(&target) {
2700 let status_atom = match target_proc.status {
2701 ProcessStatus::Ready => "ready",
2702 ProcessStatus::Waiting => "waiting",
2703 ProcessStatus::Done => "done",
2704 ProcessStatus::Crashed => "crashed",
2705 };
2706 Value::Tuple(vec![
2707 Value::Atom(status_atom.to_string()),
2708 Value::Int(target_proc.mailbox.len() as i64),
2709 Value::Int(target_proc.links.len() as i64),
2710 Value::Int(target_proc.monitors.len() as i64),
2711 Value::Int(if target_proc.trap_exit { 1 } else { 0 }),
2712 ])
2713 } else {
2714 Value::Atom("undefined".to_string())
2715 };
2716 let Some(process) = self.processes.get_mut(&pid) else {
2717 return ExecResult::Crash;
2718 };
2719 process.registers[dest.0 as usize] = info;
2720 ExecResult::Continue(1)
2721 }
2722
2723 Instruction::ModuleList { dest } => {
2724 let modules: Vec<Value> = self
2725 .modules
2726 .keys()
2727 .map(|name| Value::Atom(name.clone()))
2728 .collect();
2729 let Some(process) = self.processes.get_mut(&pid) else {
2730 return ExecResult::Crash;
2731 };
2732 process.registers[dest.0 as usize] = Value::List(modules);
2733 ExecResult::Continue(1)
2734 }
2735
2736 Instruction::FunctionExported {
2737 module,
2738 function,
2739 arity,
2740 dest,
2741 } => {
2742 let Some(process) = self.processes.get(&pid) else {
2743 return ExecResult::Crash;
2744 };
2745 let mod_name = match &process.registers[module.0 as usize] {
2746 Value::Atom(s) => s.clone(),
2747 Value::String(s) => s.clone(),
2748 _ => return ExecResult::Crash,
2749 };
2750 let func_name = match &process.registers[function.0 as usize] {
2751 Value::Atom(s) => s.clone(),
2752 Value::String(s) => s.clone(),
2753 _ => return ExecResult::Crash,
2754 };
2755 let func_arity = match &process.registers[arity.0 as usize] {
2756 Value::Int(n) => *n as u8,
2757 _ => return ExecResult::Crash,
2758 };
2759 let exported = self
2760 .modules
2761 .get(&mod_name)
2762 .map(|m| m.is_exported(&func_name, func_arity))
2763 .unwrap_or(false);
2764 let Some(process) = self.processes.get_mut(&pid) else {
2765 return ExecResult::Crash;
2766 };
2767 process.registers[dest.0 as usize] = Value::Int(if exported { 1 } else { 0 });
2768 ExecResult::Continue(1)
2769 }
2770
2771 Instruction::Try {
2773 catch_target,
2774 after_target,
2775 } => {
2776 let Some(process) = self.processes.get_mut(&pid) else {
2777 return ExecResult::Crash;
2778 };
2779 process.try_stack.push(TryFrame {
2781 catch_target,
2782 after_target,
2783 call_stack_depth: process.call_stack.len(),
2784 stack_depth: process.stack.len(),
2785 });
2786 ExecResult::Continue(1)
2787 }
2788
2789 Instruction::EndTry => {
2790 let Some(process) = self.processes.get_mut(&pid) else {
2791 return ExecResult::Crash;
2792 };
2793 if let Some(frame) = process.try_stack.pop() {
2795 if let Some(after_target) = frame.after_target {
2797 return ExecResult::Jump(after_target, 1);
2798 }
2799 }
2800 ExecResult::Continue(1)
2801 }
2802
2803 Instruction::Throw { class, reason } => {
2804 let Some(process) = self.processes.get(&pid) else {
2805 return ExecResult::Crash;
2806 };
2807 let class_val = process.registers[class.0 as usize].clone();
2808 let reason_val = process.registers[reason.0 as usize].clone();
2809 ExecResult::Throw(class_val, reason_val)
2810 }
2811
2812 Instruction::GetException { dest } => {
2813 let Some(process) = self.processes.get_mut(&pid) else {
2814 return ExecResult::Crash;
2815 };
2816 let exception_tuple =
2817 if let Some((class, reason, stacktrace)) = &process.current_exception {
2818 let stacktrace_list = Value::List(
2820 stacktrace
2821 .iter()
2822 .map(|s| Value::String(s.clone()))
2823 .collect(),
2824 );
2825 Value::Tuple(vec![class.clone(), reason.clone(), stacktrace_list])
2826 } else {
2827 Value::None
2828 };
2829 process.registers[dest.0 as usize] = exception_tuple;
2830 ExecResult::Continue(1)
2831 }
2832
2833 Instruction::ClearException => {
2834 let Some(process) = self.processes.get_mut(&pid) else {
2835 return ExecResult::Crash;
2836 };
2837 process.current_exception = None;
2838 ExecResult::Continue(1)
2839 }
2840
2841 Instruction::Reraise => {
2842 let Some(process) = self.processes.get(&pid) else {
2843 return ExecResult::Crash;
2844 };
2845 if let Some((class, reason, _)) = &process.current_exception {
2846 ExecResult::Throw(class.clone(), reason.clone())
2847 } else {
2848 ExecResult::Crash
2850 }
2851 }
2852 }
2853 }
2854
2855 fn bigint_arith_op<F, G>(
2857 &mut self,
2858 pid: Pid,
2859 a: &Operand,
2860 b: &Operand,
2861 dest: crate::Register,
2862 checked_op: F,
2863 bigint_op: G,
2864 ) -> ExecResult
2865 where
2866 F: Fn(i64, i64) -> Option<Value>,
2867 G: Fn(BigInt, BigInt) -> BigInt,
2868 {
2869 let Some(process) = self.processes.get(&pid) else {
2870 return ExecResult::Crash;
2871 };
2872 let av = self.resolve_operand_value(process, a);
2873 let bv = self.resolve_operand_value(process, b);
2874 match (av, bv) {
2875 (Some(Value::Int(x)), Some(Value::Int(y))) => {
2876 let result = checked_op(x, y).unwrap_or_else(|| {
2878 Value::from_bigint(bigint_op(BigInt::from(x), BigInt::from(y)))
2879 });
2880 if let Some(p) = self.processes.get_mut(&pid) {
2881 p.registers[dest.0 as usize] = result;
2882 }
2883 ExecResult::Continue(1)
2884 }
2885 (Some(a_val), Some(b_val)) => {
2886 match (a_val.to_bigint(), b_val.to_bigint()) {
2888 (Some(x), Some(y)) => {
2889 let result = Value::from_bigint(bigint_op(x, y));
2890 if let Some(p) = self.processes.get_mut(&pid) {
2891 p.registers[dest.0 as usize] = result;
2892 }
2893 ExecResult::Continue(1)
2894 }
2895 _ => ExecResult::Crash,
2896 }
2897 }
2898 _ => ExecResult::Crash,
2899 }
2900 }
2901
2902 fn bigint_cmp_op<F>(
2904 &mut self,
2905 pid: Pid,
2906 a: &Operand,
2907 b: &Operand,
2908 dest: crate::Register,
2909 op: F,
2910 ) -> ExecResult
2911 where
2912 F: Fn(&BigInt, &BigInt) -> bool,
2913 {
2914 let Some(process) = self.processes.get(&pid) else {
2915 return ExecResult::Crash;
2916 };
2917 let av = self.resolve_operand_value(process, a);
2918 let bv = self.resolve_operand_value(process, b);
2919 match (av, bv) {
2920 (Some(a_val), Some(b_val)) => match (a_val.to_bigint(), b_val.to_bigint()) {
2921 (Some(x), Some(y)) => {
2922 let result = if op(&x, &y) { 1 } else { 0 };
2923 if let Some(p) = self.processes.get_mut(&pid) {
2924 p.registers[dest.0 as usize] = Value::Int(result);
2925 }
2926 ExecResult::Continue(1)
2927 }
2928 _ => ExecResult::Crash,
2929 },
2930 _ => ExecResult::Crash,
2931 }
2932 }
2933
2934 fn resolve_operand_value(&self, process: &Process, operand: &Operand) -> Option<Value> {
2936 match operand {
2937 Operand::Int(n) => Some(Value::Int(*n)),
2938 Operand::Reg(r) => {
2939 let val = &process.registers[r.0 as usize];
2940 match val {
2941 Value::Int(_) | Value::BigInt(_) => Some(val.clone()),
2942 _ => None,
2943 }
2944 }
2945 }
2946 }
2947
2948 fn resolve_operand(&self, process: &Process, operand: &Operand) -> Option<i64> {
2950 match operand {
2951 Operand::Int(n) => Some(*n),
2952 Operand::Reg(r) => process.registers[r.0 as usize].as_int(),
2953 }
2954 }
2955
2956 fn resolve_pid(&self, current: Pid, source: &Source) -> Option<Pid> {
2957 match source {
2958 Source::Self_ => Some(current),
2959 Source::Parent => {
2960 let process = self.processes.get(¤t)?;
2961 process.parent
2962 }
2963 Source::Pid(p) => Some(*p),
2964 Source::Reg(r) => {
2965 let process = self.processes.get(¤t)?;
2966 match &process.registers[r.0 as usize] {
2967 Value::Pid(p) => Some(*p),
2968 _ => None,
2969 }
2970 }
2971 Source::Named(name) => self.registry.get(name).copied(),
2972 }
2973 }
2974
2975 fn resolve_source(&self, process: &Process, source: &Source) -> Value {
2976 match source {
2977 Source::Self_ => Value::Pid(process.pid),
2978 Source::Parent => process.parent.map(Value::Pid).unwrap_or(Value::None),
2979 Source::Pid(p) => Value::Pid(*p),
2980 Source::Reg(r) => process.registers[r.0 as usize].clone(),
2981 Source::Named(name) => self
2982 .registry
2983 .get(name)
2984 .map(|p| Value::Pid(*p))
2985 .unwrap_or(Value::None),
2986 }
2987 }
2988
2989 fn message_to_value(msg: Message) -> Value {
2990 match msg {
2991 Message::User(s) => Value::String(s),
2992 Message::System(sys) => match sys {
2993 SystemMsg::Exit(pid, reason) => {
2994 Value::Tuple(vec![
2996 Value::Atom("EXIT".to_string()),
2997 Value::Pid(pid),
2998 reason,
2999 ])
3000 }
3001 SystemMsg::Down(ref_id, pid, reason) => {
3002 Value::Tuple(vec![
3004 Value::Atom("DOWN".to_string()),
3005 Value::Ref(ref_id),
3006 Value::Atom("process".to_string()),
3007 Value::Pid(pid),
3008 reason,
3009 ])
3010 }
3011 },
3012 }
3013 }
3014
3015 fn match_pattern(
3018 value: &Value,
3019 pattern: &Pattern,
3020 bindings: &mut Vec<(Register, Value)>,
3021 ) -> bool {
3022 match pattern {
3023 Pattern::Wildcard => true,
3024
3025 Pattern::Variable(reg) => {
3026 bindings.push((*reg, value.clone()));
3027 true
3028 }
3029
3030 Pattern::Int(n) => matches!(value, Value::Int(v) if v == n),
3031
3032 Pattern::Atom(name) => matches!(value, Value::Atom(a) if a == name),
3033
3034 Pattern::String(s) => matches!(value, Value::String(v) if v == s),
3035
3036 Pattern::Binary(bytes) => matches!(value, Value::Binary(b) if b == bytes),
3037
3038 Pattern::Tuple(patterns) => {
3039 let Value::Tuple(elements) = value else {
3040 return false;
3041 };
3042 if elements.len() != patterns.len() {
3043 return false;
3044 }
3045 for (elem, pat) in elements.iter().zip(patterns.iter()) {
3046 if !Self::match_pattern(elem, pat, bindings) {
3047 return false;
3048 }
3049 }
3050 true
3051 }
3052
3053 Pattern::ListEmpty => {
3054 matches!(value, Value::List(elements) if elements.is_empty())
3055 }
3056
3057 Pattern::ListCons { head, tail } => {
3058 let Value::List(elements) = value else {
3059 return false;
3060 };
3061 if elements.is_empty() {
3062 return false;
3063 }
3064 if !Self::match_pattern(&elements[0], head, bindings) {
3066 return false;
3067 }
3068 let tail_value = Value::List(elements[1..].to_vec());
3070 Self::match_pattern(&tail_value, tail, bindings)
3071 }
3072
3073 Pattern::Map(pairs) => {
3074 let Value::Map(map) = value else {
3075 return false;
3076 };
3077 for (key_pattern, value_pattern) in pairs {
3079 let mut found = false;
3081 for (k, v) in map.iter() {
3082 let mut key_bindings = Vec::new();
3083 if Self::match_pattern(k, key_pattern, &mut key_bindings) {
3084 let mut val_bindings = Vec::new();
3086 if Self::match_pattern(v, value_pattern, &mut val_bindings) {
3087 bindings.extend(key_bindings);
3089 bindings.extend(val_bindings);
3090 found = true;
3091 break;
3092 }
3093 }
3094 }
3095 if !found {
3096 return false;
3097 }
3098 }
3099 true
3100 }
3101 }
3102 }
3103
3104 fn finish_process_with_reason(&mut self, pid: Pid, status: ProcessStatus, reason: Value) {
3105 let (links, monitors) = if let Some(p) = self.processes.get_mut(&pid) {
3106 p.status = status;
3107 p.exit_reason = reason.clone();
3108 (p.links.clone(), p.monitored_by.clone())
3109 } else {
3110 return;
3111 };
3112
3113 self.registry.retain(|_, v| *v != pid);
3115
3116 let is_normal = reason == Value::Atom("normal".to_string());
3118
3119 let mut to_crash = Vec::new();
3123 for linked_pid in links {
3124 if let Some(linked) = self.processes.get_mut(&linked_pid) {
3125 linked.links.retain(|p| *p != pid);
3127
3128 if linked.trap_exit {
3129 let exit_tuple = Value::Tuple(vec![
3131 Value::Atom("EXIT".to_string()),
3132 Value::Pid(pid),
3133 reason.clone(),
3134 ]);
3135 linked
3136 .mailbox
3137 .push_back(Message::System(SystemMsg::Exit(pid, reason.clone())));
3138 linked
3140 .mailbox
3141 .push_back(Message::User(format!("{:?}", exit_tuple)));
3142 if linked.status == ProcessStatus::Waiting {
3143 linked.status = ProcessStatus::Ready;
3144 self.ready_queue.push_back(linked_pid);
3145 }
3146 } else if !is_normal {
3147 to_crash.push(linked_pid);
3149 }
3150 }
3152 }
3153
3154 for linked_pid in to_crash {
3156 self.finish_process_with_reason(linked_pid, ProcessStatus::Crashed, reason.clone());
3157 }
3158
3159 for (ref_id, monitor_pid) in monitors {
3161 if let Some(monitor) = self.processes.get_mut(&monitor_pid) {
3162 monitor.mailbox.push_back(Message::System(SystemMsg::Down(
3163 ref_id,
3164 pid,
3165 reason.clone(),
3166 )));
3167 if monitor.status == ProcessStatus::Waiting {
3168 monitor.status = ProcessStatus::Ready;
3169 self.ready_queue.push_back(monitor_pid);
3170 }
3171 monitor.monitors.retain(|(_, target)| *target != pid);
3173 }
3174 }
3175 }
3176
3177 pub fn process_count(&self) -> (usize, usize, usize, usize) {
3179 let mut ready = 0;
3180 let mut waiting = 0;
3181 let mut done = 0;
3182 let mut crashed = 0;
3183
3184 for p in self.processes.values() {
3185 match p.status {
3186 ProcessStatus::Ready => ready += 1,
3187 ProcessStatus::Waiting => waiting += 1,
3188 ProcessStatus::Done => done += 1,
3189 ProcessStatus::Crashed => crashed += 1,
3190 }
3191 }
3192
3193 (ready, waiting, done, crashed)
3194 }
3195}
3196
3197impl Default for Scheduler {
3198 fn default() -> Self {
3199 Self::new()
3200 }
3201}
3202
3203#[cfg(test)]
3204mod tests {
3205 use super::*;
3206 use crate::{Operand, Register};
3207
3208 fn run_to_idle(scheduler: &mut Scheduler) {
3209 loop {
3210 if scheduler.step(100) == StepResult::Idle {
3211 break;
3212 }
3213 }
3214 }
3215
3216 #[test]
3217 fn test_spawn_and_end() {
3218 let mut scheduler = Scheduler::new();
3219
3220 let program = vec![Instruction::End];
3221 scheduler.spawn(program);
3222
3223 run_to_idle(&mut scheduler);
3224
3225 let (ready, waiting, done, crashed) = scheduler.process_count();
3226 assert_eq!((ready, waiting, done, crashed), (0, 0, 1, 0));
3227 }
3228
3229 #[test]
3230 fn test_work_uses_reductions() {
3231 let mut scheduler = Scheduler::new();
3232
3233 let program = vec![Instruction::Work { amount: 5 }, Instruction::End];
3234 scheduler.spawn(program);
3235
3236 let result = scheduler.step(3);
3238 assert_eq!(result, StepResult::Busy);
3239
3240 let (ready, _, _, _) = scheduler.process_count();
3242 assert_eq!(ready, 1);
3243
3244 run_to_idle(&mut scheduler);
3246 let (_, _, done, _) = scheduler.process_count();
3247 assert_eq!(done, 1);
3248 }
3249
3250 #[test]
3251 fn test_message_passing() {
3252 let mut scheduler = Scheduler::new();
3253
3254 let child = vec![Instruction::Receive { dest: Register(0) }, Instruction::End];
3256
3257 let parent = vec![
3259 Instruction::Spawn {
3260 code: child,
3261 dest: Register(0),
3262 },
3263 Instruction::Send {
3264 to: Source::Reg(Register(0)),
3265 msg: "hello".into(),
3266 },
3267 Instruction::End,
3268 ];
3269
3270 scheduler.spawn(parent);
3271 run_to_idle(&mut scheduler);
3272
3273 let (_, _, done, _) = scheduler.process_count();
3274 assert_eq!(done, 2);
3275 }
3276
3277 #[test]
3278 fn test_parent_tracking() {
3279 let mut scheduler = Scheduler::new();
3280
3281 let child = vec![
3283 Instruction::Send {
3284 to: Source::Parent,
3285 msg: "from child".into(),
3286 },
3287 Instruction::End,
3288 ];
3289
3290 let parent = vec![
3292 Instruction::Spawn {
3293 code: child,
3294 dest: Register(0),
3295 },
3296 Instruction::Receive { dest: Register(1) },
3297 Instruction::End,
3298 ];
3299
3300 scheduler.spawn(parent);
3301 run_to_idle(&mut scheduler);
3302
3303 let parent_process = scheduler.processes.get(&Pid(0)).unwrap();
3305 match &parent_process.registers[1] {
3306 Value::String(s) => assert_eq!(s, "from child"),
3307 _ => panic!("Expected string in register"),
3308 }
3309 }
3310
3311 #[test]
3312 fn test_spawn_link_crash_notification() {
3313 let mut scheduler = Scheduler::new();
3314
3315 let child = vec![Instruction::Crash];
3317
3318 let parent = vec![
3320 Instruction::TrapExit { enable: true },
3321 Instruction::SpawnLink {
3322 code: child,
3323 dest: Register(0),
3324 },
3325 Instruction::Receive { dest: Register(1) },
3326 Instruction::End,
3327 ];
3328
3329 scheduler.spawn(parent);
3330 run_to_idle(&mut scheduler);
3331
3332 let parent_process = scheduler.processes.get(&Pid(0)).unwrap();
3334 match &parent_process.registers[1] {
3335 Value::Tuple(elems) => {
3336 assert_eq!(elems.len(), 3);
3337 assert_eq!(elems[0], Value::Atom("EXIT".to_string()));
3338 assert!(matches!(elems[1], Value::Pid(_)));
3339 assert_eq!(elems[2], Value::Atom("crashed".to_string()));
3340 }
3341 _ => panic!("Expected EXIT tuple, got {:?}", parent_process.registers[1]),
3342 }
3343
3344 let (_, _, done, crashed) = scheduler.process_count();
3345 assert_eq!(done, 1); assert_eq!(crashed, 1); }
3348
3349 #[test]
3350 fn test_monitor_down_notification() {
3351 let mut scheduler = Scheduler::new();
3352
3353 let worker = vec![Instruction::Work { amount: 1 }, Instruction::Crash];
3355
3356 let observer = vec![
3358 Instruction::Spawn {
3359 code: worker,
3360 dest: Register(0),
3361 },
3362 Instruction::Monitor {
3363 target: Source::Reg(Register(0)),
3364 dest: Register(2),
3365 },
3366 Instruction::Receive { dest: Register(1) },
3367 Instruction::End,
3368 ];
3369
3370 scheduler.spawn(observer);
3371 run_to_idle(&mut scheduler);
3372
3373 let observer_process = scheduler.processes.get(&Pid(0)).unwrap();
3375 match &observer_process.registers[1] {
3376 Value::Tuple(elems) => {
3377 assert_eq!(elems.len(), 5);
3378 assert_eq!(elems[0], Value::Atom("DOWN".to_string()));
3379 assert!(matches!(elems[1], Value::Ref(_)));
3380 assert_eq!(elems[2], Value::Atom("process".to_string()));
3381 assert!(matches!(elems[3], Value::Pid(_)));
3382 assert_eq!(elems[4], Value::Atom("crashed".to_string()));
3383 }
3384 _ => panic!(
3385 "Expected DOWN tuple, got {:?}",
3386 observer_process.registers[1]
3387 ),
3388 }
3389 }
3390
3391 #[test]
3392 fn test_receive_timeout() {
3393 let mut scheduler = Scheduler::new();
3394
3395 let waiter = vec![
3397 Instruction::ReceiveTimeout {
3398 dest: Register(0),
3399 timeout: 3,
3400 },
3401 Instruction::End,
3402 ];
3403
3404 scheduler.spawn(waiter);
3405
3406 for _ in 0..10 {
3408 if scheduler.step(1) == StepResult::Idle {
3409 break;
3410 }
3411 }
3412
3413 let process = scheduler.processes.get(&Pid(0)).unwrap();
3415 match &process.registers[0] {
3416 Value::String(s) => assert_eq!(s, "TIMEOUT"),
3417 _ => panic!("Expected TIMEOUT string"),
3418 }
3419 }
3420
3421 #[test]
3422 fn test_process_registry() {
3423 let mut scheduler = Scheduler::new();
3424
3425 let server = vec![
3427 Instruction::Register {
3428 name: "myserver".into(),
3429 },
3430 Instruction::Send {
3431 to: Source::Parent,
3432 msg: "ready".into(),
3433 },
3434 Instruction::Receive { dest: Register(0) },
3435 Instruction::End,
3436 ];
3437
3438 let client = vec![
3440 Instruction::Spawn {
3441 code: server,
3442 dest: Register(0),
3443 },
3444 Instruction::Receive { dest: Register(1) }, Instruction::Send {
3446 to: Source::Named("myserver".into()),
3447 msg: "ping".into(),
3448 },
3449 Instruction::End,
3450 ];
3451
3452 scheduler.spawn(client);
3453 run_to_idle(&mut scheduler);
3454
3455 let (_, _, done, _) = scheduler.process_count();
3457 assert_eq!(done, 2);
3458
3459 let server_process = scheduler.processes.get(&Pid(1)).unwrap();
3461 match &server_process.registers[0] {
3462 Value::String(s) => assert_eq!(s, "ping"),
3463 _ => panic!("Expected ping message"),
3464 }
3465 }
3466
3467 #[test]
3468 fn test_whereis() {
3469 let mut scheduler = Scheduler::new();
3470
3471 let program = vec![
3472 Instruction::Register {
3473 name: "test".into(),
3474 },
3475 Instruction::WhereIs {
3476 name: "test".into(),
3477 dest: Register(0),
3478 },
3479 Instruction::WhereIs {
3480 name: "nonexistent".into(),
3481 dest: Register(1),
3482 },
3483 Instruction::End,
3484 ];
3485
3486 scheduler.spawn(program);
3487 run_to_idle(&mut scheduler);
3488
3489 let process = scheduler.processes.get(&Pid(0)).unwrap();
3490
3491 match &process.registers[0] {
3493 Value::Pid(p) => assert_eq!(p.0, 0),
3494 _ => panic!("Expected Pid in register 0"),
3495 }
3496
3497 match &process.registers[1] {
3499 Value::None => {}
3500 _ => panic!("Expected None in register 1"),
3501 }
3502 }
3503
3504 #[test]
3505 fn test_registry_cleanup_on_exit() {
3506 let mut scheduler = Scheduler::new();
3507
3508 let program = vec![
3509 Instruction::Register {
3510 name: "temp".into(),
3511 },
3512 Instruction::End,
3513 ];
3514
3515 scheduler.spawn(program);
3516
3517 scheduler.step(1);
3519 assert!(scheduler.registry.contains_key("temp"));
3520
3521 run_to_idle(&mut scheduler);
3523 assert!(!scheduler.registry.contains_key("temp"));
3524 }
3525
3526 #[test]
3527 fn test_spawn_many_processes() {
3528 let mut scheduler = Scheduler::new();
3529
3530 let worker = vec![Instruction::End];
3531
3532 let mut spawner = Vec::new();
3534 for _ in 0..50 {
3535 spawner.push(Instruction::Spawn {
3536 code: worker.clone(),
3537 dest: Register(0),
3538 });
3539 }
3540 spawner.push(Instruction::End);
3541
3542 scheduler.spawn(spawner);
3543 run_to_idle(&mut scheduler);
3544
3545 assert_eq!(scheduler.processes.len(), 51);
3547
3548 let (_, _, done, _) = scheduler.process_count();
3549 assert_eq!(done, 51);
3550 }
3551
3552 #[test]
3553 fn test_print_captures_output() {
3554 let mut scheduler = Scheduler::new();
3555
3556 let program = vec![
3557 Instruction::Print {
3558 source: Source::Self_,
3559 },
3560 Instruction::End,
3561 ];
3562
3563 scheduler.spawn(program);
3564 run_to_idle(&mut scheduler);
3565
3566 let output = scheduler.take_output();
3567 assert_eq!(output.len(), 1);
3568 assert!(output[0].contains("Pid(0)"));
3569 }
3570
3571 #[test]
3574 fn test_load_int() {
3575 let mut scheduler = Scheduler::new();
3576
3577 let program = vec![
3578 Instruction::LoadInt {
3579 value: 42,
3580 dest: Register(0),
3581 },
3582 Instruction::End,
3583 ];
3584
3585 scheduler.spawn(program);
3586 run_to_idle(&mut scheduler);
3587
3588 let process = scheduler.processes.get(&Pid(0)).unwrap();
3589 assert_eq!(process.registers[0], Value::Int(42));
3590 }
3591
3592 #[test]
3593 fn test_add() {
3594 let mut scheduler = Scheduler::new();
3595
3596 let program = vec![
3597 Instruction::LoadInt {
3598 value: 10,
3599 dest: Register(0),
3600 },
3601 Instruction::Add {
3602 a: Operand::Reg(Register(0)),
3603 b: Operand::Int(5),
3604 dest: Register(1),
3605 },
3606 Instruction::End,
3607 ];
3608
3609 scheduler.spawn(program);
3610 run_to_idle(&mut scheduler);
3611
3612 let process = scheduler.processes.get(&Pid(0)).unwrap();
3613 assert_eq!(process.registers[1], Value::Int(15));
3614 }
3615
3616 #[test]
3617 fn test_sub() {
3618 let mut scheduler = Scheduler::new();
3619
3620 let program = vec![
3621 Instruction::Sub {
3622 a: Operand::Int(20),
3623 b: Operand::Int(7),
3624 dest: Register(0),
3625 },
3626 Instruction::End,
3627 ];
3628
3629 scheduler.spawn(program);
3630 run_to_idle(&mut scheduler);
3631
3632 let process = scheduler.processes.get(&Pid(0)).unwrap();
3633 assert_eq!(process.registers[0], Value::Int(13));
3634 }
3635
3636 #[test]
3637 fn test_mul() {
3638 let mut scheduler = Scheduler::new();
3639
3640 let program = vec![
3641 Instruction::LoadInt {
3642 value: 6,
3643 dest: Register(0),
3644 },
3645 Instruction::LoadInt {
3646 value: 7,
3647 dest: Register(1),
3648 },
3649 Instruction::Mul {
3650 a: Operand::Reg(Register(0)),
3651 b: Operand::Reg(Register(1)),
3652 dest: Register(2),
3653 },
3654 Instruction::End,
3655 ];
3656
3657 scheduler.spawn(program);
3658 run_to_idle(&mut scheduler);
3659
3660 let process = scheduler.processes.get(&Pid(0)).unwrap();
3661 assert_eq!(process.registers[2], Value::Int(42));
3662 }
3663
3664 #[test]
3665 fn test_div() {
3666 let mut scheduler = Scheduler::new();
3667
3668 let program = vec![
3669 Instruction::Div {
3670 a: Operand::Int(100),
3671 b: Operand::Int(7),
3672 dest: Register(0),
3673 },
3674 Instruction::End,
3675 ];
3676
3677 scheduler.spawn(program);
3678 run_to_idle(&mut scheduler);
3679
3680 let process = scheduler.processes.get(&Pid(0)).unwrap();
3681 assert_eq!(process.registers[0], Value::Int(14)); }
3683
3684 #[test]
3685 fn test_div_by_zero_crashes() {
3686 let mut scheduler = Scheduler::new();
3687
3688 let program = vec![
3689 Instruction::Div {
3690 a: Operand::Int(10),
3691 b: Operand::Int(0),
3692 dest: Register(0),
3693 },
3694 Instruction::End,
3695 ];
3696
3697 scheduler.spawn(program);
3698 run_to_idle(&mut scheduler);
3699
3700 let (_, _, _, crashed) = scheduler.process_count();
3701 assert_eq!(crashed, 1);
3702 }
3703
3704 #[test]
3705 fn test_mod() {
3706 let mut scheduler = Scheduler::new();
3707
3708 let program = vec![
3709 Instruction::Mod {
3710 a: Operand::Int(17),
3711 b: Operand::Int(5),
3712 dest: Register(0),
3713 },
3714 Instruction::End,
3715 ];
3716
3717 scheduler.spawn(program);
3718 run_to_idle(&mut scheduler);
3719
3720 let process = scheduler.processes.get(&Pid(0)).unwrap();
3721 assert_eq!(process.registers[0], Value::Int(2));
3722 }
3723
3724 #[test]
3727 fn test_eq() {
3728 let mut scheduler = Scheduler::new();
3729
3730 let program = vec![
3731 Instruction::Eq {
3732 a: Operand::Int(5),
3733 b: Operand::Int(5),
3734 dest: Register(0),
3735 },
3736 Instruction::Eq {
3737 a: Operand::Int(5),
3738 b: Operand::Int(3),
3739 dest: Register(1),
3740 },
3741 Instruction::End,
3742 ];
3743
3744 scheduler.spawn(program);
3745 run_to_idle(&mut scheduler);
3746
3747 let process = scheduler.processes.get(&Pid(0)).unwrap();
3748 assert_eq!(process.registers[0], Value::Int(1)); assert_eq!(process.registers[1], Value::Int(0)); }
3751
3752 #[test]
3753 fn test_lt_gt() {
3754 let mut scheduler = Scheduler::new();
3755
3756 let program = vec![
3757 Instruction::Lt {
3758 a: Operand::Int(3),
3759 b: Operand::Int(5),
3760 dest: Register(0),
3761 },
3762 Instruction::Gt {
3763 a: Operand::Int(3),
3764 b: Operand::Int(5),
3765 dest: Register(1),
3766 },
3767 Instruction::Lt {
3768 a: Operand::Int(5),
3769 b: Operand::Int(3),
3770 dest: Register(2),
3771 },
3772 Instruction::End,
3773 ];
3774
3775 scheduler.spawn(program);
3776 run_to_idle(&mut scheduler);
3777
3778 let process = scheduler.processes.get(&Pid(0)).unwrap();
3779 assert_eq!(process.registers[0], Value::Int(1)); assert_eq!(process.registers[1], Value::Int(0)); assert_eq!(process.registers[2], Value::Int(0)); }
3783
3784 #[test]
3785 fn test_lte_gte() {
3786 let mut scheduler = Scheduler::new();
3787
3788 let program = vec![
3789 Instruction::Lte {
3790 a: Operand::Int(5),
3791 b: Operand::Int(5),
3792 dest: Register(0),
3793 },
3794 Instruction::Gte {
3795 a: Operand::Int(5),
3796 b: Operand::Int(5),
3797 dest: Register(1),
3798 },
3799 Instruction::Lte {
3800 a: Operand::Int(3),
3801 b: Operand::Int(5),
3802 dest: Register(2),
3803 },
3804 Instruction::Gte {
3805 a: Operand::Int(3),
3806 b: Operand::Int(5),
3807 dest: Register(3),
3808 },
3809 Instruction::End,
3810 ];
3811
3812 scheduler.spawn(program);
3813 run_to_idle(&mut scheduler);
3814
3815 let process = scheduler.processes.get(&Pid(0)).unwrap();
3816 assert_eq!(process.registers[0], Value::Int(1)); assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[2], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
3821
3822 #[test]
3823 fn test_ne() {
3824 let mut scheduler = Scheduler::new();
3825
3826 let program = vec![
3827 Instruction::Ne {
3828 a: Operand::Int(5),
3829 b: Operand::Int(3),
3830 dest: Register(0),
3831 },
3832 Instruction::Ne {
3833 a: Operand::Int(5),
3834 b: Operand::Int(5),
3835 dest: Register(1),
3836 },
3837 Instruction::End,
3838 ];
3839
3840 scheduler.spawn(program);
3841 run_to_idle(&mut scheduler);
3842
3843 let process = scheduler.processes.get(&Pid(0)).unwrap();
3844 assert_eq!(process.registers[0], Value::Int(1)); assert_eq!(process.registers[1], Value::Int(0)); }
3847
3848 #[test]
3849 fn test_arithmetic_chain() {
3850 let mut scheduler = Scheduler::new();
3851
3852 let program = vec![
3854 Instruction::Add {
3855 a: Operand::Int(10),
3856 b: Operand::Int(5),
3857 dest: Register(0),
3858 },
3859 Instruction::Mul {
3860 a: Operand::Reg(Register(0)),
3861 b: Operand::Int(2),
3862 dest: Register(0),
3863 },
3864 Instruction::Sub {
3865 a: Operand::Reg(Register(0)),
3866 b: Operand::Int(3),
3867 dest: Register(0),
3868 },
3869 Instruction::End,
3870 ];
3871
3872 scheduler.spawn(program);
3873 run_to_idle(&mut scheduler);
3874
3875 let process = scheduler.processes.get(&Pid(0)).unwrap();
3876 assert_eq!(process.registers[0], Value::Int(27));
3877 }
3878
3879 #[test]
3882 fn test_jump() {
3883 let mut scheduler = Scheduler::new();
3884
3885 let program = vec![
3887 Instruction::Jump { target: 2 },
3889 Instruction::LoadInt {
3891 value: 999,
3892 dest: Register(0),
3893 },
3894 Instruction::LoadInt {
3896 value: 42,
3897 dest: Register(0),
3898 },
3899 Instruction::End,
3900 ];
3901
3902 scheduler.spawn(program);
3903 run_to_idle(&mut scheduler);
3904
3905 let process = scheduler.processes.get(&Pid(0)).unwrap();
3906 assert_eq!(process.registers[0], Value::Int(42));
3907 }
3908
3909 #[test]
3910 fn test_jump_if_truthy() {
3911 let mut scheduler = Scheduler::new();
3912
3913 let program = vec![
3915 Instruction::LoadInt {
3917 value: 1,
3918 dest: Register(0),
3919 },
3920 Instruction::JumpIf {
3922 cond: Operand::Reg(Register(0)),
3923 target: 3,
3924 },
3925 Instruction::LoadInt {
3927 value: 999,
3928 dest: Register(1),
3929 },
3930 Instruction::LoadInt {
3932 value: 42,
3933 dest: Register(1),
3934 },
3935 Instruction::End,
3936 ];
3937
3938 scheduler.spawn(program);
3939 run_to_idle(&mut scheduler);
3940
3941 let process = scheduler.processes.get(&Pid(0)).unwrap();
3942 assert_eq!(process.registers[1], Value::Int(42));
3943 }
3944
3945 #[test]
3946 fn test_jump_if_falsy() {
3947 let mut scheduler = Scheduler::new();
3948
3949 let program = vec![
3951 Instruction::LoadInt {
3953 value: 0,
3954 dest: Register(0),
3955 },
3956 Instruction::JumpIf {
3958 cond: Operand::Reg(Register(0)),
3959 target: 3,
3960 },
3961 Instruction::LoadInt {
3963 value: 42,
3964 dest: Register(1),
3965 },
3966 Instruction::End,
3968 ];
3969
3970 scheduler.spawn(program);
3971 run_to_idle(&mut scheduler);
3972
3973 let process = scheduler.processes.get(&Pid(0)).unwrap();
3974 assert_eq!(process.registers[1], Value::Int(42));
3975 }
3976
3977 #[test]
3978 fn test_jump_unless_falsy() {
3979 let mut scheduler = Scheduler::new();
3980
3981 let program = vec![
3983 Instruction::LoadInt {
3985 value: 0,
3986 dest: Register(0),
3987 },
3988 Instruction::JumpUnless {
3990 cond: Operand::Reg(Register(0)),
3991 target: 3,
3992 },
3993 Instruction::LoadInt {
3995 value: 999,
3996 dest: Register(1),
3997 },
3998 Instruction::LoadInt {
4000 value: 42,
4001 dest: Register(1),
4002 },
4003 Instruction::End,
4004 ];
4005
4006 scheduler.spawn(program);
4007 run_to_idle(&mut scheduler);
4008
4009 let process = scheduler.processes.get(&Pid(0)).unwrap();
4010 assert_eq!(process.registers[1], Value::Int(42));
4011 }
4012
4013 #[test]
4014 fn test_simple_loop() {
4015 let mut scheduler = Scheduler::new();
4016
4017 let program = vec![
4020 Instruction::LoadInt {
4022 value: 5,
4023 dest: Register(0),
4024 },
4025 Instruction::LoadInt {
4027 value: 0,
4028 dest: Register(1),
4029 },
4030 Instruction::Add {
4032 a: Operand::Reg(Register(1)),
4033 b: Operand::Reg(Register(0)),
4034 dest: Register(1),
4035 },
4036 Instruction::Sub {
4038 a: Operand::Reg(Register(0)),
4039 b: Operand::Int(1),
4040 dest: Register(0),
4041 },
4042 Instruction::JumpIf {
4044 cond: Operand::Reg(Register(0)),
4045 target: 2,
4046 },
4047 Instruction::End,
4049 ];
4050
4051 scheduler.spawn(program);
4052 run_to_idle(&mut scheduler);
4053
4054 let process = scheduler.processes.get(&Pid(0)).unwrap();
4055 assert_eq!(process.registers[1], Value::Int(15));
4057 }
4058
4059 #[test]
4060 fn test_conditional_with_comparison() {
4061 let mut scheduler = Scheduler::new();
4062
4063 let program = vec![
4065 Instruction::Gt {
4067 a: Operand::Int(10),
4068 b: Operand::Int(5),
4069 dest: Register(0),
4070 },
4071 Instruction::JumpIf {
4073 cond: Operand::Reg(Register(0)),
4074 target: 3,
4075 },
4076 Instruction::LoadInt {
4078 value: 0,
4079 dest: Register(1),
4080 },
4081 Instruction::Jump { target: 4 },
4082 Instruction::LoadInt {
4084 value: 1,
4085 dest: Register(1),
4086 },
4087 Instruction::End,
4089 ];
4090
4091 scheduler.spawn(program);
4092 run_to_idle(&mut scheduler);
4093
4094 let process = scheduler.processes.get(&Pid(0)).unwrap();
4095 assert_eq!(process.registers[1], Value::Int(1)); }
4097
4098 #[test]
4101 fn test_simple_call_return() {
4102 let mut scheduler = Scheduler::new();
4103
4104 let program = vec![
4106 Instruction::Call { target: 3 },
4108 Instruction::End,
4110 Instruction::LoadInt {
4112 value: 999,
4113 dest: Register(0),
4114 },
4115 Instruction::LoadInt {
4117 value: 42,
4118 dest: Register(0),
4119 },
4120 Instruction::Return,
4122 ];
4123
4124 scheduler.spawn(program);
4125 run_to_idle(&mut scheduler);
4126
4127 let process = scheduler.processes.get(&Pid(0)).unwrap();
4128 assert_eq!(process.registers[0], Value::Int(42));
4129 }
4130
4131 #[test]
4132 fn test_nested_calls() {
4133 let mut scheduler = Scheduler::new();
4134
4135 let program = vec![
4137 Instruction::Call { target: 3 },
4139 Instruction::End,
4141 Instruction::End,
4143 Instruction::Call { target: 6 },
4145 Instruction::Add {
4147 a: Operand::Reg(Register(0)),
4148 b: Operand::Int(10),
4149 dest: Register(0),
4150 },
4151 Instruction::Return,
4153 Instruction::LoadInt {
4155 value: 100,
4156 dest: Register(0),
4157 },
4158 Instruction::Return,
4160 ];
4161
4162 scheduler.spawn(program);
4163 run_to_idle(&mut scheduler);
4164
4165 let process = scheduler.processes.get(&Pid(0)).unwrap();
4166 assert_eq!(process.registers[0], Value::Int(110));
4168 }
4169
4170 #[test]
4171 fn test_function_with_loop() {
4172 let mut scheduler = Scheduler::new();
4173
4174 let program = vec![
4177 Instruction::LoadInt {
4179 value: 5,
4180 dest: Register(0),
4181 },
4182 Instruction::Call { target: 4 },
4184 Instruction::End,
4186 Instruction::End,
4188 Instruction::LoadInt {
4192 value: 1,
4193 dest: Register(1),
4194 },
4195 Instruction::Lte {
4197 a: Operand::Reg(Register(0)),
4198 b: Operand::Int(0),
4199 dest: Register(2),
4200 },
4201 Instruction::JumpIf {
4203 cond: Operand::Reg(Register(2)),
4204 target: 10,
4205 },
4206 Instruction::Mul {
4208 a: Operand::Reg(Register(1)),
4209 b: Operand::Reg(Register(0)),
4210 dest: Register(1),
4211 },
4212 Instruction::Sub {
4214 a: Operand::Reg(Register(0)),
4215 b: Operand::Int(1),
4216 dest: Register(0),
4217 },
4218 Instruction::Jump { target: 5 },
4220 Instruction::Add {
4222 a: Operand::Reg(Register(1)),
4223 b: Operand::Int(0),
4224 dest: Register(0),
4225 },
4226 Instruction::Return,
4228 ];
4229
4230 scheduler.spawn(program);
4231 run_to_idle(&mut scheduler);
4232
4233 let process = scheduler.processes.get(&Pid(0)).unwrap();
4234 assert_eq!(process.registers[0], Value::Int(120)); }
4236
4237 #[test]
4238 fn test_return_without_call_ends_process() {
4239 let mut scheduler = Scheduler::new();
4240
4241 let program = vec![
4243 Instruction::LoadInt {
4244 value: 42,
4245 dest: Register(0),
4246 },
4247 Instruction::Return,
4248 Instruction::LoadInt {
4250 value: 999,
4251 dest: Register(0),
4252 },
4253 Instruction::End,
4254 ];
4255
4256 scheduler.spawn(program);
4257 run_to_idle(&mut scheduler);
4258
4259 let process = scheduler.processes.get(&Pid(0)).unwrap();
4260 assert_eq!(process.registers[0], Value::Int(42));
4261 assert_eq!(process.status, ProcessStatus::Done);
4262 }
4263
4264 #[test]
4267 fn test_push_pop() {
4268 let mut scheduler = Scheduler::new();
4269
4270 let program = vec![
4271 Instruction::Push {
4273 source: Operand::Int(10),
4274 },
4275 Instruction::Push {
4276 source: Operand::Int(20),
4277 },
4278 Instruction::Push {
4279 source: Operand::Int(30),
4280 },
4281 Instruction::Pop { dest: Register(0) }, Instruction::Pop { dest: Register(1) }, Instruction::Pop { dest: Register(2) }, Instruction::End,
4286 ];
4287
4288 scheduler.spawn(program);
4289 run_to_idle(&mut scheduler);
4290
4291 let process = scheduler.processes.get(&Pid(0)).unwrap();
4292 assert_eq!(process.registers[0], Value::Int(30));
4293 assert_eq!(process.registers[1], Value::Int(20));
4294 assert_eq!(process.registers[2], Value::Int(10));
4295 }
4296
4297 #[test]
4298 fn test_push_register() {
4299 let mut scheduler = Scheduler::new();
4300
4301 let program = vec![
4302 Instruction::LoadInt {
4303 value: 42,
4304 dest: Register(0),
4305 },
4306 Instruction::Push {
4307 source: Operand::Reg(Register(0)),
4308 },
4309 Instruction::LoadInt {
4310 value: 0,
4311 dest: Register(0),
4312 },
4313 Instruction::Pop { dest: Register(1) },
4314 Instruction::End,
4315 ];
4316
4317 scheduler.spawn(program);
4318 run_to_idle(&mut scheduler);
4319
4320 let process = scheduler.processes.get(&Pid(0)).unwrap();
4321 assert_eq!(process.registers[0], Value::Int(0)); assert_eq!(process.registers[1], Value::Int(42)); }
4324
4325 #[test]
4326 fn test_pop_empty_crashes() {
4327 let mut scheduler = Scheduler::new();
4328
4329 let program = vec![Instruction::Pop { dest: Register(0) }, Instruction::End];
4330
4331 scheduler.spawn(program);
4332 run_to_idle(&mut scheduler);
4333
4334 let (_, _, _, crashed) = scheduler.process_count();
4335 assert_eq!(crashed, 1);
4336 }
4337
4338 #[test]
4339 fn test_recursive_factorial_with_stack() {
4340 let mut scheduler = Scheduler::new();
4341
4342 let program = vec![
4345 Instruction::LoadInt {
4347 value: 5,
4348 dest: Register(0),
4349 },
4350 Instruction::Call { target: 4 },
4352 Instruction::End,
4354 Instruction::End,
4356 Instruction::Lte {
4359 a: Operand::Reg(Register(0)),
4360 b: Operand::Int(1),
4361 dest: Register(1),
4362 },
4363 Instruction::JumpIf {
4365 cond: Operand::Reg(Register(1)),
4366 target: 13,
4367 },
4368 Instruction::Push {
4370 source: Operand::Reg(Register(0)),
4371 },
4372 Instruction::Sub {
4374 a: Operand::Reg(Register(0)),
4375 b: Operand::Int(1),
4376 dest: Register(0),
4377 },
4378 Instruction::Call { target: 4 },
4380 Instruction::Pop { dest: Register(1) },
4382 Instruction::Mul {
4384 a: Operand::Reg(Register(0)),
4385 b: Operand::Reg(Register(1)),
4386 dest: Register(0),
4387 },
4388 Instruction::Return,
4390 Instruction::End,
4392 Instruction::Return,
4394 ];
4395
4396 scheduler.spawn(program);
4397 run_to_idle(&mut scheduler);
4398
4399 let process = scheduler.processes.get(&Pid(0)).unwrap();
4400 assert_eq!(process.registers[0], Value::Int(120)); }
4402
4403 #[test]
4404 fn test_recursive_fibonacci() {
4405 let mut scheduler = Scheduler::new();
4406
4407 let program = vec![
4410 Instruction::LoadInt {
4412 value: 10,
4413 dest: Register(0),
4414 },
4415 Instruction::Call { target: 4 },
4417 Instruction::End,
4419 Instruction::End,
4421 Instruction::Lte {
4424 a: Operand::Reg(Register(0)),
4425 b: Operand::Int(1),
4426 dest: Register(1),
4427 },
4428 Instruction::JumpIf {
4430 cond: Operand::Reg(Register(1)),
4431 target: 16,
4432 },
4433 Instruction::Push {
4435 source: Operand::Reg(Register(0)),
4436 },
4437 Instruction::Sub {
4439 a: Operand::Reg(Register(0)),
4440 b: Operand::Int(1),
4441 dest: Register(0),
4442 },
4443 Instruction::Call { target: 4 },
4445 Instruction::Add {
4447 a: Operand::Reg(Register(0)),
4448 b: Operand::Int(0),
4449 dest: Register(2),
4450 },
4451 Instruction::Pop { dest: Register(0) },
4453 Instruction::Push {
4455 source: Operand::Reg(Register(2)),
4456 },
4457 Instruction::Sub {
4459 a: Operand::Reg(Register(0)),
4460 b: Operand::Int(2),
4461 dest: Register(0),
4462 },
4463 Instruction::Call { target: 4 },
4465 Instruction::Pop { dest: Register(1) },
4467 Instruction::Add {
4469 a: Operand::Reg(Register(0)),
4470 b: Operand::Reg(Register(1)),
4471 dest: Register(0),
4472 },
4473 Instruction::Return,
4475 ];
4476
4477 scheduler.spawn(program);
4478 run_to_idle(&mut scheduler);
4479
4480 let process = scheduler.processes.get(&Pid(0)).unwrap();
4481 assert_eq!(process.registers[0], Value::Int(55)); }
4483
4484 #[test]
4487 fn test_load_atom() {
4488 let mut scheduler = Scheduler::new();
4489
4490 let program = vec![
4491 Instruction::LoadAtom {
4492 name: "ok".to_string(),
4493 dest: Register(0),
4494 },
4495 Instruction::LoadAtom {
4496 name: "error".to_string(),
4497 dest: Register(1),
4498 },
4499 Instruction::End,
4500 ];
4501
4502 scheduler.spawn(program);
4503 run_to_idle(&mut scheduler);
4504
4505 let process = scheduler.processes.get(&Pid(0)).unwrap();
4506 assert_eq!(process.registers[0], Value::Atom("ok".to_string()));
4507 assert_eq!(process.registers[1], Value::Atom("error".to_string()));
4508 }
4509
4510 #[test]
4511 fn test_make_tuple() {
4512 let mut scheduler = Scheduler::new();
4513
4514 let program = vec![
4516 Instruction::LoadAtom {
4517 name: "ok".to_string(),
4518 dest: Register(0),
4519 },
4520 Instruction::LoadInt {
4521 value: 42,
4522 dest: Register(1),
4523 },
4524 Instruction::Push {
4526 source: Operand::Reg(Register(0)),
4527 },
4528 Instruction::Push {
4529 source: Operand::Reg(Register(1)),
4530 },
4531 Instruction::MakeTuple {
4533 arity: 2,
4534 dest: Register(2),
4535 },
4536 Instruction::End,
4537 ];
4538
4539 scheduler.spawn(program);
4540 run_to_idle(&mut scheduler);
4541
4542 let process = scheduler.processes.get(&Pid(0)).unwrap();
4543 assert_eq!(
4544 process.registers[2],
4545 Value::Tuple(vec![Value::Atom("ok".to_string()), Value::Int(42),])
4546 );
4547 }
4548
4549 #[test]
4550 fn test_tuple_element() {
4551 let mut scheduler = Scheduler::new();
4552
4553 let program = vec![
4555 Instruction::LoadAtom {
4556 name: "error".to_string(),
4557 dest: Register(0),
4558 },
4559 Instruction::LoadInt {
4560 value: 404,
4561 dest: Register(1),
4562 },
4563 Instruction::Push {
4564 source: Operand::Reg(Register(0)),
4565 },
4566 Instruction::Push {
4567 source: Operand::Reg(Register(1)),
4568 },
4569 Instruction::MakeTuple {
4570 arity: 2,
4571 dest: Register(0),
4572 },
4573 Instruction::TupleElement {
4575 tuple: Register(0),
4576 index: 0,
4577 dest: Register(1),
4578 },
4579 Instruction::TupleElement {
4581 tuple: Register(0),
4582 index: 1,
4583 dest: Register(2),
4584 },
4585 Instruction::End,
4586 ];
4587
4588 scheduler.spawn(program);
4589 run_to_idle(&mut scheduler);
4590
4591 let process = scheduler.processes.get(&Pid(0)).unwrap();
4592 assert_eq!(process.registers[1], Value::Atom("error".to_string()));
4593 assert_eq!(process.registers[2], Value::Int(404));
4594 }
4595
4596 #[test]
4597 fn test_tuple_arity() {
4598 let mut scheduler = Scheduler::new();
4599
4600 let program = vec![
4601 Instruction::Push {
4603 source: Operand::Int(1),
4604 },
4605 Instruction::Push {
4606 source: Operand::Int(2),
4607 },
4608 Instruction::Push {
4609 source: Operand::Int(3),
4610 },
4611 Instruction::MakeTuple {
4612 arity: 3,
4613 dest: Register(0),
4614 },
4615 Instruction::TupleArity {
4616 tuple: Register(0),
4617 dest: Register(1),
4618 },
4619 Instruction::End,
4620 ];
4621
4622 scheduler.spawn(program);
4623 run_to_idle(&mut scheduler);
4624
4625 let process = scheduler.processes.get(&Pid(0)).unwrap();
4626 assert_eq!(process.registers[1], Value::Int(3));
4627 }
4628
4629 #[test]
4630 fn test_tuple_element_out_of_bounds_crashes() {
4631 let mut scheduler = Scheduler::new();
4632
4633 let program = vec![
4634 Instruction::Push {
4635 source: Operand::Int(1),
4636 },
4637 Instruction::MakeTuple {
4638 arity: 1,
4639 dest: Register(0),
4640 },
4641 Instruction::TupleElement {
4643 tuple: Register(0),
4644 index: 5,
4645 dest: Register(1),
4646 },
4647 Instruction::End,
4648 ];
4649
4650 scheduler.spawn(program);
4651 run_to_idle(&mut scheduler);
4652
4653 let (_, _, _, crashed) = scheduler.process_count();
4654 assert_eq!(crashed, 1);
4655 }
4656
4657 #[test]
4658 fn test_nested_tuples() {
4659 let mut scheduler = Scheduler::new();
4660
4661 let program = vec![
4663 Instruction::LoadAtom {
4665 name: "inner".to_string(),
4666 dest: Register(0),
4667 },
4668 Instruction::Push {
4669 source: Operand::Reg(Register(0)),
4670 },
4671 Instruction::Push {
4672 source: Operand::Int(1),
4673 },
4674 Instruction::MakeTuple {
4675 arity: 2,
4676 dest: Register(0),
4677 },
4678 Instruction::LoadAtom {
4680 name: "outer".to_string(),
4681 dest: Register(1),
4682 },
4683 Instruction::Push {
4684 source: Operand::Reg(Register(0)),
4685 },
4686 Instruction::Push {
4687 source: Operand::Reg(Register(1)),
4688 },
4689 Instruction::MakeTuple {
4690 arity: 2,
4691 dest: Register(2),
4692 },
4693 Instruction::TupleElement {
4695 tuple: Register(2),
4696 index: 0,
4697 dest: Register(3),
4698 },
4699 Instruction::TupleElement {
4701 tuple: Register(3),
4702 index: 1,
4703 dest: Register(4),
4704 },
4705 Instruction::End,
4706 ];
4707
4708 scheduler.spawn(program);
4709 run_to_idle(&mut scheduler);
4710
4711 let process = scheduler.processes.get(&Pid(0)).unwrap();
4712 assert_eq!(process.registers[4], Value::Int(1));
4714 }
4715
4716 #[test]
4719 fn test_match_wildcard() {
4720 let mut scheduler = Scheduler::new();
4721
4722 let program = vec![
4723 Instruction::LoadInt {
4724 value: 42,
4725 dest: Register(0),
4726 },
4727 Instruction::Match {
4729 source: Register(0),
4730 pattern: Pattern::Wildcard,
4731 fail_target: 4,
4732 },
4733 Instruction::LoadInt {
4735 value: 1,
4736 dest: Register(1),
4737 },
4738 Instruction::End,
4739 Instruction::LoadInt {
4741 value: 0,
4742 dest: Register(1),
4743 },
4744 Instruction::End,
4745 ];
4746
4747 scheduler.spawn(program);
4748 run_to_idle(&mut scheduler);
4749
4750 let process = scheduler.processes.get(&Pid(0)).unwrap();
4751 assert_eq!(process.registers[1], Value::Int(1)); }
4753
4754 #[test]
4755 fn test_match_variable() {
4756 let mut scheduler = Scheduler::new();
4757
4758 let program = vec![
4759 Instruction::LoadInt {
4760 value: 42,
4761 dest: Register(0),
4762 },
4763 Instruction::Match {
4765 source: Register(0),
4766 pattern: Pattern::Variable(Register(1)),
4767 fail_target: 3,
4768 },
4769 Instruction::End,
4770 Instruction::LoadInt {
4772 value: 0,
4773 dest: Register(1),
4774 },
4775 Instruction::End,
4776 ];
4777
4778 scheduler.spawn(program);
4779 run_to_idle(&mut scheduler);
4780
4781 let process = scheduler.processes.get(&Pid(0)).unwrap();
4782 assert_eq!(process.registers[1], Value::Int(42)); }
4784
4785 #[test]
4786 fn test_match_int_success() {
4787 let mut scheduler = Scheduler::new();
4788
4789 let program = vec![
4790 Instruction::LoadInt {
4791 value: 42,
4792 dest: Register(0),
4793 },
4794 Instruction::Match {
4796 source: Register(0),
4797 pattern: Pattern::Int(42),
4798 fail_target: 4,
4799 },
4800 Instruction::LoadInt {
4801 value: 1,
4802 dest: Register(1),
4803 },
4804 Instruction::End,
4805 Instruction::LoadInt {
4807 value: 0,
4808 dest: Register(1),
4809 },
4810 Instruction::End,
4811 ];
4812
4813 scheduler.spawn(program);
4814 run_to_idle(&mut scheduler);
4815
4816 let process = scheduler.processes.get(&Pid(0)).unwrap();
4817 assert_eq!(process.registers[1], Value::Int(1)); }
4819
4820 #[test]
4821 fn test_match_int_failure() {
4822 let mut scheduler = Scheduler::new();
4823
4824 let program = vec![
4825 Instruction::LoadInt {
4826 value: 42,
4827 dest: Register(0),
4828 },
4829 Instruction::Match {
4831 source: Register(0),
4832 pattern: Pattern::Int(99),
4833 fail_target: 4,
4834 },
4835 Instruction::LoadInt {
4836 value: 1,
4837 dest: Register(1),
4838 },
4839 Instruction::End,
4840 Instruction::LoadInt {
4842 value: 0,
4843 dest: Register(1),
4844 },
4845 Instruction::End,
4846 ];
4847
4848 scheduler.spawn(program);
4849 run_to_idle(&mut scheduler);
4850
4851 let process = scheduler.processes.get(&Pid(0)).unwrap();
4852 assert_eq!(process.registers[1], Value::Int(0)); }
4854
4855 #[test]
4856 fn test_match_atom() {
4857 let mut scheduler = Scheduler::new();
4858
4859 let program = vec![
4860 Instruction::LoadAtom {
4861 name: "ok".to_string(),
4862 dest: Register(0),
4863 },
4864 Instruction::Match {
4866 source: Register(0),
4867 pattern: Pattern::Atom("ok".to_string()),
4868 fail_target: 4,
4869 },
4870 Instruction::LoadInt {
4871 value: 1,
4872 dest: Register(1),
4873 },
4874 Instruction::End,
4875 Instruction::LoadInt {
4877 value: 0,
4878 dest: Register(1),
4879 },
4880 Instruction::End,
4881 ];
4882
4883 scheduler.spawn(program);
4884 run_to_idle(&mut scheduler);
4885
4886 let process = scheduler.processes.get(&Pid(0)).unwrap();
4887 assert_eq!(process.registers[1], Value::Int(1)); }
4889
4890 #[test]
4891 fn test_match_tuple_with_bindings() {
4892 let mut scheduler = Scheduler::new();
4893
4894 let program = vec![
4896 Instruction::LoadAtom {
4898 name: "ok".to_string(),
4899 dest: Register(0),
4900 },
4901 Instruction::Push {
4902 source: Operand::Reg(Register(0)),
4903 },
4904 Instruction::Push {
4905 source: Operand::Int(42),
4906 },
4907 Instruction::MakeTuple {
4908 arity: 2,
4909 dest: Register(0),
4910 },
4911 Instruction::Match {
4913 source: Register(0),
4914 pattern: Pattern::Tuple(vec![
4915 Pattern::Atom("ok".to_string()),
4916 Pattern::Variable(Register(1)),
4917 ]),
4918 fail_target: 7,
4919 },
4920 Instruction::LoadInt {
4922 value: 1,
4923 dest: Register(2),
4924 },
4925 Instruction::End,
4926 Instruction::LoadInt {
4928 value: 0,
4929 dest: Register(2),
4930 },
4931 Instruction::End,
4932 ];
4933
4934 scheduler.spawn(program);
4935 run_to_idle(&mut scheduler);
4936
4937 let process = scheduler.processes.get(&Pid(0)).unwrap();
4938 assert_eq!(process.registers[1], Value::Int(42)); assert_eq!(process.registers[2], Value::Int(1)); }
4941
4942 #[test]
4943 fn test_match_tuple_wrong_atom() {
4944 let mut scheduler = Scheduler::new();
4945
4946 let program = vec![
4948 Instruction::LoadAtom {
4949 name: "error".to_string(),
4950 dest: Register(0),
4951 },
4952 Instruction::Push {
4953 source: Operand::Reg(Register(0)),
4954 },
4955 Instruction::Push {
4956 source: Operand::Int(42),
4957 },
4958 Instruction::MakeTuple {
4959 arity: 2,
4960 dest: Register(0),
4961 },
4962 Instruction::Match {
4964 source: Register(0),
4965 pattern: Pattern::Tuple(vec![
4966 Pattern::Atom("ok".to_string()),
4967 Pattern::Variable(Register(1)),
4968 ]),
4969 fail_target: 7,
4970 },
4971 Instruction::LoadInt {
4972 value: 1,
4973 dest: Register(2),
4974 },
4975 Instruction::End,
4976 Instruction::LoadInt {
4978 value: 0,
4979 dest: Register(2),
4980 },
4981 Instruction::End,
4982 ];
4983
4984 scheduler.spawn(program);
4985 run_to_idle(&mut scheduler);
4986
4987 let process = scheduler.processes.get(&Pid(0)).unwrap();
4988 assert_eq!(process.registers[2], Value::Int(0)); }
4990
4991 #[test]
4992 fn test_match_tuple_wrong_arity() {
4993 let mut scheduler = Scheduler::new();
4994
4995 let program = vec![
4997 Instruction::Push {
4998 source: Operand::Int(1),
4999 },
5000 Instruction::Push {
5001 source: Operand::Int(2),
5002 },
5003 Instruction::Push {
5004 source: Operand::Int(3),
5005 },
5006 Instruction::MakeTuple {
5007 arity: 3,
5008 dest: Register(0),
5009 },
5010 Instruction::Match {
5012 source: Register(0),
5013 pattern: Pattern::Tuple(vec![
5014 Pattern::Variable(Register(1)),
5015 Pattern::Variable(Register(2)),
5016 ]),
5017 fail_target: 7,
5018 },
5019 Instruction::LoadInt {
5020 value: 1,
5021 dest: Register(3),
5022 },
5023 Instruction::End,
5024 Instruction::LoadInt {
5026 value: 0,
5027 dest: Register(3),
5028 },
5029 Instruction::End,
5030 ];
5031
5032 scheduler.spawn(program);
5033 run_to_idle(&mut scheduler);
5034
5035 let process = scheduler.processes.get(&Pid(0)).unwrap();
5036 assert_eq!(process.registers[3], Value::Int(0)); }
5038
5039 #[test]
5040 fn test_match_nested_tuple() {
5041 let mut scheduler = Scheduler::new();
5042
5043 let program = vec![
5045 Instruction::LoadAtom {
5047 name: "data".to_string(),
5048 dest: Register(0),
5049 },
5050 Instruction::Push {
5051 source: Operand::Reg(Register(0)),
5052 },
5053 Instruction::Push {
5054 source: Operand::Int(100),
5055 },
5056 Instruction::MakeTuple {
5057 arity: 2,
5058 dest: Register(0),
5059 },
5060 Instruction::LoadAtom {
5062 name: "ok".to_string(),
5063 dest: Register(1),
5064 },
5065 Instruction::Push {
5066 source: Operand::Reg(Register(1)),
5067 },
5068 Instruction::Push {
5069 source: Operand::Reg(Register(0)),
5070 },
5071 Instruction::MakeTuple {
5072 arity: 2,
5073 dest: Register(0),
5074 },
5075 Instruction::Match {
5077 source: Register(0),
5078 pattern: Pattern::Tuple(vec![
5079 Pattern::Atom("ok".to_string()),
5080 Pattern::Tuple(vec![
5081 Pattern::Atom("data".to_string()),
5082 Pattern::Variable(Register(1)),
5083 ]),
5084 ]),
5085 fail_target: 11,
5086 },
5087 Instruction::LoadInt {
5088 value: 1,
5089 dest: Register(2),
5090 },
5091 Instruction::End,
5092 Instruction::LoadInt {
5094 value: 0,
5095 dest: Register(2),
5096 },
5097 Instruction::End,
5098 ];
5099
5100 scheduler.spawn(program);
5101 run_to_idle(&mut scheduler);
5102
5103 let process = scheduler.processes.get(&Pid(0)).unwrap();
5104 assert_eq!(process.registers[1], Value::Int(100)); assert_eq!(process.registers[2], Value::Int(1)); }
5107
5108 #[test]
5111 fn test_receive_match_simple() {
5112 let mut scheduler = Scheduler::new();
5113
5114 let child_code = vec![
5116 Instruction::ReceiveMatch {
5118 clauses: vec![(Pattern::Variable(Register(0)), 2)],
5119 timeout: None,
5120 timeout_target: 0,
5121 },
5122 Instruction::End, Instruction::LoadInt {
5125 value: 1,
5126 dest: Register(1),
5127 },
5128 Instruction::End,
5129 ];
5130
5131 let parent_code = vec![
5132 Instruction::Spawn {
5133 code: child_code,
5134 dest: Register(0),
5135 },
5136 Instruction::Send {
5137 to: Source::Reg(Register(0)),
5138 msg: "hello".to_string(),
5139 },
5140 Instruction::End,
5141 ];
5142
5143 scheduler.spawn(parent_code);
5144 run_to_idle(&mut scheduler);
5145
5146 let child = scheduler.processes.get(&Pid(1)).unwrap();
5147 assert_eq!(child.registers[0], Value::String("hello".to_string()));
5148 assert_eq!(child.registers[1], Value::Int(1));
5149 }
5150
5151 #[test]
5152 fn test_receive_match_specific_string() {
5153 let mut scheduler = Scheduler::new();
5154
5155 let child_code = vec![
5157 Instruction::ReceiveMatch {
5158 clauses: vec![(Pattern::String("ping".to_string()), 2)],
5159 timeout: None,
5160 timeout_target: 0,
5161 },
5162 Instruction::End,
5163 Instruction::LoadInt {
5165 value: 1,
5166 dest: Register(0),
5167 },
5168 Instruction::End,
5169 ];
5170
5171 let parent_code = vec![
5172 Instruction::Spawn {
5173 code: child_code,
5174 dest: Register(0),
5175 },
5176 Instruction::Send {
5178 to: Source::Reg(Register(0)),
5179 msg: "pong".to_string(),
5180 },
5181 Instruction::Send {
5183 to: Source::Reg(Register(0)),
5184 msg: "ping".to_string(),
5185 },
5186 Instruction::End,
5187 ];
5188
5189 scheduler.spawn(parent_code);
5190 run_to_idle(&mut scheduler);
5191
5192 let child = scheduler.processes.get(&Pid(1)).unwrap();
5193 assert_eq!(child.registers[0], Value::Int(1));
5194 assert_eq!(child.mailbox.len(), 1);
5196 }
5197
5198 #[test]
5199 fn test_receive_match_multiple_clauses() {
5200 let mut scheduler = Scheduler::new();
5201
5202 let child_code = vec![
5204 Instruction::ReceiveMatch {
5205 clauses: vec![
5206 (Pattern::String("ping".to_string()), 2), (Pattern::String("pong".to_string()), 5), ],
5209 timeout: None,
5210 timeout_target: 0,
5211 },
5212 Instruction::End,
5213 Instruction::LoadInt {
5215 value: 1,
5216 dest: Register(0),
5217 },
5218 Instruction::End,
5219 Instruction::End, Instruction::LoadInt {
5222 value: 2,
5223 dest: Register(0),
5224 },
5225 Instruction::End,
5226 ];
5227
5228 let parent_code = vec![
5229 Instruction::Spawn {
5230 code: child_code,
5231 dest: Register(0),
5232 },
5233 Instruction::Send {
5234 to: Source::Reg(Register(0)),
5235 msg: "pong".to_string(),
5236 },
5237 Instruction::End,
5238 ];
5239
5240 scheduler.spawn(parent_code);
5241 run_to_idle(&mut scheduler);
5242
5243 let child = scheduler.processes.get(&Pid(1)).unwrap();
5244 assert_eq!(child.registers[0], Value::Int(2)); }
5246
5247 #[test]
5248 fn test_receive_match_timeout() {
5249 let mut scheduler = Scheduler::new();
5250
5251 let program = vec![
5253 Instruction::ReceiveMatch {
5254 clauses: vec![(Pattern::String("hello".to_string()), 3)],
5255 timeout: Some(5),
5256 timeout_target: 5,
5257 },
5258 Instruction::End, Instruction::End, Instruction::LoadInt {
5262 value: 1,
5263 dest: Register(0),
5264 },
5265 Instruction::End,
5266 Instruction::LoadInt {
5268 value: 99,
5269 dest: Register(0),
5270 },
5271 Instruction::End,
5272 ];
5273
5274 scheduler.spawn(program);
5275 run_to_idle(&mut scheduler);
5276
5277 let process = scheduler.processes.get(&Pid(0)).unwrap();
5278 assert_eq!(process.registers[0], Value::Int(99)); }
5280
5281 #[test]
5282 fn test_receive_match_selective() {
5283 let mut scheduler = Scheduler::new();
5284
5285 let child_code = vec![
5287 Instruction::ReceiveMatch {
5289 clauses: vec![(Pattern::String("second".to_string()), 2)],
5290 timeout: None,
5291 timeout_target: 0,
5292 },
5293 Instruction::End,
5294 Instruction::LoadInt {
5296 value: 1,
5297 dest: Register(0),
5298 },
5299 Instruction::ReceiveMatch {
5301 clauses: vec![(Pattern::String("first".to_string()), 5)],
5302 timeout: None,
5303 timeout_target: 0,
5304 },
5305 Instruction::End,
5306 Instruction::LoadInt {
5308 value: 2,
5309 dest: Register(1),
5310 },
5311 Instruction::End,
5312 ];
5313
5314 let parent_code = vec![
5315 Instruction::Spawn {
5316 code: child_code,
5317 dest: Register(0),
5318 },
5319 Instruction::Send {
5321 to: Source::Reg(Register(0)),
5322 msg: "first".to_string(),
5323 },
5324 Instruction::Send {
5325 to: Source::Reg(Register(0)),
5326 msg: "second".to_string(),
5327 },
5328 Instruction::End,
5329 ];
5330
5331 scheduler.spawn(parent_code);
5332 run_to_idle(&mut scheduler);
5333
5334 let child = scheduler.processes.get(&Pid(1)).unwrap();
5335 assert_eq!(child.registers[0], Value::Int(1)); assert_eq!(child.registers[1], Value::Int(2)); }
5338
5339 #[test]
5342 fn test_make_list() {
5343 let mut scheduler = Scheduler::new();
5344
5345 let program = vec![
5346 Instruction::Push {
5348 source: Operand::Int(1),
5349 },
5350 Instruction::Push {
5351 source: Operand::Int(2),
5352 },
5353 Instruction::Push {
5354 source: Operand::Int(3),
5355 },
5356 Instruction::MakeList {
5358 length: 3,
5359 dest: Register(0),
5360 },
5361 Instruction::End,
5362 ];
5363
5364 scheduler.spawn(program);
5365 run_to_idle(&mut scheduler);
5366
5367 let process = scheduler.processes.get(&Pid(0)).unwrap();
5368 assert_eq!(
5369 process.registers[0],
5370 Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
5371 );
5372 }
5373
5374 #[test]
5375 fn test_make_empty_list() {
5376 let mut scheduler = Scheduler::new();
5377
5378 let program = vec![
5379 Instruction::MakeList {
5380 length: 0,
5381 dest: Register(0),
5382 },
5383 Instruction::End,
5384 ];
5385
5386 scheduler.spawn(program);
5387 run_to_idle(&mut scheduler);
5388
5389 let process = scheduler.processes.get(&Pid(0)).unwrap();
5390 assert_eq!(process.registers[0], Value::List(vec![]));
5391 }
5392
5393 #[test]
5394 fn test_cons() {
5395 let mut scheduler = Scheduler::new();
5396
5397 let program = vec![
5398 Instruction::MakeList {
5400 length: 0,
5401 dest: Register(0),
5402 },
5403 Instruction::LoadInt {
5405 value: 3,
5406 dest: Register(1),
5407 },
5408 Instruction::Cons {
5410 head: Register(1),
5411 tail: Register(0),
5412 dest: Register(0),
5413 },
5414 Instruction::LoadInt {
5416 value: 2,
5417 dest: Register(1),
5418 },
5419 Instruction::Cons {
5421 head: Register(1),
5422 tail: Register(0),
5423 dest: Register(0),
5424 },
5425 Instruction::LoadInt {
5427 value: 1,
5428 dest: Register(1),
5429 },
5430 Instruction::Cons {
5432 head: Register(1),
5433 tail: Register(0),
5434 dest: Register(0),
5435 },
5436 Instruction::End,
5437 ];
5438
5439 scheduler.spawn(program);
5440 run_to_idle(&mut scheduler);
5441
5442 let process = scheduler.processes.get(&Pid(0)).unwrap();
5443 assert_eq!(
5444 process.registers[0],
5445 Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
5446 );
5447 }
5448
5449 #[test]
5450 fn test_list_head_tail() {
5451 let mut scheduler = Scheduler::new();
5452
5453 let program = vec![
5454 Instruction::Push {
5456 source: Operand::Int(1),
5457 },
5458 Instruction::Push {
5459 source: Operand::Int(2),
5460 },
5461 Instruction::Push {
5462 source: Operand::Int(3),
5463 },
5464 Instruction::MakeList {
5465 length: 3,
5466 dest: Register(0),
5467 },
5468 Instruction::ListHead {
5470 list: Register(0),
5471 dest: Register(1),
5472 },
5473 Instruction::ListTail {
5475 list: Register(0),
5476 dest: Register(2),
5477 },
5478 Instruction::End,
5479 ];
5480
5481 scheduler.spawn(program);
5482 run_to_idle(&mut scheduler);
5483
5484 let process = scheduler.processes.get(&Pid(0)).unwrap();
5485 assert_eq!(process.registers[1], Value::Int(1));
5486 assert_eq!(
5487 process.registers[2],
5488 Value::List(vec![Value::Int(2), Value::Int(3)])
5489 );
5490 }
5491
5492 #[test]
5493 fn test_list_is_empty() {
5494 let mut scheduler = Scheduler::new();
5495
5496 let program = vec![
5497 Instruction::MakeList {
5499 length: 0,
5500 dest: Register(0),
5501 },
5502 Instruction::ListIsEmpty {
5503 list: Register(0),
5504 dest: Register(1),
5505 },
5506 Instruction::Push {
5508 source: Operand::Int(1),
5509 },
5510 Instruction::MakeList {
5511 length: 1,
5512 dest: Register(2),
5513 },
5514 Instruction::ListIsEmpty {
5515 list: Register(2),
5516 dest: Register(3),
5517 },
5518 Instruction::End,
5519 ];
5520
5521 scheduler.spawn(program);
5522 run_to_idle(&mut scheduler);
5523
5524 let process = scheduler.processes.get(&Pid(0)).unwrap();
5525 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
5528
5529 #[test]
5530 fn test_list_head_empty_crashes() {
5531 let mut scheduler = Scheduler::new();
5532
5533 let program = vec![
5534 Instruction::MakeList {
5535 length: 0,
5536 dest: Register(0),
5537 },
5538 Instruction::ListHead {
5539 list: Register(0),
5540 dest: Register(1),
5541 },
5542 Instruction::End,
5543 ];
5544
5545 scheduler.spawn(program);
5546 run_to_idle(&mut scheduler);
5547
5548 let (_, _, _, crashed) = scheduler.process_count();
5549 assert_eq!(crashed, 1);
5550 }
5551
5552 #[test]
5553 fn test_match_list_empty() {
5554 let mut scheduler = Scheduler::new();
5555
5556 let program = vec![
5557 Instruction::MakeList {
5559 length: 0,
5560 dest: Register(0),
5561 },
5562 Instruction::Match {
5564 source: Register(0),
5565 pattern: Pattern::ListEmpty,
5566 fail_target: 4,
5567 },
5568 Instruction::LoadInt {
5570 value: 1,
5571 dest: Register(1),
5572 },
5573 Instruction::End,
5574 Instruction::LoadInt {
5576 value: 0,
5577 dest: Register(1),
5578 },
5579 Instruction::End,
5580 ];
5581
5582 scheduler.spawn(program);
5583 run_to_idle(&mut scheduler);
5584
5585 let process = scheduler.processes.get(&Pid(0)).unwrap();
5586 assert_eq!(process.registers[1], Value::Int(1)); }
5588
5589 #[test]
5590 fn test_match_list_cons() {
5591 let mut scheduler = Scheduler::new();
5592
5593 let program = vec![
5594 Instruction::Push {
5596 source: Operand::Int(1),
5597 },
5598 Instruction::Push {
5599 source: Operand::Int(2),
5600 },
5601 Instruction::Push {
5602 source: Operand::Int(3),
5603 },
5604 Instruction::MakeList {
5605 length: 3,
5606 dest: Register(0),
5607 },
5608 Instruction::Match {
5610 source: Register(0),
5611 pattern: Pattern::ListCons {
5612 head: Box::new(Pattern::Variable(Register(1))),
5613 tail: Box::new(Pattern::Variable(Register(2))),
5614 },
5615 fail_target: 7,
5616 },
5617 Instruction::LoadInt {
5619 value: 1,
5620 dest: Register(3),
5621 },
5622 Instruction::End,
5623 Instruction::LoadInt {
5625 value: 0,
5626 dest: Register(3),
5627 },
5628 Instruction::End,
5629 ];
5630
5631 scheduler.spawn(program);
5632 run_to_idle(&mut scheduler);
5633
5634 let process = scheduler.processes.get(&Pid(0)).unwrap();
5635 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(
5637 process.registers[2],
5638 Value::List(vec![Value::Int(2), Value::Int(3)])
5639 ); assert_eq!(process.registers[3], Value::Int(1)); }
5642
5643 #[test]
5644 fn test_match_list_cons_on_empty_fails() {
5645 let mut scheduler = Scheduler::new();
5646
5647 let program = vec![
5648 Instruction::MakeList {
5650 length: 0,
5651 dest: Register(0),
5652 },
5653 Instruction::Match {
5655 source: Register(0),
5656 pattern: Pattern::ListCons {
5657 head: Box::new(Pattern::Variable(Register(1))),
5658 tail: Box::new(Pattern::Variable(Register(2))),
5659 },
5660 fail_target: 4,
5661 },
5662 Instruction::LoadInt {
5663 value: 1,
5664 dest: Register(3),
5665 },
5666 Instruction::End,
5667 Instruction::LoadInt {
5669 value: 0,
5670 dest: Register(3),
5671 },
5672 Instruction::End,
5673 ];
5674
5675 scheduler.spawn(program);
5676 run_to_idle(&mut scheduler);
5677
5678 let process = scheduler.processes.get(&Pid(0)).unwrap();
5679 assert_eq!(process.registers[3], Value::Int(0)); }
5681
5682 #[test]
5683 fn test_match_list_nested() {
5684 let mut scheduler = Scheduler::new();
5685
5686 let program = vec![
5688 Instruction::Push {
5690 source: Operand::Int(1),
5691 },
5692 Instruction::Push {
5693 source: Operand::Int(2),
5694 },
5695 Instruction::Push {
5696 source: Operand::Int(3),
5697 },
5698 Instruction::Push {
5699 source: Operand::Int(4),
5700 },
5701 Instruction::MakeList {
5702 length: 4,
5703 dest: Register(0),
5704 },
5705 Instruction::Match {
5707 source: Register(0),
5708 pattern: Pattern::ListCons {
5709 head: Box::new(Pattern::Int(1)),
5710 tail: Box::new(Pattern::ListCons {
5711 head: Box::new(Pattern::Int(2)),
5712 tail: Box::new(Pattern::Variable(Register(1))),
5713 }),
5714 },
5715 fail_target: 8,
5716 },
5717 Instruction::LoadInt {
5719 value: 1,
5720 dest: Register(2),
5721 },
5722 Instruction::End,
5723 Instruction::LoadInt {
5725 value: 0,
5726 dest: Register(2),
5727 },
5728 Instruction::End,
5729 ];
5730
5731 scheduler.spawn(program);
5732 run_to_idle(&mut scheduler);
5733
5734 let process = scheduler.processes.get(&Pid(0)).unwrap();
5735 assert_eq!(
5736 process.registers[1],
5737 Value::List(vec![Value::Int(3), Value::Int(4)])
5738 ); assert_eq!(process.registers[2], Value::Int(1)); }
5741
5742 #[test]
5745 fn test_module_load() {
5746 use crate::Module;
5747
5748 let mut scheduler = Scheduler::new();
5749
5750 let mut math = Module::new("math".to_string());
5751 math.add_function(
5752 "identity".to_string(),
5753 1,
5754 vec![
5755 Instruction::Return,
5757 ],
5758 );
5759 math.export("identity", 1);
5760
5761 assert!(scheduler.load_module(math).is_ok());
5762 assert!(scheduler.modules.contains_key("math"));
5763 }
5764
5765 #[test]
5766 fn test_call_mfa() {
5767 use crate::Module;
5768
5769 let mut scheduler = Scheduler::new();
5770
5771 let mut math = Module::new("math".to_string());
5773 math.add_function(
5774 "add_one".to_string(),
5775 1,
5776 vec![
5777 Instruction::Add {
5779 a: Operand::Reg(Register(0)),
5780 b: Operand::Int(1),
5781 dest: Register(0),
5782 },
5783 Instruction::Return,
5784 ],
5785 );
5786 math.export("add_one", 1);
5787 scheduler.load_module(math).unwrap();
5788
5789 let program = vec![
5791 Instruction::LoadInt {
5792 value: 5,
5793 dest: Register(0),
5794 },
5795 Instruction::CallMFA {
5796 module: "math".to_string(),
5797 function: "add_one".to_string(),
5798 arity: 1,
5799 },
5800 Instruction::End,
5802 ];
5803
5804 scheduler.spawn(program);
5805 run_to_idle(&mut scheduler);
5806
5807 let process = scheduler.processes.get(&Pid(0)).unwrap();
5808 assert_eq!(process.registers[0], Value::Int(6));
5809 }
5810
5811 #[test]
5812 fn test_call_local() {
5813 use crate::Module;
5814
5815 let mut scheduler = Scheduler::new();
5816
5817 let mut math = Module::new("math".to_string());
5819
5820 math.add_function(
5822 "double".to_string(),
5823 1,
5824 vec![
5825 Instruction::Mul {
5826 a: Operand::Reg(Register(0)),
5827 b: Operand::Int(2),
5828 dest: Register(0),
5829 },
5830 Instruction::Return,
5831 ],
5832 );
5833
5834 math.add_function(
5836 "quadruple".to_string(),
5837 1,
5838 vec![
5839 Instruction::CallLocal {
5840 function: "double".to_string(),
5841 arity: 1,
5842 },
5843 Instruction::CallLocal {
5844 function: "double".to_string(),
5845 arity: 1,
5846 },
5847 Instruction::Return,
5848 ],
5849 );
5850 math.export("quadruple", 1);
5851 scheduler.load_module(math).unwrap();
5852
5853 let program = vec![
5855 Instruction::LoadInt {
5856 value: 3,
5857 dest: Register(0),
5858 },
5859 Instruction::CallMFA {
5860 module: "math".to_string(),
5861 function: "quadruple".to_string(),
5862 arity: 1,
5863 },
5864 Instruction::End,
5865 ];
5866
5867 scheduler.spawn(program);
5868 run_to_idle(&mut scheduler);
5869
5870 let process = scheduler.processes.get(&Pid(0)).unwrap();
5871 assert_eq!(process.registers[0], Value::Int(12));
5872 }
5873
5874 #[test]
5875 fn test_tail_call_no_stack_growth() {
5876 use crate::Module;
5877
5878 let mut scheduler = Scheduler::new();
5879
5880 let mut counter = Module::new("counter".to_string());
5882
5883 counter.add_function(
5886 "countdown".to_string(),
5887 1,
5888 vec![
5889 Instruction::Eq {
5891 a: Operand::Reg(Register(0)),
5892 b: Operand::Int(0),
5893 dest: Register(1),
5894 },
5895 Instruction::JumpIf {
5896 cond: Operand::Reg(Register(1)),
5897 target: 5, },
5899 Instruction::Sub {
5901 a: Operand::Reg(Register(0)),
5902 b: Operand::Int(1),
5903 dest: Register(0),
5904 },
5905 Instruction::TailCallLocal {
5907 function: "countdown".to_string(),
5908 arity: 1,
5909 },
5910 Instruction::End,
5912 Instruction::LoadInt {
5914 value: 0,
5915 dest: Register(0),
5916 },
5917 Instruction::Return,
5918 ],
5919 );
5920 counter.export("countdown", 1);
5921 scheduler.load_module(counter).unwrap();
5922
5923 let program = vec![
5925 Instruction::LoadInt {
5926 value: 1000,
5927 dest: Register(0),
5928 },
5929 Instruction::CallMFA {
5930 module: "counter".to_string(),
5931 function: "countdown".to_string(),
5932 arity: 1,
5933 },
5934 Instruction::End,
5935 ];
5936
5937 scheduler.spawn(program);
5938 run_to_idle(&mut scheduler);
5939
5940 let process = scheduler.processes.get(&Pid(0)).unwrap();
5941 assert_eq!(process.registers[0], Value::Int(0));
5942 assert!(process.call_stack.is_empty());
5944 }
5945
5946 #[test]
5947 fn test_spawn_mfa() {
5948 use crate::Module;
5949
5950 let mut scheduler = Scheduler::new();
5951
5952 let mut worker = Module::new("worker".to_string());
5954 worker.add_function(
5955 "start".to_string(),
5956 1,
5957 vec![
5958 Instruction::Mul {
5960 a: Operand::Reg(Register(0)),
5961 b: Operand::Int(2),
5962 dest: Register(0),
5963 },
5964 Instruction::End,
5965 ],
5966 );
5967 worker.export("start", 1);
5968 scheduler.load_module(worker).unwrap();
5969
5970 let program = vec![
5972 Instruction::LoadInt {
5973 value: 21,
5974 dest: Register(0),
5975 },
5976 Instruction::SpawnMFA {
5977 module: "worker".to_string(),
5978 function: "start".to_string(),
5979 arity: 1,
5980 dest: Register(1),
5981 },
5982 Instruction::End,
5983 ];
5984
5985 scheduler.spawn(program);
5986 run_to_idle(&mut scheduler);
5987
5988 let child = scheduler.processes.get(&Pid(1)).unwrap();
5990 assert_eq!(child.registers[0], Value::Int(42));
5991 assert_eq!(child.status, ProcessStatus::Done);
5992 }
5993
5994 #[test]
5995 fn test_make_fun_and_apply() {
5996 use crate::Module;
5997
5998 let mut scheduler = Scheduler::new();
5999
6000 let mut math = Module::new("math".to_string());
6002 math.add_function(
6003 "double".to_string(),
6004 1,
6005 vec![
6006 Instruction::Mul {
6007 a: Operand::Reg(Register(0)),
6008 b: Operand::Int(2),
6009 dest: Register(0),
6010 },
6011 Instruction::Return,
6012 ],
6013 );
6014 math.export("double", 1);
6015 scheduler.load_module(math).unwrap();
6016
6017 let program = vec![
6019 Instruction::MakeFun {
6021 module: "math".to_string(),
6022 function: "double".to_string(),
6023 arity: 1,
6024 dest: Register(7),
6025 },
6026 Instruction::LoadInt {
6028 value: 21,
6029 dest: Register(0),
6030 },
6031 Instruction::Apply {
6033 fun: Register(7),
6034 arity: 1,
6035 },
6036 Instruction::End,
6037 ];
6038
6039 scheduler.spawn(program);
6040 run_to_idle(&mut scheduler);
6041
6042 let process = scheduler.processes.get(&Pid(0)).unwrap();
6043 assert_eq!(process.registers[0], Value::Int(42));
6044 }
6045
6046 #[test]
6047 fn test_cross_module_call() {
6048 use crate::Module;
6049
6050 let mut scheduler = Scheduler::new();
6051
6052 let mut mod_a = Module::new("mod_a".to_string());
6054 mod_a.add_function(
6055 "call_b".to_string(),
6056 1,
6057 vec![
6058 Instruction::CallMFA {
6059 module: "mod_b".to_string(),
6060 function: "add_ten".to_string(),
6061 arity: 1,
6062 },
6063 Instruction::Return,
6064 ],
6065 );
6066 mod_a.export("call_b", 1);
6067
6068 let mut mod_b = Module::new("mod_b".to_string());
6069 mod_b.add_function(
6070 "add_ten".to_string(),
6071 1,
6072 vec![
6073 Instruction::Add {
6074 a: Operand::Reg(Register(0)),
6075 b: Operand::Int(10),
6076 dest: Register(0),
6077 },
6078 Instruction::Return,
6079 ],
6080 );
6081 mod_b.export("add_ten", 1);
6082
6083 scheduler.load_module(mod_a).unwrap();
6084 scheduler.load_module(mod_b).unwrap();
6085
6086 let program = vec![
6087 Instruction::LoadInt {
6088 value: 5,
6089 dest: Register(0),
6090 },
6091 Instruction::CallMFA {
6092 module: "mod_a".to_string(),
6093 function: "call_b".to_string(),
6094 arity: 1,
6095 },
6096 Instruction::End,
6097 ];
6098
6099 scheduler.spawn(program);
6100 run_to_idle(&mut scheduler);
6101
6102 let process = scheduler.processes.get(&Pid(0)).unwrap();
6103 assert_eq!(process.registers[0], Value::Int(15));
6104 }
6105
6106 #[test]
6107 fn test_make_closure_and_apply() {
6108 use crate::Module;
6109
6110 let mut scheduler = Scheduler::new();
6111
6112 let mut math = Module::new("math".to_string());
6115 math.add_function(
6116 "multiply_impl".to_string(),
6117 2, vec![
6119 Instruction::Mul {
6120 a: Operand::Reg(Register(0)),
6121 b: Operand::Reg(Register(1)),
6122 dest: Register(0),
6123 },
6124 Instruction::Return,
6125 ],
6126 );
6127 math.export("multiply_impl", 2);
6128 scheduler.load_module(math).unwrap();
6129
6130 let program = vec![
6132 Instruction::LoadInt {
6134 value: 5,
6135 dest: Register(1),
6136 },
6137 Instruction::MakeClosure {
6139 module: "math".to_string(),
6140 function: "multiply_impl".to_string(),
6141 arity: 1, captures: vec![Register(1)],
6143 dest: Register(2),
6144 },
6145 Instruction::LoadInt {
6147 value: 7,
6148 dest: Register(0),
6149 },
6150 Instruction::Apply {
6152 fun: Register(2),
6153 arity: 1,
6154 },
6155 Instruction::End,
6157 ];
6158
6159 scheduler.spawn(program);
6160 run_to_idle(&mut scheduler);
6161
6162 let process = scheduler.processes.get(&Pid(0)).unwrap();
6163 assert_eq!(process.registers[0], Value::Int(35));
6164 }
6165
6166 #[test]
6167 fn test_closure_with_multiple_captures() {
6168 use crate::Module;
6169
6170 let mut scheduler = Scheduler::new();
6171
6172 let mut math = Module::new("math".to_string());
6174 math.add_function(
6175 "add_three".to_string(),
6176 3, vec![
6178 Instruction::Add {
6179 a: Operand::Reg(Register(0)),
6180 b: Operand::Reg(Register(1)),
6181 dest: Register(0),
6182 },
6183 Instruction::Add {
6184 a: Operand::Reg(Register(0)),
6185 b: Operand::Reg(Register(2)),
6186 dest: Register(0),
6187 },
6188 Instruction::Return,
6189 ],
6190 );
6191 math.export("add_three", 3);
6192 scheduler.load_module(math).unwrap();
6193
6194 let program = vec![
6195 Instruction::LoadInt {
6197 value: 10,
6198 dest: Register(3),
6199 },
6200 Instruction::LoadInt {
6201 value: 20,
6202 dest: Register(4),
6203 },
6204 Instruction::MakeClosure {
6206 module: "math".to_string(),
6207 function: "add_three".to_string(),
6208 arity: 1,
6209 captures: vec![Register(3), Register(4)],
6210 dest: Register(5),
6211 },
6212 Instruction::LoadInt {
6214 value: 5,
6215 dest: Register(0),
6216 },
6217 Instruction::Apply {
6219 fun: Register(5),
6220 arity: 1,
6221 },
6222 Instruction::End,
6223 ];
6224
6225 scheduler.spawn(program);
6226 run_to_idle(&mut scheduler);
6227
6228 let process = scheduler.processes.get(&Pid(0)).unwrap();
6229 assert_eq!(process.registers[0], Value::Int(35));
6230 }
6231
6232 #[test]
6233 fn test_closure_values_are_copied() {
6234 use crate::Module;
6235
6236 let mut scheduler = Scheduler::new();
6237
6238 let mut mod_test = Module::new("test".to_string());
6241 mod_test.add_function(
6242 "get_captured".to_string(),
6243 1, vec![Instruction::Return],
6245 );
6246 mod_test.export("get_captured", 1);
6247 scheduler.load_module(mod_test).unwrap();
6248
6249 let program = vec![
6250 Instruction::LoadInt {
6252 value: 42,
6253 dest: Register(1),
6254 },
6255 Instruction::MakeClosure {
6257 module: "test".to_string(),
6258 function: "get_captured".to_string(),
6259 arity: 0,
6260 captures: vec![Register(1)],
6261 dest: Register(2),
6262 },
6263 Instruction::LoadInt {
6265 value: 100,
6266 dest: Register(1),
6267 },
6268 Instruction::Apply {
6270 fun: Register(2),
6271 arity: 0,
6272 },
6273 Instruction::End,
6275 ];
6276
6277 scheduler.spawn(program);
6278 run_to_idle(&mut scheduler);
6279
6280 let process = scheduler.processes.get(&Pid(0)).unwrap();
6281 assert_eq!(process.registers[0], Value::Int(42));
6282 }
6283
6284 #[test]
6285 fn test_closure_arity_mismatch_crashes() {
6286 use crate::Module;
6287
6288 let mut scheduler = Scheduler::new();
6289
6290 let mut math = Module::new("math".to_string());
6291 math.add_function(
6292 "add".to_string(),
6293 2,
6294 vec![
6295 Instruction::Add {
6296 a: Operand::Reg(Register(0)),
6297 b: Operand::Reg(Register(1)),
6298 dest: Register(0),
6299 },
6300 Instruction::Return,
6301 ],
6302 );
6303 math.export("add", 2);
6304 scheduler.load_module(math).unwrap();
6305
6306 let program = vec![
6307 Instruction::LoadInt {
6308 value: 5,
6309 dest: Register(1),
6310 },
6311 Instruction::MakeClosure {
6312 module: "math".to_string(),
6313 function: "add".to_string(),
6314 arity: 1, captures: vec![Register(1)],
6316 dest: Register(2),
6317 },
6318 Instruction::Apply {
6320 fun: Register(2),
6321 arity: 2, },
6323 Instruction::End,
6324 ];
6325
6326 scheduler.spawn(program);
6327 run_to_idle(&mut scheduler);
6328
6329 let process = scheduler.processes.get(&Pid(0)).unwrap();
6330 assert_eq!(process.status, ProcessStatus::Crashed);
6331 }
6332
6333 #[test]
6336 fn test_list_length() {
6337 let mut scheduler = Scheduler::new();
6338
6339 let program = vec![
6340 Instruction::Push {
6342 source: Operand::Int(1),
6343 },
6344 Instruction::Push {
6345 source: Operand::Int(2),
6346 },
6347 Instruction::Push {
6348 source: Operand::Int(3),
6349 },
6350 Instruction::MakeList {
6351 length: 3,
6352 dest: Register(0),
6353 },
6354 Instruction::ListLength {
6355 list: Register(0),
6356 dest: Register(1),
6357 },
6358 Instruction::MakeList {
6360 length: 0,
6361 dest: Register(2),
6362 },
6363 Instruction::ListLength {
6364 list: Register(2),
6365 dest: Register(3),
6366 },
6367 Instruction::End,
6368 ];
6369
6370 scheduler.spawn(program);
6371 run_to_idle(&mut scheduler);
6372
6373 let process = scheduler.processes.get(&Pid(0)).unwrap();
6374 assert_eq!(process.registers[1], Value::Int(3));
6375 assert_eq!(process.registers[3], Value::Int(0));
6376 }
6377
6378 #[test]
6379 fn test_list_append() {
6380 let mut scheduler = Scheduler::new();
6381
6382 let program = vec![
6383 Instruction::Push {
6385 source: Operand::Int(1),
6386 },
6387 Instruction::Push {
6388 source: Operand::Int(2),
6389 },
6390 Instruction::MakeList {
6391 length: 2,
6392 dest: Register(0),
6393 },
6394 Instruction::Push {
6396 source: Operand::Int(3),
6397 },
6398 Instruction::Push {
6399 source: Operand::Int(4),
6400 },
6401 Instruction::MakeList {
6402 length: 2,
6403 dest: Register(1),
6404 },
6405 Instruction::ListAppend {
6407 a: Register(0),
6408 b: Register(1),
6409 dest: Register(2),
6410 },
6411 Instruction::End,
6412 ];
6413
6414 scheduler.spawn(program);
6415 run_to_idle(&mut scheduler);
6416
6417 let process = scheduler.processes.get(&Pid(0)).unwrap();
6418 assert_eq!(
6419 process.registers[2],
6420 Value::List(vec![
6421 Value::Int(1),
6422 Value::Int(2),
6423 Value::Int(3),
6424 Value::Int(4)
6425 ])
6426 );
6427 }
6428
6429 #[test]
6430 fn test_list_reverse() {
6431 let mut scheduler = Scheduler::new();
6432
6433 let program = vec![
6434 Instruction::Push {
6436 source: Operand::Int(1),
6437 },
6438 Instruction::Push {
6439 source: Operand::Int(2),
6440 },
6441 Instruction::Push {
6442 source: Operand::Int(3),
6443 },
6444 Instruction::MakeList {
6445 length: 3,
6446 dest: Register(0),
6447 },
6448 Instruction::ListReverse {
6449 list: Register(0),
6450 dest: Register(1),
6451 },
6452 Instruction::End,
6453 ];
6454
6455 scheduler.spawn(program);
6456 run_to_idle(&mut scheduler);
6457
6458 let process = scheduler.processes.get(&Pid(0)).unwrap();
6459 assert_eq!(
6460 process.registers[1],
6461 Value::List(vec![Value::Int(3), Value::Int(2), Value::Int(1)])
6462 );
6463 }
6464
6465 #[test]
6466 fn test_list_nth() {
6467 let mut scheduler = Scheduler::new();
6468
6469 let program = vec![
6470 Instruction::Push {
6472 source: Operand::Int(10),
6473 },
6474 Instruction::Push {
6475 source: Operand::Int(20),
6476 },
6477 Instruction::Push {
6478 source: Operand::Int(30),
6479 },
6480 Instruction::MakeList {
6481 length: 3,
6482 dest: Register(0),
6483 },
6484 Instruction::LoadInt {
6486 value: 0,
6487 dest: Register(1),
6488 },
6489 Instruction::ListNth {
6490 list: Register(0),
6491 n: Register(1),
6492 dest: Register(2),
6493 },
6494 Instruction::LoadInt {
6496 value: 2,
6497 dest: Register(3),
6498 },
6499 Instruction::ListNth {
6500 list: Register(0),
6501 n: Register(3),
6502 dest: Register(4),
6503 },
6504 Instruction::End,
6505 ];
6506
6507 scheduler.spawn(program);
6508 run_to_idle(&mut scheduler);
6509
6510 let process = scheduler.processes.get(&Pid(0)).unwrap();
6511 assert_eq!(process.registers[2], Value::Int(10)); assert_eq!(process.registers[4], Value::Int(30)); }
6514
6515 #[test]
6516 fn test_list_nth_out_of_bounds_crashes() {
6517 let mut scheduler = Scheduler::new();
6518
6519 let program = vec![
6520 Instruction::Push {
6522 source: Operand::Int(1),
6523 },
6524 Instruction::Push {
6525 source: Operand::Int(2),
6526 },
6527 Instruction::MakeList {
6528 length: 2,
6529 dest: Register(0),
6530 },
6531 Instruction::LoadInt {
6533 value: 5,
6534 dest: Register(1),
6535 },
6536 Instruction::ListNth {
6537 list: Register(0),
6538 n: Register(1),
6539 dest: Register(2),
6540 },
6541 Instruction::End,
6542 ];
6543
6544 scheduler.spawn(program);
6545 run_to_idle(&mut scheduler);
6546
6547 let process = scheduler.processes.get(&Pid(0)).unwrap();
6548 assert_eq!(process.status, ProcessStatus::Crashed);
6549 }
6550
6551 #[test]
6552 fn test_list_member() {
6553 let mut scheduler = Scheduler::new();
6554
6555 let program = vec![
6556 Instruction::Push {
6558 source: Operand::Int(1),
6559 },
6560 Instruction::Push {
6561 source: Operand::Int(2),
6562 },
6563 Instruction::Push {
6564 source: Operand::Int(3),
6565 },
6566 Instruction::MakeList {
6567 length: 3,
6568 dest: Register(0),
6569 },
6570 Instruction::LoadInt {
6572 value: 2,
6573 dest: Register(1),
6574 },
6575 Instruction::ListMember {
6576 elem: Register(1),
6577 list: Register(0),
6578 dest: Register(2),
6579 },
6580 Instruction::LoadInt {
6582 value: 5,
6583 dest: Register(3),
6584 },
6585 Instruction::ListMember {
6586 elem: Register(3),
6587 list: Register(0),
6588 dest: Register(4),
6589 },
6590 Instruction::End,
6591 ];
6592
6593 scheduler.spawn(program);
6594 run_to_idle(&mut scheduler);
6595
6596 let process = scheduler.processes.get(&Pid(0)).unwrap();
6597 assert_eq!(process.registers[2], Value::Int(1)); assert_eq!(process.registers[4], Value::Int(0)); }
6600
6601 #[test]
6602 fn test_list_append_with_empty() {
6603 let mut scheduler = Scheduler::new();
6604
6605 let program = vec![
6606 Instruction::Push {
6608 source: Operand::Int(1),
6609 },
6610 Instruction::Push {
6611 source: Operand::Int(2),
6612 },
6613 Instruction::MakeList {
6614 length: 2,
6615 dest: Register(0),
6616 },
6617 Instruction::MakeList {
6619 length: 0,
6620 dest: Register(1),
6621 },
6622 Instruction::ListAppend {
6624 a: Register(0),
6625 b: Register(1),
6626 dest: Register(2),
6627 },
6628 Instruction::ListAppend {
6630 a: Register(1),
6631 b: Register(0),
6632 dest: Register(3),
6633 },
6634 Instruction::End,
6635 ];
6636
6637 scheduler.spawn(program);
6638 run_to_idle(&mut scheduler);
6639
6640 let process = scheduler.processes.get(&Pid(0)).unwrap();
6641 assert_eq!(
6642 process.registers[2],
6643 Value::List(vec![Value::Int(1), Value::Int(2)])
6644 );
6645 assert_eq!(
6646 process.registers[3],
6647 Value::List(vec![Value::Int(1), Value::Int(2)])
6648 );
6649 }
6650
6651 #[test]
6654 fn test_is_integer() {
6655 let mut scheduler = Scheduler::new();
6656
6657 let program = vec![
6658 Instruction::LoadInt {
6660 value: 42,
6661 dest: Register(0),
6662 },
6663 Instruction::IsInteger {
6664 source: Register(0),
6665 dest: Register(1),
6666 },
6667 Instruction::LoadAtom {
6669 name: "test".to_string(),
6670 dest: Register(2),
6671 },
6672 Instruction::IsInteger {
6673 source: Register(2),
6674 dest: Register(3),
6675 },
6676 Instruction::End,
6677 ];
6678
6679 scheduler.spawn(program);
6680 run_to_idle(&mut scheduler);
6681
6682 let process = scheduler.processes.get(&Pid(0)).unwrap();
6683 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
6686
6687 #[test]
6688 fn test_is_atom() {
6689 let mut scheduler = Scheduler::new();
6690
6691 let program = vec![
6692 Instruction::LoadAtom {
6694 name: "ok".to_string(),
6695 dest: Register(0),
6696 },
6697 Instruction::IsAtom {
6698 source: Register(0),
6699 dest: Register(1),
6700 },
6701 Instruction::LoadInt {
6703 value: 42,
6704 dest: Register(2),
6705 },
6706 Instruction::IsAtom {
6707 source: Register(2),
6708 dest: Register(3),
6709 },
6710 Instruction::End,
6711 ];
6712
6713 scheduler.spawn(program);
6714 run_to_idle(&mut scheduler);
6715
6716 let process = scheduler.processes.get(&Pid(0)).unwrap();
6717 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
6720
6721 #[test]
6722 fn test_is_tuple() {
6723 let mut scheduler = Scheduler::new();
6724
6725 let program = vec![
6726 Instruction::Push {
6728 source: Operand::Int(1),
6729 },
6730 Instruction::Push {
6731 source: Operand::Int(2),
6732 },
6733 Instruction::MakeTuple {
6734 arity: 2,
6735 dest: Register(0),
6736 },
6737 Instruction::IsTuple {
6738 source: Register(0),
6739 dest: Register(1),
6740 },
6741 Instruction::LoadInt {
6743 value: 42,
6744 dest: Register(2),
6745 },
6746 Instruction::IsTuple {
6747 source: Register(2),
6748 dest: Register(3),
6749 },
6750 Instruction::End,
6751 ];
6752
6753 scheduler.spawn(program);
6754 run_to_idle(&mut scheduler);
6755
6756 let process = scheduler.processes.get(&Pid(0)).unwrap();
6757 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
6760
6761 #[test]
6762 fn test_is_list() {
6763 let mut scheduler = Scheduler::new();
6764
6765 let program = vec![
6766 Instruction::Push {
6768 source: Operand::Int(1),
6769 },
6770 Instruction::Push {
6771 source: Operand::Int(2),
6772 },
6773 Instruction::MakeList {
6774 length: 2,
6775 dest: Register(0),
6776 },
6777 Instruction::IsList {
6778 source: Register(0),
6779 dest: Register(1),
6780 },
6781 Instruction::MakeList {
6783 length: 0,
6784 dest: Register(2),
6785 },
6786 Instruction::IsList {
6787 source: Register(2),
6788 dest: Register(3),
6789 },
6790 Instruction::LoadInt {
6792 value: 42,
6793 dest: Register(4),
6794 },
6795 Instruction::IsList {
6796 source: Register(4),
6797 dest: Register(5),
6798 },
6799 Instruction::End,
6800 ];
6801
6802 scheduler.spawn(program);
6803 run_to_idle(&mut scheduler);
6804
6805 let process = scheduler.processes.get(&Pid(0)).unwrap();
6806 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(1)); assert_eq!(process.registers[5], Value::Int(0)); }
6810
6811 #[test]
6812 fn test_is_pid() {
6813 let mut scheduler = Scheduler::new();
6814
6815 let program = vec![
6816 Instruction::Spawn {
6818 code: vec![Instruction::End],
6819 dest: Register(0),
6820 },
6821 Instruction::IsPid {
6822 source: Register(0),
6823 dest: Register(1),
6824 },
6825 Instruction::LoadInt {
6827 value: 42,
6828 dest: Register(2),
6829 },
6830 Instruction::IsPid {
6831 source: Register(2),
6832 dest: Register(3),
6833 },
6834 Instruction::End,
6835 ];
6836
6837 scheduler.spawn(program);
6838 run_to_idle(&mut scheduler);
6839
6840 let process = scheduler.processes.get(&Pid(0)).unwrap();
6841 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
6844
6845 #[test]
6846 fn test_is_function() {
6847 use crate::Module;
6848
6849 let mut scheduler = Scheduler::new();
6850
6851 let mut math = Module::new("math".to_string());
6853 math.add_function("id".to_string(), 1, vec![Instruction::Return]);
6854 math.export("id", 1);
6855 scheduler.load_module(math).unwrap();
6856
6857 let program = vec![
6858 Instruction::MakeFun {
6860 module: "math".to_string(),
6861 function: "id".to_string(),
6862 arity: 1,
6863 dest: Register(0),
6864 },
6865 Instruction::IsFunction {
6866 source: Register(0),
6867 dest: Register(1),
6868 },
6869 Instruction::LoadInt {
6871 value: 42,
6872 dest: Register(2),
6873 },
6874 Instruction::IsFunction {
6875 source: Register(2),
6876 dest: Register(3),
6877 },
6878 Instruction::End,
6879 ];
6880
6881 scheduler.spawn(program);
6882 run_to_idle(&mut scheduler);
6883
6884 let process = scheduler.processes.get(&Pid(0)).unwrap();
6885 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
6888
6889 #[test]
6890 fn test_is_function_closure() {
6891 use crate::Module;
6892
6893 let mut scheduler = Scheduler::new();
6894
6895 let mut math = Module::new("math".to_string());
6897 math.add_function(
6898 "add".to_string(),
6899 2,
6900 vec![
6901 Instruction::Add {
6902 a: Operand::Reg(Register(0)),
6903 b: Operand::Reg(Register(1)),
6904 dest: Register(0),
6905 },
6906 Instruction::Return,
6907 ],
6908 );
6909 math.export("add", 2);
6910 scheduler.load_module(math).unwrap();
6911
6912 let program = vec![
6913 Instruction::LoadInt {
6915 value: 5,
6916 dest: Register(1),
6917 },
6918 Instruction::MakeClosure {
6920 module: "math".to_string(),
6921 function: "add".to_string(),
6922 arity: 1,
6923 captures: vec![Register(1)],
6924 dest: Register(0),
6925 },
6926 Instruction::IsFunction {
6927 source: Register(0),
6928 dest: Register(2),
6929 },
6930 Instruction::End,
6931 ];
6932
6933 scheduler.spawn(program);
6934 run_to_idle(&mut scheduler);
6935
6936 let process = scheduler.processes.get(&Pid(0)).unwrap();
6937 assert_eq!(process.registers[2], Value::Int(1)); }
6939
6940 #[test]
6941 fn test_type_checks_comprehensive() {
6942 let mut scheduler = Scheduler::new();
6943
6944 let program = vec![
6946 Instruction::LoadInt {
6948 value: 42,
6949 dest: Register(0),
6950 },
6951 Instruction::Push {
6953 source: Operand::Int(1),
6954 },
6955 Instruction::MakeList {
6956 length: 1,
6957 dest: Register(1),
6958 },
6959 Instruction::IsInteger {
6961 source: Register(0),
6962 dest: Register(2),
6963 },
6964 Instruction::IsAtom {
6965 source: Register(0),
6966 dest: Register(3),
6967 },
6968 Instruction::IsTuple {
6969 source: Register(0),
6970 dest: Register(4),
6971 },
6972 Instruction::IsList {
6973 source: Register(0),
6974 dest: Register(5),
6975 },
6976 Instruction::IsPid {
6977 source: Register(0),
6978 dest: Register(6),
6979 },
6980 Instruction::End,
6981 ];
6982
6983 scheduler.spawn(program);
6984 run_to_idle(&mut scheduler);
6985
6986 let process = scheduler.processes.get(&Pid(0)).unwrap();
6987 assert_eq!(process.registers[2], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); assert_eq!(process.registers[4], Value::Int(0)); assert_eq!(process.registers[5], Value::Int(0)); assert_eq!(process.registers[6], Value::Int(0)); }
6993
6994 #[test]
6995 fn test_process_dictionary_put_get() {
6996 let mut scheduler = Scheduler::new();
6997
6998 let program = vec![
6999 Instruction::LoadAtom {
7001 name: "foo".into(),
7002 dest: Register(0),
7003 },
7004 Instruction::LoadInt {
7005 value: 42,
7006 dest: Register(1),
7007 },
7008 Instruction::PutDict {
7009 key: Register(0),
7010 value: Register(1),
7011 dest: Register(2), },
7013 Instruction::GetDict {
7015 key: Register(0),
7016 dest: Register(3),
7017 },
7018 Instruction::End,
7019 ];
7020
7021 scheduler.spawn(program);
7022 run_to_idle(&mut scheduler);
7023
7024 let process = scheduler.processes.get(&Pid(0)).unwrap();
7025 assert_eq!(process.registers[2], Value::None); assert_eq!(process.registers[3], Value::Int(42)); }
7028
7029 #[test]
7030 fn test_process_dictionary_overwrite() {
7031 let mut scheduler = Scheduler::new();
7032
7033 let program = vec![
7034 Instruction::LoadAtom {
7036 name: "bar".into(),
7037 dest: Register(0),
7038 },
7039 Instruction::LoadInt {
7040 value: 100,
7041 dest: Register(1),
7042 },
7043 Instruction::PutDict {
7044 key: Register(0),
7045 value: Register(1),
7046 dest: Register(2),
7047 },
7048 Instruction::LoadInt {
7050 value: 200,
7051 dest: Register(1),
7052 },
7053 Instruction::PutDict {
7054 key: Register(0),
7055 value: Register(1),
7056 dest: Register(3), },
7058 Instruction::GetDict {
7060 key: Register(0),
7061 dest: Register(4),
7062 },
7063 Instruction::End,
7064 ];
7065
7066 scheduler.spawn(program);
7067 run_to_idle(&mut scheduler);
7068
7069 let process = scheduler.processes.get(&Pid(0)).unwrap();
7070 assert_eq!(process.registers[2], Value::None); assert_eq!(process.registers[3], Value::Int(100)); assert_eq!(process.registers[4], Value::Int(200)); }
7074
7075 #[test]
7076 fn test_process_dictionary_erase() {
7077 let mut scheduler = Scheduler::new();
7078
7079 let program = vec![
7080 Instruction::LoadAtom {
7082 name: "temp".into(),
7083 dest: Register(0),
7084 },
7085 Instruction::LoadInt {
7086 value: 999,
7087 dest: Register(1),
7088 },
7089 Instruction::PutDict {
7090 key: Register(0),
7091 value: Register(1),
7092 dest: Register(2),
7093 },
7094 Instruction::EraseDict {
7096 key: Register(0),
7097 dest: Register(3), },
7099 Instruction::GetDict {
7101 key: Register(0),
7102 dest: Register(4), },
7104 Instruction::End,
7105 ];
7106
7107 scheduler.spawn(program);
7108 run_to_idle(&mut scheduler);
7109
7110 let process = scheduler.processes.get(&Pid(0)).unwrap();
7111 assert_eq!(process.registers[3], Value::Int(999)); assert_eq!(process.registers[4], Value::None); }
7114
7115 #[test]
7116 fn test_process_dictionary_get_keys() {
7117 let mut scheduler = Scheduler::new();
7118
7119 let program = vec![
7120 Instruction::LoadAtom {
7122 name: "key1".into(),
7123 dest: Register(0),
7124 },
7125 Instruction::LoadInt {
7126 value: 1,
7127 dest: Register(1),
7128 },
7129 Instruction::PutDict {
7130 key: Register(0),
7131 value: Register(1),
7132 dest: Register(7),
7133 },
7134 Instruction::LoadAtom {
7135 name: "key2".into(),
7136 dest: Register(2),
7137 },
7138 Instruction::LoadInt {
7139 value: 2,
7140 dest: Register(3),
7141 },
7142 Instruction::PutDict {
7143 key: Register(2),
7144 value: Register(3),
7145 dest: Register(7),
7146 },
7147 Instruction::GetDictKeys { dest: Register(4) },
7149 Instruction::End,
7150 ];
7151
7152 scheduler.spawn(program);
7153 run_to_idle(&mut scheduler);
7154
7155 let process = scheduler.processes.get(&Pid(0)).unwrap();
7156 match &process.registers[4] {
7157 Value::List(keys) => {
7158 assert_eq!(keys.len(), 2);
7159 assert!(
7161 keys.contains(&Value::Atom("key1".into()))
7162 && keys.contains(&Value::Atom("key2".into()))
7163 );
7164 }
7165 other => panic!("Expected List, got {:?}", other),
7166 }
7167 }
7168
7169 #[test]
7170 fn test_process_dictionary_get_missing() {
7171 let mut scheduler = Scheduler::new();
7172
7173 let program = vec![
7174 Instruction::LoadAtom {
7176 name: "nonexistent".into(),
7177 dest: Register(0),
7178 },
7179 Instruction::GetDict {
7180 key: Register(0),
7181 dest: Register(1),
7182 },
7183 Instruction::End,
7184 ];
7185
7186 scheduler.spawn(program);
7187 run_to_idle(&mut scheduler);
7188
7189 let process = scheduler.processes.get(&Pid(0)).unwrap();
7190 assert_eq!(process.registers[1], Value::None);
7191 }
7192
7193 #[test]
7196 fn test_make_map() {
7197 let mut scheduler = Scheduler::new();
7198
7199 let program = vec![
7200 Instruction::LoadAtom {
7202 name: "a".into(),
7203 dest: Register(0),
7204 },
7205 Instruction::Push {
7206 source: Operand::Reg(Register(0)),
7207 },
7208 Instruction::Push {
7209 source: Operand::Int(1),
7210 },
7211 Instruction::LoadAtom {
7212 name: "b".into(),
7213 dest: Register(0),
7214 },
7215 Instruction::Push {
7216 source: Operand::Reg(Register(0)),
7217 },
7218 Instruction::Push {
7219 source: Operand::Int(2),
7220 },
7221 Instruction::MakeMap {
7223 count: 2,
7224 dest: Register(1),
7225 },
7226 Instruction::End,
7227 ];
7228
7229 scheduler.spawn(program);
7230 run_to_idle(&mut scheduler);
7231
7232 let process = scheduler.processes.get(&Pid(0)).unwrap();
7233 match &process.registers[1] {
7234 Value::Map(m) => {
7235 assert_eq!(m.len(), 2);
7236 assert_eq!(m.get(&Value::Atom("a".into())), Some(&Value::Int(1)));
7237 assert_eq!(m.get(&Value::Atom("b".into())), Some(&Value::Int(2)));
7238 }
7239 other => panic!("Expected Map, got {:?}", other),
7240 }
7241 }
7242
7243 #[test]
7244 fn test_map_get() {
7245 let mut scheduler = Scheduler::new();
7246
7247 let program = vec![
7248 Instruction::LoadAtom {
7250 name: "x".into(),
7251 dest: Register(0),
7252 },
7253 Instruction::Push {
7254 source: Operand::Reg(Register(0)),
7255 },
7256 Instruction::Push {
7257 source: Operand::Int(42),
7258 },
7259 Instruction::MakeMap {
7260 count: 1,
7261 dest: Register(1),
7262 },
7263 Instruction::MapGet {
7265 map: Register(1),
7266 key: Register(0),
7267 dest: Register(2),
7268 },
7269 Instruction::End,
7270 ];
7271
7272 scheduler.spawn(program);
7273 run_to_idle(&mut scheduler);
7274
7275 let process = scheduler.processes.get(&Pid(0)).unwrap();
7276 assert_eq!(process.registers[2], Value::Int(42));
7277 }
7278
7279 #[test]
7280 fn test_map_get_default() {
7281 let mut scheduler = Scheduler::new();
7282
7283 let program = vec![
7284 Instruction::MakeMap {
7286 count: 0,
7287 dest: Register(0),
7288 },
7289 Instruction::LoadAtom {
7291 name: "missing".into(),
7292 dest: Register(1),
7293 },
7294 Instruction::LoadInt {
7295 value: -1,
7296 dest: Register(2),
7297 },
7298 Instruction::MapGetDefault {
7299 map: Register(0),
7300 key: Register(1),
7301 default: Register(2),
7302 dest: Register(3),
7303 },
7304 Instruction::End,
7305 ];
7306
7307 scheduler.spawn(program);
7308 run_to_idle(&mut scheduler);
7309
7310 let process = scheduler.processes.get(&Pid(0)).unwrap();
7311 assert_eq!(process.registers[3], Value::Int(-1));
7312 }
7313
7314 #[test]
7315 fn test_map_put() {
7316 let mut scheduler = Scheduler::new();
7317
7318 let program = vec![
7319 Instruction::MakeMap {
7321 count: 0,
7322 dest: Register(0),
7323 },
7324 Instruction::LoadAtom {
7326 name: "key".into(),
7327 dest: Register(1),
7328 },
7329 Instruction::LoadInt {
7330 value: 100,
7331 dest: Register(2),
7332 },
7333 Instruction::MapPut {
7334 map: Register(0),
7335 key: Register(1),
7336 value: Register(2),
7337 dest: Register(3),
7338 },
7339 Instruction::MapGet {
7341 map: Register(3),
7342 key: Register(1),
7343 dest: Register(4),
7344 },
7345 Instruction::MapSize {
7347 map: Register(0),
7348 dest: Register(5),
7349 },
7350 Instruction::MapSize {
7352 map: Register(3),
7353 dest: Register(6),
7354 },
7355 Instruction::End,
7356 ];
7357
7358 scheduler.spawn(program);
7359 run_to_idle(&mut scheduler);
7360
7361 let process = scheduler.processes.get(&Pid(0)).unwrap();
7362 assert_eq!(process.registers[4], Value::Int(100));
7363 assert_eq!(process.registers[5], Value::Int(0)); assert_eq!(process.registers[6], Value::Int(1)); }
7366
7367 #[test]
7368 fn test_map_remove() {
7369 let mut scheduler = Scheduler::new();
7370
7371 let program = vec![
7372 Instruction::LoadAtom {
7374 name: "rem".into(),
7375 dest: Register(0),
7376 },
7377 Instruction::Push {
7378 source: Operand::Reg(Register(0)),
7379 },
7380 Instruction::Push {
7381 source: Operand::Int(999),
7382 },
7383 Instruction::MakeMap {
7384 count: 1,
7385 dest: Register(1),
7386 },
7387 Instruction::MapRemove {
7389 map: Register(1),
7390 key: Register(0),
7391 dest: Register(2),
7392 },
7393 Instruction::MapSize {
7395 map: Register(1),
7396 dest: Register(3),
7397 },
7398 Instruction::MapSize {
7399 map: Register(2),
7400 dest: Register(4),
7401 },
7402 Instruction::End,
7403 ];
7404
7405 scheduler.spawn(program);
7406 run_to_idle(&mut scheduler);
7407
7408 let process = scheduler.processes.get(&Pid(0)).unwrap();
7409 assert_eq!(process.registers[3], Value::Int(1)); assert_eq!(process.registers[4], Value::Int(0)); }
7412
7413 #[test]
7414 fn test_map_has() {
7415 let mut scheduler = Scheduler::new();
7416
7417 let program = vec![
7418 Instruction::LoadAtom {
7420 name: "exists".into(),
7421 dest: Register(0),
7422 },
7423 Instruction::Push {
7424 source: Operand::Reg(Register(0)),
7425 },
7426 Instruction::Push {
7427 source: Operand::Int(1),
7428 },
7429 Instruction::MakeMap {
7430 count: 1,
7431 dest: Register(1),
7432 },
7433 Instruction::MapHas {
7435 map: Register(1),
7436 key: Register(0),
7437 dest: Register(2),
7438 },
7439 Instruction::LoadAtom {
7441 name: "missing".into(),
7442 dest: Register(3),
7443 },
7444 Instruction::MapHas {
7445 map: Register(1),
7446 key: Register(3),
7447 dest: Register(4),
7448 },
7449 Instruction::End,
7450 ];
7451
7452 scheduler.spawn(program);
7453 run_to_idle(&mut scheduler);
7454
7455 let process = scheduler.processes.get(&Pid(0)).unwrap();
7456 assert_eq!(process.registers[2], Value::Int(1)); assert_eq!(process.registers[4], Value::Int(0)); }
7459
7460 #[test]
7461 fn test_map_keys_values() {
7462 let mut scheduler = Scheduler::new();
7463
7464 let program = vec![
7465 Instruction::LoadAtom {
7467 name: "a".into(),
7468 dest: Register(0),
7469 },
7470 Instruction::Push {
7471 source: Operand::Reg(Register(0)),
7472 },
7473 Instruction::Push {
7474 source: Operand::Int(1),
7475 },
7476 Instruction::LoadAtom {
7477 name: "b".into(),
7478 dest: Register(0),
7479 },
7480 Instruction::Push {
7481 source: Operand::Reg(Register(0)),
7482 },
7483 Instruction::Push {
7484 source: Operand::Int(2),
7485 },
7486 Instruction::MakeMap {
7487 count: 2,
7488 dest: Register(1),
7489 },
7490 Instruction::MapKeys {
7492 map: Register(1),
7493 dest: Register(2),
7494 },
7495 Instruction::MapValues {
7496 map: Register(1),
7497 dest: Register(3),
7498 },
7499 Instruction::End,
7500 ];
7501
7502 scheduler.spawn(program);
7503 run_to_idle(&mut scheduler);
7504
7505 let process = scheduler.processes.get(&Pid(0)).unwrap();
7506 match &process.registers[2] {
7507 Value::List(keys) => {
7508 assert_eq!(keys.len(), 2);
7509 assert!(keys.contains(&Value::Atom("a".into())));
7510 assert!(keys.contains(&Value::Atom("b".into())));
7511 }
7512 other => panic!("Expected List, got {:?}", other),
7513 }
7514 match &process.registers[3] {
7515 Value::List(vals) => {
7516 assert_eq!(vals.len(), 2);
7517 assert!(vals.contains(&Value::Int(1)));
7518 assert!(vals.contains(&Value::Int(2)));
7519 }
7520 other => panic!("Expected List, got {:?}", other),
7521 }
7522 }
7523
7524 #[test]
7525 fn test_is_map() {
7526 let mut scheduler = Scheduler::new();
7527
7528 let program = vec![
7529 Instruction::MakeMap {
7531 count: 0,
7532 dest: Register(0),
7533 },
7534 Instruction::IsMap {
7536 source: Register(0),
7537 dest: Register(1),
7538 },
7539 Instruction::LoadInt {
7541 value: 42,
7542 dest: Register(2),
7543 },
7544 Instruction::IsMap {
7545 source: Register(2),
7546 dest: Register(3),
7547 },
7548 Instruction::End,
7549 ];
7550
7551 scheduler.spawn(program);
7552 run_to_idle(&mut scheduler);
7553
7554 let process = scheduler.processes.get(&Pid(0)).unwrap();
7555 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
7558
7559 #[test]
7560 fn test_map_pattern_matching() {
7561 let mut scheduler = Scheduler::new();
7562
7563 let program = vec![
7565 Instruction::LoadAtom {
7567 name: "name".into(),
7568 dest: Register(0),
7569 },
7570 Instruction::Push {
7571 source: Operand::Reg(Register(0)),
7572 },
7573 Instruction::LoadAtom {
7574 name: "Alice".into(),
7575 dest: Register(0),
7576 },
7577 Instruction::Push {
7578 source: Operand::Reg(Register(0)),
7579 },
7580 Instruction::LoadAtom {
7581 name: "age".into(),
7582 dest: Register(0),
7583 },
7584 Instruction::Push {
7585 source: Operand::Reg(Register(0)),
7586 },
7587 Instruction::Push {
7588 source: Operand::Int(30),
7589 },
7590 Instruction::MakeMap {
7591 count: 2,
7592 dest: Register(1),
7593 },
7594 Instruction::Match {
7596 source: Register(1),
7597 pattern: Pattern::Map(vec![(
7598 Pattern::Atom("age".into()),
7599 Pattern::Variable(Register(2)),
7600 )]),
7601 fail_target: 100, },
7603 Instruction::End,
7604 ];
7605
7606 scheduler.spawn(program);
7607 run_to_idle(&mut scheduler);
7608
7609 let process = scheduler.processes.get(&Pid(0)).unwrap();
7610 assert_eq!(process.registers[2], Value::Int(30));
7611 }
7612
7613 #[test]
7616 fn test_make_ref() {
7617 let mut scheduler = Scheduler::new();
7618
7619 let program = vec![
7620 Instruction::MakeRef { dest: Register(0) },
7621 Instruction::MakeRef { dest: Register(1) },
7622 Instruction::MakeRef { dest: Register(2) },
7623 Instruction::End,
7624 ];
7625
7626 scheduler.spawn(program);
7627 run_to_idle(&mut scheduler);
7628
7629 let process = scheduler.processes.get(&Pid(0)).unwrap();
7630
7631 match (
7633 &process.registers[0],
7634 &process.registers[1],
7635 &process.registers[2],
7636 ) {
7637 (Value::Ref(r0), Value::Ref(r1), Value::Ref(r2)) => {
7638 assert_ne!(r0, r1);
7639 assert_ne!(r1, r2);
7640 assert_ne!(r0, r2);
7641 }
7642 _ => panic!("Expected Ref values"),
7643 }
7644 }
7645
7646 #[test]
7647 fn test_refs_unique_across_processes() {
7648 let mut scheduler = Scheduler::new();
7649
7650 let program1 = vec![Instruction::MakeRef { dest: Register(0) }, Instruction::End];
7652
7653 let program2 = vec![Instruction::MakeRef { dest: Register(0) }, Instruction::End];
7655
7656 scheduler.spawn(program1);
7657 scheduler.spawn(program2);
7658 run_to_idle(&mut scheduler);
7659
7660 let p0 = scheduler.processes.get(&Pid(0)).unwrap();
7661 let p1 = scheduler.processes.get(&Pid(1)).unwrap();
7662
7663 match (&p0.registers[0], &p1.registers[0]) {
7665 (Value::Ref(r0), Value::Ref(r1)) => {
7666 assert_ne!(r0, r1);
7667 }
7668 _ => panic!("Expected Ref values"),
7669 }
7670 }
7671
7672 #[test]
7673 fn test_is_ref() {
7674 let mut scheduler = Scheduler::new();
7675
7676 let program = vec![
7677 Instruction::MakeRef { dest: Register(0) },
7679 Instruction::IsRef {
7681 source: Register(0),
7682 dest: Register(1),
7683 },
7684 Instruction::LoadInt {
7686 value: 42,
7687 dest: Register(2),
7688 },
7689 Instruction::IsRef {
7690 source: Register(2),
7691 dest: Register(3),
7692 },
7693 Instruction::End,
7694 ];
7695
7696 scheduler.spawn(program);
7697 run_to_idle(&mut scheduler);
7698
7699 let process = scheduler.processes.get(&Pid(0)).unwrap();
7700 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
7703
7704 #[test]
7705 fn test_ref_equality_via_copy() {
7706 let mut scheduler = Scheduler::new();
7707
7708 let program = vec![
7709 Instruction::MakeRef { dest: Register(0) },
7711 Instruction::Move {
7713 source: Register(0),
7714 dest: Register(1),
7715 },
7716 Instruction::End,
7717 ];
7718
7719 scheduler.spawn(program);
7720 run_to_idle(&mut scheduler);
7721
7722 let process = scheduler.processes.get(&Pid(0)).unwrap();
7723 assert_eq!(process.registers[0], process.registers[1]);
7725 }
7726
7727 #[test]
7728 fn test_ref_as_map_key() {
7729 let mut scheduler = Scheduler::new();
7730
7731 let program = vec![
7732 Instruction::MakeRef { dest: Register(0) },
7734 Instruction::Push {
7736 source: Operand::Reg(Register(0)),
7737 },
7738 Instruction::Push {
7739 source: Operand::Int(42),
7740 },
7741 Instruction::MakeMap {
7743 count: 1,
7744 dest: Register(1),
7745 },
7746 Instruction::MapGet {
7748 map: Register(1),
7749 key: Register(0),
7750 dest: Register(2),
7751 },
7752 Instruction::End,
7753 ];
7754
7755 scheduler.spawn(program);
7756 run_to_idle(&mut scheduler);
7757
7758 let process = scheduler.processes.get(&Pid(0)).unwrap();
7759 assert_eq!(process.registers[2], Value::Int(42));
7761 }
7762
7763 #[test]
7766 fn test_load_float() {
7767 let mut scheduler = Scheduler::new();
7768
7769 let program = vec![
7770 Instruction::LoadFloat {
7771 value: 3.14,
7772 dest: Register(0),
7773 },
7774 Instruction::LoadFloat {
7775 value: -2.5,
7776 dest: Register(1),
7777 },
7778 Instruction::End,
7779 ];
7780
7781 scheduler.spawn(program);
7782 run_to_idle(&mut scheduler);
7783
7784 let process = scheduler.processes.get(&Pid(0)).unwrap();
7785 assert_eq!(process.registers[0], Value::Float(3.14));
7786 assert_eq!(process.registers[1], Value::Float(-2.5));
7787 }
7788
7789 #[test]
7790 fn test_is_float() {
7791 let mut scheduler = Scheduler::new();
7792
7793 let program = vec![
7794 Instruction::LoadFloat {
7795 value: 1.5,
7796 dest: Register(0),
7797 },
7798 Instruction::IsFloat {
7799 source: Register(0),
7800 dest: Register(1),
7801 },
7802 Instruction::LoadInt {
7803 value: 42,
7804 dest: Register(2),
7805 },
7806 Instruction::IsFloat {
7807 source: Register(2),
7808 dest: Register(3),
7809 },
7810 Instruction::End,
7811 ];
7812
7813 scheduler.spawn(program);
7814 run_to_idle(&mut scheduler);
7815
7816 let process = scheduler.processes.get(&Pid(0)).unwrap();
7817 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
7820
7821 #[test]
7822 fn test_int_to_float() {
7823 let mut scheduler = Scheduler::new();
7824
7825 let program = vec![
7826 Instruction::LoadInt {
7827 value: 42,
7828 dest: Register(0),
7829 },
7830 Instruction::IntToFloat {
7831 source: Register(0),
7832 dest: Register(1),
7833 },
7834 Instruction::End,
7835 ];
7836
7837 scheduler.spawn(program);
7838 run_to_idle(&mut scheduler);
7839
7840 let process = scheduler.processes.get(&Pid(0)).unwrap();
7841 assert_eq!(process.registers[1], Value::Float(42.0));
7842 }
7843
7844 #[test]
7845 fn test_float_to_int() {
7846 let mut scheduler = Scheduler::new();
7847
7848 let program = vec![
7849 Instruction::LoadFloat {
7850 value: 3.7,
7851 dest: Register(0),
7852 },
7853 Instruction::FloatToInt {
7854 source: Register(0),
7855 dest: Register(1),
7856 },
7857 Instruction::LoadFloat {
7858 value: -2.9,
7859 dest: Register(2),
7860 },
7861 Instruction::FloatToInt {
7862 source: Register(2),
7863 dest: Register(3),
7864 },
7865 Instruction::End,
7866 ];
7867
7868 scheduler.spawn(program);
7869 run_to_idle(&mut scheduler);
7870
7871 let process = scheduler.processes.get(&Pid(0)).unwrap();
7872 assert_eq!(process.registers[1], Value::Int(3)); assert_eq!(process.registers[3], Value::Int(-2)); }
7875
7876 #[test]
7877 fn test_floor_ceil_round_trunc() {
7878 let mut scheduler = Scheduler::new();
7879
7880 let program = vec![
7881 Instruction::LoadFloat {
7882 value: 3.7,
7883 dest: Register(0),
7884 },
7885 Instruction::Floor {
7886 source: Register(0),
7887 dest: Register(1),
7888 },
7889 Instruction::Ceil {
7890 source: Register(0),
7891 dest: Register(2),
7892 },
7893 Instruction::Round {
7894 source: Register(0),
7895 dest: Register(3),
7896 },
7897 Instruction::Trunc {
7898 source: Register(0),
7899 dest: Register(4),
7900 },
7901 Instruction::End,
7902 ];
7903
7904 scheduler.spawn(program);
7905 run_to_idle(&mut scheduler);
7906
7907 let process = scheduler.processes.get(&Pid(0)).unwrap();
7908 assert_eq!(process.registers[1], Value::Float(3.0)); assert_eq!(process.registers[2], Value::Float(4.0)); assert_eq!(process.registers[3], Value::Float(4.0)); assert_eq!(process.registers[4], Value::Float(3.0)); }
7913
7914 #[test]
7915 fn test_sqrt() {
7916 let mut scheduler = Scheduler::new();
7917
7918 let program = vec![
7919 Instruction::LoadFloat {
7920 value: 16.0,
7921 dest: Register(0),
7922 },
7923 Instruction::Sqrt {
7924 source: Register(0),
7925 dest: Register(1),
7926 },
7927 Instruction::LoadInt {
7929 value: 25,
7930 dest: Register(2),
7931 },
7932 Instruction::Sqrt {
7933 source: Register(2),
7934 dest: Register(3),
7935 },
7936 Instruction::End,
7937 ];
7938
7939 scheduler.spawn(program);
7940 run_to_idle(&mut scheduler);
7941
7942 let process = scheduler.processes.get(&Pid(0)).unwrap();
7943 assert_eq!(process.registers[1], Value::Float(4.0));
7944 assert_eq!(process.registers[3], Value::Float(5.0));
7945 }
7946
7947 #[test]
7948 fn test_abs() {
7949 let mut scheduler = Scheduler::new();
7950
7951 let program = vec![
7952 Instruction::LoadFloat {
7953 value: -3.5,
7954 dest: Register(0),
7955 },
7956 Instruction::Abs {
7957 source: Register(0),
7958 dest: Register(1),
7959 },
7960 Instruction::LoadInt {
7961 value: -42,
7962 dest: Register(2),
7963 },
7964 Instruction::Abs {
7965 source: Register(2),
7966 dest: Register(3),
7967 },
7968 Instruction::End,
7969 ];
7970
7971 scheduler.spawn(program);
7972 run_to_idle(&mut scheduler);
7973
7974 let process = scheduler.processes.get(&Pid(0)).unwrap();
7975 assert_eq!(process.registers[1], Value::Float(3.5));
7976 assert_eq!(process.registers[3], Value::Int(42));
7977 }
7978
7979 #[test]
7980 fn test_pow() {
7981 let mut scheduler = Scheduler::new();
7982
7983 let program = vec![
7984 Instruction::LoadInt {
7986 value: 2,
7987 dest: Register(0),
7988 },
7989 Instruction::LoadInt {
7990 value: 3,
7991 dest: Register(1),
7992 },
7993 Instruction::Pow {
7994 base: Register(0),
7995 exp: Register(1),
7996 dest: Register(2),
7997 },
7998 Instruction::LoadFloat {
8000 value: 2.5,
8001 dest: Register(3),
8002 },
8003 Instruction::LoadInt {
8004 value: 2,
8005 dest: Register(4),
8006 },
8007 Instruction::Pow {
8008 base: Register(3),
8009 exp: Register(4),
8010 dest: Register(5),
8011 },
8012 Instruction::End,
8013 ];
8014
8015 scheduler.spawn(program);
8016 run_to_idle(&mut scheduler);
8017
8018 let process = scheduler.processes.get(&Pid(0)).unwrap();
8019 assert_eq!(process.registers[2], Value::Float(8.0));
8020 assert_eq!(process.registers[5], Value::Float(6.25));
8021 }
8022
8023 #[test]
8026 fn test_unlink() {
8027 let mut scheduler = Scheduler::new();
8028
8029 let worker = vec![
8031 Instruction::Work { amount: 100 },
8032 Instruction::Crash, ];
8034
8035 let spawner = vec![
8036 Instruction::SpawnLink {
8037 code: worker,
8038 dest: Register(0),
8039 },
8040 Instruction::Unlink {
8042 target: Source::Reg(Register(0)),
8043 },
8044 Instruction::Work { amount: 200 },
8046 Instruction::End,
8047 ];
8048
8049 scheduler.spawn(spawner);
8050 run_to_idle(&mut scheduler);
8051
8052 let spawner_process = scheduler.processes.get(&Pid(0)).unwrap();
8054 assert_eq!(spawner_process.status, ProcessStatus::Done);
8055
8056 let worker_process = scheduler.processes.get(&Pid(1)).unwrap();
8058 assert_eq!(worker_process.status, ProcessStatus::Crashed);
8059 }
8060
8061 #[test]
8062 fn test_monitor_returns_ref() {
8063 let mut scheduler = Scheduler::new();
8064
8065 let worker = vec![Instruction::End];
8066
8067 let observer = vec![
8068 Instruction::Spawn {
8069 code: worker,
8070 dest: Register(0),
8071 },
8072 Instruction::Monitor {
8073 target: Source::Reg(Register(0)),
8074 dest: Register(1),
8075 },
8076 Instruction::End,
8077 ];
8078
8079 scheduler.spawn(observer);
8080 run_to_idle(&mut scheduler);
8081
8082 let process = scheduler.processes.get(&Pid(0)).unwrap();
8083 match &process.registers[1] {
8085 Value::Ref(_) => {}
8086 other => panic!("Expected Ref, got {:?}", other),
8087 }
8088 }
8089
8090 #[test]
8091 fn test_demonitor() {
8092 let mut scheduler = Scheduler::new();
8093
8094 let worker = vec![Instruction::Work { amount: 100 }, Instruction::Crash];
8096
8097 let observer = vec![
8098 Instruction::Spawn {
8099 code: worker,
8100 dest: Register(0),
8101 },
8102 Instruction::Monitor {
8104 target: Source::Reg(Register(0)),
8105 dest: Register(1),
8106 },
8107 Instruction::Demonitor {
8109 monitor_ref: Register(1),
8110 },
8111 Instruction::ReceiveTimeout {
8113 dest: Register(2),
8114 timeout: 50,
8115 },
8116 Instruction::End,
8117 ];
8118
8119 scheduler.spawn(observer);
8120 run_to_idle(&mut scheduler);
8121
8122 let process = scheduler.processes.get(&Pid(0)).unwrap();
8124 assert_eq!(process.status, ProcessStatus::Done);
8125 assert_eq!(process.registers[2], Value::String("TIMEOUT".to_string()));
8127 }
8128
8129 #[test]
8130 fn test_multiple_monitors_same_target() {
8131 let mut scheduler = Scheduler::new();
8132
8133 let worker = vec![Instruction::End];
8134
8135 let observer = vec![
8136 Instruction::Spawn {
8137 code: worker,
8138 dest: Register(0),
8139 },
8140 Instruction::Monitor {
8142 target: Source::Reg(Register(0)),
8143 dest: Register(1),
8144 },
8145 Instruction::Monitor {
8146 target: Source::Reg(Register(0)),
8147 dest: Register(2),
8148 },
8149 Instruction::End,
8150 ];
8151
8152 scheduler.spawn(observer);
8153 run_to_idle(&mut scheduler);
8154
8155 let process = scheduler.processes.get(&Pid(0)).unwrap();
8156
8157 match (&process.registers[1], &process.registers[2]) {
8159 (Value::Ref(r1), Value::Ref(r2)) => {
8160 assert_ne!(r1, r2); }
8162 _ => panic!("Expected two Refs"),
8163 }
8164 }
8165
8166 #[test]
8167 fn test_exit_with_normal_reason() {
8168 let mut scheduler = Scheduler::new();
8170
8171 let child = vec![
8173 Instruction::LoadAtom {
8174 name: "normal".to_string(),
8175 dest: Register(0),
8176 },
8177 Instruction::Exit {
8178 reason: Register(0),
8179 },
8180 ];
8181
8182 let parent = vec![
8184 Instruction::SpawnLink {
8185 code: child,
8186 dest: Register(0),
8187 },
8188 Instruction::ReceiveTimeout {
8190 dest: Register(1),
8191 timeout: 10,
8192 },
8193 Instruction::End,
8194 ];
8195
8196 scheduler.spawn(parent);
8197 run_to_idle(&mut scheduler);
8198
8199 let parent_process = scheduler.processes.get(&Pid(0)).unwrap();
8201 assert_eq!(parent_process.status, ProcessStatus::Done);
8202 assert_eq!(
8204 parent_process.registers[1],
8205 Value::String("TIMEOUT".to_string())
8206 );
8207 }
8208
8209 #[test]
8210 fn test_exit_with_custom_reason() {
8211 let mut scheduler = Scheduler::new();
8213
8214 let child = vec![
8216 Instruction::LoadAtom {
8217 name: "shutdown".to_string(),
8218 dest: Register(0),
8219 },
8220 Instruction::Exit {
8221 reason: Register(0),
8222 },
8223 ];
8224
8225 let parent = vec![
8227 Instruction::TrapExit { enable: true },
8228 Instruction::SpawnLink {
8229 code: child,
8230 dest: Register(0),
8231 },
8232 Instruction::Receive { dest: Register(1) },
8233 Instruction::End,
8234 ];
8235
8236 scheduler.spawn(parent);
8237 run_to_idle(&mut scheduler);
8238
8239 let parent_process = scheduler.processes.get(&Pid(0)).unwrap();
8241 assert_eq!(parent_process.status, ProcessStatus::Done);
8242 match &parent_process.registers[1] {
8243 Value::Tuple(elems) => {
8244 assert_eq!(elems.len(), 3);
8245 assert_eq!(elems[0], Value::Atom("EXIT".to_string()));
8246 assert!(matches!(elems[1], Value::Pid(_)));
8247 assert_eq!(elems[2], Value::Atom("shutdown".to_string()));
8248 }
8249 _ => panic!("Expected EXIT tuple, got {:?}", parent_process.registers[1]),
8250 }
8251 }
8252
8253 #[test]
8254 fn test_link_crash_without_trap_exit() {
8255 let mut scheduler = Scheduler::new();
8257
8258 let child = vec![Instruction::Crash];
8260
8261 let parent = vec![
8263 Instruction::SpawnLink {
8264 code: child,
8265 dest: Register(0),
8266 },
8267 Instruction::Receive { dest: Register(1) },
8268 Instruction::End,
8269 ];
8270
8271 scheduler.spawn(parent);
8272 run_to_idle(&mut scheduler);
8273
8274 let parent_process = scheduler.processes.get(&Pid(0)).unwrap();
8276 assert_eq!(parent_process.status, ProcessStatus::Crashed);
8277 assert_eq!(
8278 parent_process.exit_reason,
8279 Value::Atom("crashed".to_string())
8280 );
8281 }
8282
8283 #[test]
8284 fn test_trap_exit_toggle() {
8285 let mut scheduler = Scheduler::new();
8287
8288 let worker = vec![Instruction::End];
8290
8291 let program = vec![
8293 Instruction::TrapExit { enable: true },
8294 Instruction::SpawnLink {
8295 code: worker,
8296 dest: Register(0),
8297 },
8298 Instruction::ReceiveTimeout {
8300 dest: Register(1),
8301 timeout: 10,
8302 },
8303 Instruction::End,
8304 ];
8305
8306 scheduler.spawn(program);
8307 run_to_idle(&mut scheduler);
8308
8309 let process = scheduler.processes.get(&Pid(0)).unwrap();
8311 assert!(process.trap_exit);
8312 assert_eq!(process.status, ProcessStatus::Done);
8313 }
8314
8315 #[test]
8316 fn test_exit_reason_stored_in_process() {
8317 let mut scheduler = Scheduler::new();
8319
8320 let program = vec![
8321 Instruction::LoadAtom {
8322 name: "my_reason".to_string(),
8323 dest: Register(0),
8324 },
8325 Instruction::Exit {
8326 reason: Register(0),
8327 },
8328 ];
8329
8330 scheduler.spawn(program);
8331 run_to_idle(&mut scheduler);
8332
8333 let process = scheduler.processes.get(&Pid(0)).unwrap();
8334 assert_eq!(process.status, ProcessStatus::Crashed); assert_eq!(process.exit_reason, Value::Atom("my_reason".to_string()));
8336 }
8337
8338 #[test]
8339 fn test_monitor_receives_exit_reason() {
8340 let mut scheduler = Scheduler::new();
8342
8343 let worker = vec![
8345 Instruction::LoadAtom {
8346 name: "killed".to_string(),
8347 dest: Register(0),
8348 },
8349 Instruction::Exit {
8350 reason: Register(0),
8351 },
8352 ];
8353
8354 let observer = vec![
8356 Instruction::Spawn {
8357 code: worker,
8358 dest: Register(0),
8359 },
8360 Instruction::Monitor {
8361 target: Source::Reg(Register(0)),
8362 dest: Register(2),
8363 },
8364 Instruction::Receive { dest: Register(1) },
8365 Instruction::End,
8366 ];
8367
8368 scheduler.spawn(observer);
8369 run_to_idle(&mut scheduler);
8370
8371 let observer_process = scheduler.processes.get(&Pid(0)).unwrap();
8373 match &observer_process.registers[1] {
8374 Value::Tuple(elems) => {
8375 assert_eq!(elems.len(), 5);
8376 assert_eq!(elems[0], Value::Atom("DOWN".to_string()));
8377 assert!(matches!(elems[1], Value::Ref(_)));
8378 assert_eq!(elems[2], Value::Atom("process".to_string()));
8379 assert!(matches!(elems[3], Value::Pid(_)));
8380 assert_eq!(elems[4], Value::Atom("killed".to_string()));
8381 }
8382 _ => panic!(
8383 "Expected DOWN tuple, got {:?}",
8384 observer_process.registers[1]
8385 ),
8386 }
8387 }
8388
8389 #[test]
8392 fn test_start_timer() {
8393 let mut scheduler = Scheduler::new();
8395
8396 let program = vec![
8397 Instruction::LoadAtom {
8399 name: "ping".to_string(),
8400 dest: Register(0),
8401 },
8402 Instruction::StartTimer {
8404 delay: 5,
8405 msg: Register(0),
8406 dest: Register(1),
8407 },
8408 Instruction::Receive { dest: Register(2) },
8410 Instruction::End,
8411 ];
8412
8413 scheduler.spawn(program);
8414 run_to_idle(&mut scheduler);
8415
8416 let process = scheduler.processes.get(&Pid(0)).unwrap();
8417 assert_eq!(process.status, ProcessStatus::Done);
8418
8419 assert!(matches!(process.registers[1], Value::Ref(_)));
8421
8422 match &process.registers[2] {
8424 Value::String(s) => {
8425 assert!(s.contains("timeout"));
8426 assert!(s.contains("ping"));
8427 }
8428 _ => panic!(
8429 "Expected timeout message string, got {:?}",
8430 process.registers[2]
8431 ),
8432 }
8433 }
8434
8435 #[test]
8436 fn test_send_after() {
8437 let mut scheduler = Scheduler::new();
8439
8440 let receiver = vec![Instruction::Receive { dest: Register(0) }, Instruction::End];
8442
8443 let sender = vec![
8445 Instruction::Spawn {
8446 code: receiver,
8447 dest: Register(0),
8448 },
8449 Instruction::LoadAtom {
8451 name: "hello".to_string(),
8452 dest: Register(1),
8453 },
8454 Instruction::SendAfter {
8456 delay: 5,
8457 to: Source::Reg(Register(0)),
8458 msg: Register(1),
8459 dest: Register(2),
8460 },
8461 Instruction::Work { amount: 10 },
8463 Instruction::End,
8464 ];
8465
8466 scheduler.spawn(sender);
8467 run_to_idle(&mut scheduler);
8468
8469 let receiver_process = scheduler.processes.get(&Pid(1)).unwrap();
8471 assert_eq!(receiver_process.status, ProcessStatus::Done);
8472 match &receiver_process.registers[0] {
8473 Value::String(s) => assert!(s.contains("hello")),
8474 _ => panic!("Expected message, got {:?}", receiver_process.registers[0]),
8475 }
8476 }
8477
8478 #[test]
8479 fn test_cancel_timer() {
8480 let mut scheduler = Scheduler::new();
8482
8483 let program = vec![
8484 Instruction::LoadAtom {
8486 name: "ping".to_string(),
8487 dest: Register(0),
8488 },
8489 Instruction::StartTimer {
8491 delay: 1000,
8492 msg: Register(0),
8493 dest: Register(1),
8494 },
8495 Instruction::CancelTimer {
8497 timer_ref: Register(1),
8498 dest: Register(2),
8499 },
8500 Instruction::ReceiveTimeout {
8502 dest: Register(3),
8503 timeout: 10,
8504 },
8505 Instruction::End,
8506 ];
8507
8508 scheduler.spawn(program);
8509 run_to_idle(&mut scheduler);
8510
8511 let process = scheduler.processes.get(&Pid(0)).unwrap();
8512 assert_eq!(process.status, ProcessStatus::Done);
8513
8514 match &process.registers[2] {
8516 Value::Int(remaining) => assert!(*remaining > 900),
8517 _ => panic!("Expected remaining time, got {:?}", process.registers[2]),
8518 }
8519
8520 assert_eq!(process.registers[3], Value::String("TIMEOUT".to_string()));
8522
8523 assert!(scheduler.timers.is_empty());
8525 }
8526
8527 #[test]
8528 fn test_read_timer() {
8529 let mut scheduler = Scheduler::new();
8531
8532 let program = vec![
8533 Instruction::LoadAtom {
8534 name: "ping".to_string(),
8535 dest: Register(0),
8536 },
8537 Instruction::StartTimer {
8538 delay: 100,
8539 msg: Register(0),
8540 dest: Register(1),
8541 },
8542 Instruction::ReadTimer {
8544 timer_ref: Register(1),
8545 dest: Register(2),
8546 },
8547 Instruction::End,
8548 ];
8549
8550 scheduler.spawn(program);
8551 run_to_idle(&mut scheduler);
8552
8553 let process = scheduler.processes.get(&Pid(0)).unwrap();
8554
8555 match &process.registers[2] {
8557 Value::Int(remaining) => assert!(*remaining > 0 && *remaining <= 100),
8558 _ => panic!("Expected remaining time, got {:?}", process.registers[2]),
8559 }
8560 }
8561
8562 #[test]
8563 fn test_timer_fires_after_delay() {
8564 let mut scheduler = Scheduler::new();
8566
8567 let program = vec![
8568 Instruction::LoadAtom {
8569 name: "tick".to_string(),
8570 dest: Register(0),
8571 },
8572 Instruction::StartTimer {
8574 delay: 10,
8575 msg: Register(0),
8576 dest: Register(1),
8577 },
8578 Instruction::Work { amount: 20 },
8580 Instruction::ReceiveTimeout {
8582 dest: Register(2),
8583 timeout: 5,
8584 },
8585 Instruction::End,
8586 ];
8587
8588 scheduler.spawn(program);
8589 run_to_idle(&mut scheduler);
8590
8591 let process = scheduler.processes.get(&Pid(0)).unwrap();
8592 assert_eq!(process.status, ProcessStatus::Done);
8593
8594 match &process.registers[2] {
8596 Value::String(s) => {
8597 assert!(s.contains("timeout") && s.contains("tick"));
8598 }
8599 _ => panic!("Expected timer message, got {:?}", process.registers[2]),
8600 }
8601 }
8602
8603 #[test]
8604 fn test_multiple_timers() {
8605 let mut scheduler = Scheduler::new();
8607
8608 let program = vec![
8609 Instruction::LoadInt {
8611 value: 1,
8612 dest: Register(0),
8613 },
8614 Instruction::StartTimer {
8615 delay: 10,
8616 msg: Register(0),
8617 dest: Register(4),
8618 },
8619 Instruction::LoadInt {
8620 value: 2,
8621 dest: Register(0),
8622 },
8623 Instruction::StartTimer {
8624 delay: 20,
8625 msg: Register(0),
8626 dest: Register(5),
8627 },
8628 Instruction::LoadInt {
8629 value: 3,
8630 dest: Register(0),
8631 },
8632 Instruction::StartTimer {
8633 delay: 30,
8634 msg: Register(0),
8635 dest: Register(6),
8636 },
8637 Instruction::Work { amount: 50 },
8639 Instruction::Receive { dest: Register(1) },
8641 Instruction::Receive { dest: Register(2) },
8642 Instruction::Receive { dest: Register(3) },
8643 Instruction::End,
8644 ];
8645
8646 scheduler.spawn(program);
8647 run_to_idle(&mut scheduler);
8648
8649 let process = scheduler.processes.get(&Pid(0)).unwrap();
8650 assert_eq!(process.status, ProcessStatus::Done);
8651
8652 assert!(matches!(process.registers[4], Value::Ref(_)));
8654 assert!(matches!(process.registers[5], Value::Ref(_)));
8655 assert!(matches!(process.registers[6], Value::Ref(_)));
8656 }
8657
8658 #[test]
8661 fn test_try_catch_basic() {
8662 let mut scheduler = Scheduler::new();
8664
8665 let program = vec![
8667 Instruction::Try {
8669 catch_target: 5,
8670 after_target: None,
8671 },
8672 Instruction::LoadAtom {
8674 name: "error".to_string(),
8675 dest: Register(0),
8676 },
8677 Instruction::LoadAtom {
8679 name: "oops".to_string(),
8680 dest: Register(1),
8681 },
8682 Instruction::Throw {
8684 class: Register(0),
8685 reason: Register(1),
8686 },
8687 Instruction::LoadInt {
8689 value: 999,
8690 dest: Register(2),
8691 },
8692 Instruction::GetException { dest: Register(3) },
8694 Instruction::ClearException,
8696 Instruction::LoadAtom {
8698 name: "caught".to_string(),
8699 dest: Register(4),
8700 },
8701 Instruction::End,
8703 ];
8704
8705 scheduler.spawn(program);
8706 run_to_idle(&mut scheduler);
8707
8708 let process = scheduler.processes.get(&Pid(0)).unwrap();
8709 assert_eq!(process.status, ProcessStatus::Done);
8710
8711 assert_ne!(process.registers[2], Value::Int(999));
8713
8714 assert_eq!(process.registers[4], Value::Atom("caught".to_string()));
8716
8717 match &process.registers[3] {
8719 Value::Tuple(elems) => {
8720 assert_eq!(elems.len(), 3);
8721 assert_eq!(elems[0], Value::Atom("error".to_string()));
8722 assert_eq!(elems[1], Value::Atom("oops".to_string()));
8723 }
8724 _ => panic!("Expected exception tuple, got {:?}", process.registers[3]),
8725 }
8726 }
8727
8728 #[test]
8729 fn test_try_no_exception() {
8730 let mut scheduler = Scheduler::new();
8732
8733 let program = vec![
8734 Instruction::Try {
8736 catch_target: 4,
8737 after_target: None,
8738 },
8739 Instruction::LoadInt {
8741 value: 42,
8742 dest: Register(0),
8743 },
8744 Instruction::EndTry,
8746 Instruction::Jump { target: 6 },
8748 Instruction::LoadAtom {
8750 name: "caught".to_string(),
8751 dest: Register(1),
8752 },
8753 Instruction::Jump { target: 6 },
8755 Instruction::End,
8757 ];
8758
8759 scheduler.spawn(program);
8760 run_to_idle(&mut scheduler);
8761
8762 let process = scheduler.processes.get(&Pid(0)).unwrap();
8763 assert_eq!(process.status, ProcessStatus::Done);
8764
8765 assert_eq!(process.registers[0], Value::Int(42));
8767
8768 assert_ne!(process.registers[1], Value::Atom("caught".to_string()));
8770 }
8771
8772 #[test]
8773 fn test_throw_without_catch() {
8774 let mut scheduler = Scheduler::new();
8776
8777 let program = vec![
8778 Instruction::LoadAtom {
8779 name: "throw".to_string(),
8780 dest: Register(0),
8781 },
8782 Instruction::LoadAtom {
8783 name: "uncaught".to_string(),
8784 dest: Register(1),
8785 },
8786 Instruction::Throw {
8787 class: Register(0),
8788 reason: Register(1),
8789 },
8790 Instruction::End,
8791 ];
8792
8793 scheduler.spawn(program);
8794 run_to_idle(&mut scheduler);
8795
8796 let process = scheduler.processes.get(&Pid(0)).unwrap();
8797 assert_eq!(process.status, ProcessStatus::Crashed);
8798
8799 match &process.exit_reason {
8801 Value::Tuple(elems) => {
8802 assert_eq!(elems.len(), 2);
8803 assert_eq!(elems[0], Value::Atom("throw".to_string()));
8804 assert_eq!(elems[1], Value::Atom("uncaught".to_string()));
8805 }
8806 _ => panic!("Expected exit reason tuple, got {:?}", process.exit_reason),
8807 }
8808 }
8809
8810 #[test]
8811 fn test_try_with_after() {
8812 let mut scheduler = Scheduler::new();
8814
8815 let program = vec![
8816 Instruction::Try {
8818 catch_target: 4,
8819 after_target: Some(6),
8820 },
8821 Instruction::LoadInt {
8823 value: 1,
8824 dest: Register(0),
8825 },
8826 Instruction::EndTry,
8828 Instruction::LoadInt {
8830 value: 999,
8831 dest: Register(3),
8832 },
8833 Instruction::LoadAtom {
8835 name: "caught".to_string(),
8836 dest: Register(1),
8837 },
8838 Instruction::Jump { target: 6 },
8840 Instruction::LoadAtom {
8842 name: "cleanup".to_string(),
8843 dest: Register(2),
8844 },
8845 Instruction::End,
8847 ];
8848
8849 scheduler.spawn(program);
8850 run_to_idle(&mut scheduler);
8851
8852 let process = scheduler.processes.get(&Pid(0)).unwrap();
8853 assert_eq!(process.status, ProcessStatus::Done);
8854
8855 assert_eq!(process.registers[0], Value::Int(1));
8857
8858 assert_ne!(process.registers[1], Value::Atom("caught".to_string()));
8860
8861 assert_eq!(process.registers[2], Value::Atom("cleanup".to_string()));
8863
8864 assert_ne!(process.registers[3], Value::Int(999));
8866 }
8867
8868 #[test]
8869 fn test_reraise() {
8870 let mut scheduler = Scheduler::new();
8872
8873 let program = vec![
8874 Instruction::Try {
8876 catch_target: 9,
8877 after_target: None,
8878 },
8879 Instruction::Try {
8881 catch_target: 6,
8882 after_target: None,
8883 },
8884 Instruction::LoadAtom {
8886 name: "error".to_string(),
8887 dest: Register(0),
8888 },
8889 Instruction::LoadAtom {
8891 name: "inner".to_string(),
8892 dest: Register(1),
8893 },
8894 Instruction::Throw {
8896 class: Register(0),
8897 reason: Register(1),
8898 },
8899 Instruction::End,
8901 Instruction::LoadAtom {
8903 name: "inner_caught".to_string(),
8904 dest: Register(2),
8905 },
8906 Instruction::Reraise,
8908 Instruction::End,
8910 Instruction::LoadAtom {
8912 name: "outer_caught".to_string(),
8913 dest: Register(3),
8914 },
8915 Instruction::End,
8917 ];
8918
8919 scheduler.spawn(program);
8920 run_to_idle(&mut scheduler);
8921
8922 let process = scheduler.processes.get(&Pid(0)).unwrap();
8923 assert_eq!(process.status, ProcessStatus::Done);
8924
8925 assert_eq!(
8927 process.registers[2],
8928 Value::Atom("inner_caught".to_string())
8929 );
8930 assert_eq!(
8931 process.registers[3],
8932 Value::Atom("outer_caught".to_string())
8933 );
8934 }
8935
8936 #[test]
8937 fn test_nested_try() {
8938 let mut scheduler = Scheduler::new();
8940
8941 let program = vec![
8942 Instruction::Try {
8944 catch_target: 7,
8945 after_target: None,
8946 },
8947 Instruction::Try {
8949 catch_target: 5,
8950 after_target: None,
8951 },
8952 Instruction::LoadAtom {
8954 name: "error".to_string(),
8955 dest: Register(0),
8956 },
8957 Instruction::LoadAtom {
8958 name: "inner_error".to_string(),
8959 dest: Register(1),
8960 },
8961 Instruction::Throw {
8962 class: Register(0),
8963 reason: Register(1),
8964 },
8965 Instruction::LoadAtom {
8967 name: "handled".to_string(),
8968 dest: Register(2),
8969 },
8970 Instruction::ClearException,
8971 Instruction::End,
8975 ];
8976
8977 scheduler.spawn(program);
8978 run_to_idle(&mut scheduler);
8979
8980 let process = scheduler.processes.get(&Pid(0)).unwrap();
8981 assert_eq!(process.status, ProcessStatus::Done);
8982
8983 assert_eq!(process.registers[2], Value::Atom("handled".to_string()));
8985 }
8986
8987 #[test]
8990 fn test_make_binary() {
8991 let mut scheduler = Scheduler::new();
8992
8993 let program = vec![
8994 Instruction::MakeBinary {
8995 bytes: vec![1, 2, 3, 255],
8996 dest: Register(0),
8997 },
8998 Instruction::End,
8999 ];
9000
9001 scheduler.spawn(program);
9002 run_to_idle(&mut scheduler);
9003
9004 let process = scheduler.processes.get(&Pid(0)).unwrap();
9005 assert_eq!(process.registers[0], Value::Binary(vec![1, 2, 3, 255]));
9006 }
9007
9008 #[test]
9009 fn test_binary_size() {
9010 let mut scheduler = Scheduler::new();
9011
9012 let program = vec![
9013 Instruction::MakeBinary {
9014 bytes: vec![1, 2, 3, 4, 5],
9015 dest: Register(0),
9016 },
9017 Instruction::BinarySize {
9018 bin: Register(0),
9019 dest: Register(1),
9020 },
9021 Instruction::End,
9022 ];
9023
9024 scheduler.spawn(program);
9025 run_to_idle(&mut scheduler);
9026
9027 let process = scheduler.processes.get(&Pid(0)).unwrap();
9028 assert_eq!(process.registers[1], Value::Int(5));
9029 }
9030
9031 #[test]
9032 fn test_binary_at() {
9033 let mut scheduler = Scheduler::new();
9034
9035 let program = vec![
9036 Instruction::MakeBinary {
9037 bytes: vec![10, 20, 30, 40],
9038 dest: Register(0),
9039 },
9040 Instruction::LoadInt {
9041 value: 2,
9042 dest: Register(1),
9043 },
9044 Instruction::BinaryAt {
9045 bin: Register(0),
9046 index: Register(1),
9047 dest: Register(2),
9048 },
9049 Instruction::End,
9050 ];
9051
9052 scheduler.spawn(program);
9053 run_to_idle(&mut scheduler);
9054
9055 let process = scheduler.processes.get(&Pid(0)).unwrap();
9056 assert_eq!(process.registers[2], Value::Int(30)); }
9058
9059 #[test]
9060 fn test_binary_at_out_of_bounds() {
9061 let mut scheduler = Scheduler::new();
9062
9063 let program = vec![
9064 Instruction::MakeBinary {
9065 bytes: vec![1, 2, 3],
9066 dest: Register(0),
9067 },
9068 Instruction::LoadInt {
9069 value: 10, dest: Register(1),
9071 },
9072 Instruction::BinaryAt {
9073 bin: Register(0),
9074 index: Register(1),
9075 dest: Register(2),
9076 },
9077 Instruction::End,
9078 ];
9079
9080 scheduler.spawn(program);
9081 run_to_idle(&mut scheduler);
9082
9083 let process = scheduler.processes.get(&Pid(0)).unwrap();
9084 assert_eq!(process.status, ProcessStatus::Crashed);
9085 }
9086
9087 #[test]
9088 fn test_binary_slice() {
9089 let mut scheduler = Scheduler::new();
9090
9091 let program = vec![
9092 Instruction::MakeBinary {
9093 bytes: vec![0, 1, 2, 3, 4, 5],
9094 dest: Register(0),
9095 },
9096 Instruction::LoadInt {
9097 value: 2, dest: Register(1),
9099 },
9100 Instruction::LoadInt {
9101 value: 3, dest: Register(2),
9103 },
9104 Instruction::BinarySlice {
9105 bin: Register(0),
9106 start: Register(1),
9107 len: Register(2),
9108 dest: Register(3),
9109 },
9110 Instruction::End,
9111 ];
9112
9113 scheduler.spawn(program);
9114 run_to_idle(&mut scheduler);
9115
9116 let process = scheduler.processes.get(&Pid(0)).unwrap();
9117 assert_eq!(process.registers[3], Value::Binary(vec![2, 3, 4]));
9118 }
9119
9120 #[test]
9121 fn test_binary_concat() {
9122 let mut scheduler = Scheduler::new();
9123
9124 let program = vec![
9125 Instruction::MakeBinary {
9126 bytes: vec![1, 2, 3],
9127 dest: Register(0),
9128 },
9129 Instruction::MakeBinary {
9130 bytes: vec![4, 5],
9131 dest: Register(1),
9132 },
9133 Instruction::BinaryConcat {
9134 a: Register(0),
9135 b: Register(1),
9136 dest: Register(2),
9137 },
9138 Instruction::End,
9139 ];
9140
9141 scheduler.spawn(program);
9142 run_to_idle(&mut scheduler);
9143
9144 let process = scheduler.processes.get(&Pid(0)).unwrap();
9145 assert_eq!(process.registers[2], Value::Binary(vec![1, 2, 3, 4, 5]));
9146 }
9147
9148 #[test]
9149 fn test_is_binary() {
9150 let mut scheduler = Scheduler::new();
9151
9152 let program = vec![
9153 Instruction::MakeBinary {
9154 bytes: vec![1, 2, 3],
9155 dest: Register(0),
9156 },
9157 Instruction::IsBinary {
9158 source: Register(0),
9159 dest: Register(1),
9160 },
9161 Instruction::LoadInt {
9162 value: 42,
9163 dest: Register(2),
9164 },
9165 Instruction::IsBinary {
9166 source: Register(2),
9167 dest: Register(3),
9168 },
9169 Instruction::End,
9170 ];
9171
9172 scheduler.spawn(program);
9173 run_to_idle(&mut scheduler);
9174
9175 let process = scheduler.processes.get(&Pid(0)).unwrap();
9176 assert_eq!(process.registers[1], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(0)); }
9179
9180 #[test]
9181 fn test_binary_to_string() {
9182 let mut scheduler = Scheduler::new();
9183
9184 let program = vec![
9185 Instruction::MakeBinary {
9186 bytes: "hello".as_bytes().to_vec(),
9187 dest: Register(0),
9188 },
9189 Instruction::BinaryToString {
9190 source: Register(0),
9191 dest: Register(1),
9192 },
9193 Instruction::End,
9194 ];
9195
9196 scheduler.spawn(program);
9197 run_to_idle(&mut scheduler);
9198
9199 let process = scheduler.processes.get(&Pid(0)).unwrap();
9200 assert_eq!(process.registers[1], Value::String("hello".to_string()));
9201 }
9202
9203 #[test]
9204 fn test_binary_to_string_invalid_utf8() {
9205 let mut scheduler = Scheduler::new();
9206
9207 let program = vec![
9208 Instruction::MakeBinary {
9209 bytes: vec![0xFF, 0xFE], dest: Register(0),
9211 },
9212 Instruction::BinaryToString {
9213 source: Register(0),
9214 dest: Register(1),
9215 },
9216 Instruction::End,
9217 ];
9218
9219 scheduler.spawn(program);
9220 run_to_idle(&mut scheduler);
9221
9222 let process = scheduler.processes.get(&Pid(0)).unwrap();
9223 assert_eq!(process.status, ProcessStatus::Crashed);
9224 }
9225
9226 #[test]
9227 fn test_binary_pattern_matching() {
9228 let mut scheduler = Scheduler::new();
9229
9230 let program = vec![
9231 Instruction::MakeBinary {
9233 bytes: vec![1, 2, 3],
9234 dest: Register(0),
9235 },
9236 Instruction::Match {
9238 source: Register(0),
9239 pattern: Pattern::Binary(vec![1, 2, 3]),
9240 fail_target: 4,
9241 },
9242 Instruction::LoadInt {
9244 value: 100,
9245 dest: Register(1),
9246 },
9247 Instruction::End,
9248 Instruction::LoadInt {
9250 value: 0,
9251 dest: Register(1),
9252 },
9253 Instruction::End,
9254 ];
9255
9256 scheduler.spawn(program);
9257 run_to_idle(&mut scheduler);
9258
9259 let process = scheduler.processes.get(&Pid(0)).unwrap();
9260 assert_eq!(process.registers[1], Value::Int(100)); }
9262
9263 #[test]
9264 fn test_binary_pattern_mismatch() {
9265 let mut scheduler = Scheduler::new();
9266
9267 let program = vec![
9268 Instruction::MakeBinary {
9270 bytes: vec![1, 2, 3],
9271 dest: Register(0),
9272 },
9273 Instruction::Match {
9275 source: Register(0),
9276 pattern: Pattern::Binary(vec![4, 5, 6]),
9277 fail_target: 4,
9278 },
9279 Instruction::LoadInt {
9281 value: 100,
9282 dest: Register(1),
9283 },
9284 Instruction::End,
9285 Instruction::LoadInt {
9287 value: 0,
9288 dest: Register(1),
9289 },
9290 Instruction::End,
9291 ];
9292
9293 scheduler.spawn(program);
9294 run_to_idle(&mut scheduler);
9295
9296 let process = scheduler.processes.get(&Pid(0)).unwrap();
9297 assert_eq!(process.registers[1], Value::Int(0)); }
9299
9300 #[test]
9303 fn test_println() {
9304 let mut scheduler = Scheduler::new();
9305
9306 let program = vec![
9307 Instruction::LoadInt {
9308 value: 42,
9309 dest: Register(0),
9310 },
9311 Instruction::PrintLn {
9312 source: Register(0),
9313 },
9314 Instruction::End,
9315 ];
9316
9317 scheduler.spawn(program);
9318 run_to_idle(&mut scheduler);
9319
9320 assert_eq!(scheduler.output.len(), 1);
9322 assert!(scheduler.output[0].contains("42"));
9323 }
9324
9325 #[test]
9326 fn test_readline_eof_in_tests() {
9327 let mut scheduler = Scheduler::new();
9328
9329 let program = vec![
9330 Instruction::ReadLine { dest: Register(0) },
9331 Instruction::End,
9332 ];
9333
9334 scheduler.spawn(program);
9335 run_to_idle(&mut scheduler);
9336
9337 let process = scheduler.processes.get(&Pid(0)).unwrap();
9339 assert_eq!(process.registers[0], Value::Atom("eof".to_string()));
9340 }
9341
9342 #[test]
9343 fn test_file_exists() {
9344 let mut scheduler = Scheduler::new();
9345
9346 let program = vec![
9348 Instruction::MakeBinary {
9349 bytes: "Cargo.toml".as_bytes().to_vec(),
9350 dest: Register(0),
9351 },
9352 Instruction::BinaryToString {
9353 source: Register(0),
9354 dest: Register(0),
9355 },
9356 Instruction::FileExists {
9357 path: Register(0),
9358 dest: Register(1),
9359 },
9360 Instruction::End,
9361 ];
9362
9363 scheduler.spawn(program);
9364 run_to_idle(&mut scheduler);
9365
9366 let process = scheduler.processes.get(&Pid(0)).unwrap();
9367 assert_eq!(process.registers[1], Value::Int(1)); }
9369
9370 #[test]
9373 fn test_self_pid() {
9374 let mut scheduler = Scheduler::new();
9375
9376 let program = vec![Instruction::SelfPid { dest: Register(0) }, Instruction::End];
9377
9378 scheduler.spawn(program);
9379 run_to_idle(&mut scheduler);
9380
9381 let process = scheduler.processes.get(&Pid(0)).unwrap();
9382 assert_eq!(process.registers[0], Value::Pid(Pid(0)));
9383 }
9384
9385 #[test]
9386 fn test_process_count() {
9387 let mut scheduler = Scheduler::new();
9388
9389 let child_code = vec![Instruction::Work { amount: 100 }, Instruction::End];
9391
9392 let program = vec![
9393 Instruction::Spawn {
9394 code: child_code.clone(),
9395 dest: Register(0),
9396 },
9397 Instruction::Spawn {
9398 code: child_code.clone(),
9399 dest: Register(1),
9400 },
9401 Instruction::ProcessCount { dest: Register(2) },
9402 Instruction::End,
9403 ];
9404
9405 scheduler.spawn(program);
9406 scheduler.step(10);
9408
9409 let process = scheduler.processes.get(&Pid(0)).unwrap();
9410 assert_eq!(process.registers[2], Value::Int(3));
9412 }
9413
9414 #[test]
9415 fn test_process_list() {
9416 let mut scheduler = Scheduler::new();
9417
9418 let child_code = vec![Instruction::Work { amount: 100 }, Instruction::End];
9419
9420 let program = vec![
9421 Instruction::Spawn {
9422 code: child_code,
9423 dest: Register(0),
9424 },
9425 Instruction::ProcessList { dest: Register(1) },
9426 Instruction::End,
9427 ];
9428
9429 scheduler.spawn(program);
9430 scheduler.step(10);
9431
9432 let process = scheduler.processes.get(&Pid(0)).unwrap();
9433 if let Value::List(pids) = &process.registers[1] {
9434 assert_eq!(pids.len(), 2); } else {
9436 panic!("expected list");
9437 }
9438 }
9439
9440 #[test]
9441 fn test_is_alive() {
9442 let mut scheduler = Scheduler::new();
9443
9444 let program = vec![
9445 Instruction::SelfPid { dest: Register(0) },
9446 Instruction::IsAlive {
9447 pid: Register(0),
9448 dest: Register(1),
9449 },
9450 Instruction::LoadInt {
9452 value: 9999,
9453 dest: Register(2),
9454 },
9455 Instruction::End,
9457 ];
9458
9459 scheduler.spawn(program);
9460 run_to_idle(&mut scheduler);
9461
9462 let process = scheduler.processes.get(&Pid(0)).unwrap();
9463 assert_eq!(process.registers[1], Value::Int(1)); }
9465
9466 #[test]
9467 fn test_process_info() {
9468 let mut scheduler = Scheduler::new();
9469
9470 let program = vec![
9471 Instruction::SelfPid { dest: Register(0) },
9472 Instruction::ProcessInfo {
9473 pid: Register(0),
9474 dest: Register(1),
9475 },
9476 Instruction::End,
9477 ];
9478
9479 scheduler.spawn(program);
9480 run_to_idle(&mut scheduler);
9481
9482 let process = scheduler.processes.get(&Pid(0)).unwrap();
9483 if let Value::Tuple(info) = &process.registers[1] {
9484 assert_eq!(info.len(), 5);
9485 assert!(matches!(&info[0], Value::Atom(_)));
9487 } else {
9488 panic!("expected tuple");
9489 }
9490 }
9491
9492 #[test]
9493 fn test_module_list() {
9494 let mut scheduler = Scheduler::new();
9495
9496 let module = crate::Module {
9498 name: "test_mod".to_string(),
9499 code: vec![Instruction::End],
9500 functions: std::collections::HashMap::new(),
9501 exports: std::collections::HashSet::new(),
9502 };
9503 scheduler.load_module(module).unwrap();
9504
9505 let program = vec![
9506 Instruction::ModuleList { dest: Register(0) },
9507 Instruction::End,
9508 ];
9509
9510 scheduler.spawn(program);
9511 run_to_idle(&mut scheduler);
9512
9513 let process = scheduler.processes.get(&Pid(0)).unwrap();
9514 if let Value::List(modules) = &process.registers[0] {
9515 assert!(modules.contains(&Value::Atom("test_mod".to_string())));
9516 } else {
9517 panic!("expected list");
9518 }
9519 }
9520
9521 #[test]
9522 fn test_function_exported() {
9523 let mut scheduler = Scheduler::new();
9524
9525 let mut functions = std::collections::HashMap::new();
9527 functions.insert(
9528 ("my_func".to_string(), 0),
9529 crate::FunctionDef {
9530 name: "my_func".to_string(),
9531 arity: 0,
9532 entry: 0,
9533 },
9534 );
9535 let mut exports = std::collections::HashSet::new();
9536 exports.insert(("my_func".to_string(), 0));
9537
9538 let module = crate::Module {
9539 name: "test_mod".to_string(),
9540 code: vec![Instruction::End],
9541 functions,
9542 exports,
9543 };
9544 scheduler.load_module(module).unwrap();
9545
9546 let program = vec![
9547 Instruction::LoadAtom {
9548 name: "test_mod".to_string(),
9549 dest: Register(0),
9550 },
9551 Instruction::LoadAtom {
9552 name: "my_func".to_string(),
9553 dest: Register(1),
9554 },
9555 Instruction::LoadInt {
9556 value: 0,
9557 dest: Register(2),
9558 },
9559 Instruction::FunctionExported {
9560 module: Register(0),
9561 function: Register(1),
9562 arity: Register(2),
9563 dest: Register(3),
9564 },
9565 Instruction::LoadAtom {
9567 name: "no_func".to_string(),
9568 dest: Register(4),
9569 },
9570 Instruction::FunctionExported {
9571 module: Register(0),
9572 function: Register(4),
9573 arity: Register(2),
9574 dest: Register(5),
9575 },
9576 Instruction::End,
9577 ];
9578
9579 scheduler.spawn(program);
9580 run_to_idle(&mut scheduler);
9581
9582 let process = scheduler.processes.get(&Pid(0)).unwrap();
9583 assert_eq!(process.registers[3], Value::Int(1)); assert_eq!(process.registers[5], Value::Int(0)); }
9586
9587 #[test]
9590 fn test_add_overflow_promotes_to_bigint() {
9591 let mut scheduler = Scheduler::new();
9592
9593 let program = vec![
9595 Instruction::LoadInt {
9596 value: i64::MAX,
9597 dest: Register(0),
9598 },
9599 Instruction::Add {
9600 a: Operand::Reg(Register(0)),
9601 b: Operand::Int(1),
9602 dest: Register(1),
9603 },
9604 Instruction::End,
9605 ];
9606
9607 scheduler.spawn(program);
9608 run_to_idle(&mut scheduler);
9609
9610 let process = scheduler.processes.get(&Pid(0)).unwrap();
9611 let expected = BigInt::from(i64::MAX) + BigInt::from(1);
9613 assert_eq!(process.registers[1], Value::BigInt(expected));
9614 }
9615
9616 #[test]
9617 fn test_sub_overflow_promotes_to_bigint() {
9618 let mut scheduler = Scheduler::new();
9619
9620 let program = vec![
9622 Instruction::LoadInt {
9623 value: i64::MIN,
9624 dest: Register(0),
9625 },
9626 Instruction::Sub {
9627 a: Operand::Reg(Register(0)),
9628 b: Operand::Int(1),
9629 dest: Register(1),
9630 },
9631 Instruction::End,
9632 ];
9633
9634 scheduler.spawn(program);
9635 run_to_idle(&mut scheduler);
9636
9637 let process = scheduler.processes.get(&Pid(0)).unwrap();
9638 let expected = BigInt::from(i64::MIN) - BigInt::from(1);
9640 assert_eq!(process.registers[1], Value::BigInt(expected));
9641 }
9642
9643 #[test]
9644 fn test_mul_overflow_promotes_to_bigint() {
9645 let mut scheduler = Scheduler::new();
9646
9647 let program = vec![
9649 Instruction::LoadInt {
9650 value: i64::MAX / 2 + 1,
9651 dest: Register(0),
9652 },
9653 Instruction::Mul {
9654 a: Operand::Reg(Register(0)),
9655 b: Operand::Int(3),
9656 dest: Register(1),
9657 },
9658 Instruction::End,
9659 ];
9660
9661 scheduler.spawn(program);
9662 run_to_idle(&mut scheduler);
9663
9664 let process = scheduler.processes.get(&Pid(0)).unwrap();
9665 let val = i64::MAX / 2 + 1;
9667 let expected = BigInt::from(val) * BigInt::from(3);
9668 assert_eq!(process.registers[1], Value::BigInt(expected));
9669 }
9670
9671 #[test]
9672 fn test_bigint_to_int_normalization() {
9673 let mut scheduler = Scheduler::new();
9674
9675 let program = vec![
9677 Instruction::LoadInt {
9678 value: i64::MAX,
9679 dest: Register(0),
9680 },
9681 Instruction::Add {
9682 a: Operand::Reg(Register(0)),
9683 b: Operand::Int(1),
9684 dest: Register(1),
9685 },
9686 Instruction::Sub {
9688 a: Operand::Reg(Register(1)),
9689 b: Operand::Int(1),
9690 dest: Register(2),
9691 },
9692 Instruction::End,
9693 ];
9694
9695 scheduler.spawn(program);
9696 run_to_idle(&mut scheduler);
9697
9698 let process = scheduler.processes.get(&Pid(0)).unwrap();
9699 assert_eq!(process.registers[2], Value::Int(i64::MAX));
9701 }
9702
9703 #[test]
9704 fn test_bigint_comparison() {
9705 let mut scheduler = Scheduler::new();
9706
9707 let program = vec![
9709 Instruction::LoadInt {
9711 value: i64::MAX,
9712 dest: Register(0),
9713 },
9714 Instruction::Add {
9715 a: Operand::Reg(Register(0)),
9716 b: Operand::Int(1),
9717 dest: Register(1), },
9719 Instruction::Add {
9720 a: Operand::Reg(Register(0)),
9721 b: Operand::Int(2),
9722 dest: Register(2), },
9724 Instruction::Lt {
9726 a: Operand::Reg(Register(1)),
9727 b: Operand::Reg(Register(2)),
9728 dest: Register(3),
9729 },
9730 Instruction::Gt {
9732 a: Operand::Reg(Register(2)),
9733 b: Operand::Reg(Register(1)),
9734 dest: Register(4),
9735 },
9736 Instruction::Eq {
9738 a: Operand::Reg(Register(1)),
9739 b: Operand::Reg(Register(1)),
9740 dest: Register(5),
9741 },
9742 Instruction::End,
9743 ];
9744
9745 scheduler.spawn(program);
9746 run_to_idle(&mut scheduler);
9747
9748 let process = scheduler.processes.get(&Pid(0)).unwrap();
9749 assert_eq!(process.registers[3], Value::Int(1)); assert_eq!(process.registers[4], Value::Int(1)); assert_eq!(process.registers[5], Value::Int(1)); }
9753
9754 #[test]
9755 fn test_bigint_div() {
9756 let mut scheduler = Scheduler::new();
9757
9758 let program = vec![
9760 Instruction::LoadInt {
9761 value: i64::MAX,
9762 dest: Register(0),
9763 },
9764 Instruction::Add {
9765 a: Operand::Reg(Register(0)),
9766 b: Operand::Reg(Register(0)),
9767 dest: Register(1), },
9769 Instruction::Div {
9770 a: Operand::Reg(Register(1)),
9771 b: Operand::Int(2),
9772 dest: Register(2),
9773 },
9774 Instruction::End,
9775 ];
9776
9777 scheduler.spawn(program);
9778 run_to_idle(&mut scheduler);
9779
9780 let process = scheduler.processes.get(&Pid(0)).unwrap();
9781 assert_eq!(process.registers[2], Value::Int(i64::MAX));
9783 }
9784
9785 #[test]
9786 fn test_bigint_mod() {
9787 let mut scheduler = Scheduler::new();
9788
9789 let program = vec![
9791 Instruction::LoadInt {
9792 value: i64::MAX,
9793 dest: Register(0),
9794 },
9795 Instruction::Add {
9796 a: Operand::Reg(Register(0)),
9797 b: Operand::Int(5),
9798 dest: Register(1), },
9800 Instruction::Mod {
9801 a: Operand::Reg(Register(1)),
9802 b: Operand::Int(10),
9803 dest: Register(2),
9804 },
9805 Instruction::End,
9806 ];
9807
9808 scheduler.spawn(program);
9809 run_to_idle(&mut scheduler);
9810
9811 let process = scheduler.processes.get(&Pid(0)).unwrap();
9812 let expected = (BigInt::from(i64::MAX) + BigInt::from(5)) % BigInt::from(10);
9814 assert_eq!(process.registers[2], Value::from_bigint(expected));
9815 }
9816
9817 #[test]
9818 fn test_int_bigint_cross_comparison() {
9819 let mut scheduler = Scheduler::new();
9820
9821 let program = vec![
9823 Instruction::LoadInt {
9824 value: i64::MAX,
9825 dest: Register(0),
9826 },
9827 Instruction::Add {
9828 a: Operand::Reg(Register(0)),
9829 b: Operand::Int(1),
9830 dest: Register(1), },
9832 Instruction::Lt {
9834 a: Operand::Reg(Register(0)),
9835 b: Operand::Reg(Register(1)),
9836 dest: Register(2),
9837 },
9838 Instruction::Gt {
9840 a: Operand::Reg(Register(1)),
9841 b: Operand::Reg(Register(0)),
9842 dest: Register(3),
9843 },
9844 Instruction::End,
9845 ];
9846
9847 scheduler.spawn(program);
9848 run_to_idle(&mut scheduler);
9849
9850 let process = scheduler.processes.get(&Pid(0)).unwrap();
9851 assert_eq!(process.registers[2], Value::Int(1)); assert_eq!(process.registers[3], Value::Int(1)); }
9854
9855 #[test]
9858 fn test_binary_construct_segments() {
9859 let mut scheduler = Scheduler::new();
9860
9861 let program = vec![
9863 Instruction::BinaryConstructSegments {
9864 segments: vec![
9865 (
9866 crate::SegmentSource::Int(0x12),
9867 crate::BitSegment::integer(8),
9868 ),
9869 (
9870 crate::SegmentSource::Int(0x34),
9871 crate::BitSegment::integer(8),
9872 ),
9873 ],
9874 dest: Register(0),
9875 },
9876 Instruction::End,
9877 ];
9878
9879 scheduler.spawn(program);
9880 run_to_idle(&mut scheduler);
9881
9882 let process = scheduler.processes.get(&Pid(0)).unwrap();
9883 assert_eq!(process.registers[0], Value::Binary(vec![0x12, 0x34]));
9884 }
9885
9886 #[test]
9887 fn test_binary_construct_16bit_big_endian() {
9888 let mut scheduler = Scheduler::new();
9889
9890 let program = vec![
9892 Instruction::BinaryConstructSegments {
9893 segments: vec![(
9894 crate::SegmentSource::Int(0x1234),
9895 crate::BitSegment::integer(16),
9896 )],
9897 dest: Register(0),
9898 },
9899 Instruction::End,
9900 ];
9901
9902 scheduler.spawn(program);
9903 run_to_idle(&mut scheduler);
9904
9905 let process = scheduler.processes.get(&Pid(0)).unwrap();
9906 assert_eq!(process.registers[0], Value::Binary(vec![0x12, 0x34]));
9907 }
9908
9909 #[test]
9910 fn test_binary_construct_16bit_little_endian() {
9911 let mut scheduler = Scheduler::new();
9912
9913 let program = vec![
9915 Instruction::BinaryConstructSegments {
9916 segments: vec![(
9917 crate::SegmentSource::Int(0x1234),
9918 crate::BitSegment::integer(16).little(),
9919 )],
9920 dest: Register(0),
9921 },
9922 Instruction::End,
9923 ];
9924
9925 scheduler.spawn(program);
9926 run_to_idle(&mut scheduler);
9927
9928 let process = scheduler.processes.get(&Pid(0)).unwrap();
9929 assert_eq!(process.registers[0], Value::Binary(vec![0x34, 0x12]));
9930 }
9931
9932 #[test]
9933 fn test_binary_construct_with_register() {
9934 let mut scheduler = Scheduler::new();
9935
9936 let program = vec![
9937 Instruction::LoadInt {
9938 value: 0xAB,
9939 dest: Register(0),
9940 },
9941 Instruction::BinaryConstructSegments {
9942 segments: vec![
9943 (
9944 crate::SegmentSource::Reg(Register(0)),
9945 crate::BitSegment::integer(8),
9946 ),
9947 (
9948 crate::SegmentSource::Int(0xCD),
9949 crate::BitSegment::integer(8),
9950 ),
9951 ],
9952 dest: Register(1),
9953 },
9954 Instruction::End,
9955 ];
9956
9957 scheduler.spawn(program);
9958 run_to_idle(&mut scheduler);
9959
9960 let process = scheduler.processes.get(&Pid(0)).unwrap();
9961 assert_eq!(process.registers[1], Value::Binary(vec![0xAB, 0xCD]));
9962 }
9963
9964 #[test]
9965 fn test_binary_match_segments() {
9966 let mut scheduler = Scheduler::new();
9967
9968 let program = vec![
9969 Instruction::MakeBinary {
9971 bytes: vec![0x12, 0x34, 0x56, 0x78],
9972 dest: Register(0),
9973 },
9974 Instruction::BinaryMatchStart {
9976 source: Register(0),
9977 },
9978 Instruction::BinaryMatchSegment {
9980 segment: crate::BitSegment::integer(8),
9981 dest: Register(1),
9982 fail_target: 100,
9983 },
9984 Instruction::BinaryMatchSegment {
9986 segment: crate::BitSegment::integer(16),
9987 dest: Register(2),
9988 fail_target: 100,
9989 },
9990 Instruction::BinaryMatchRest { dest: Register(3) },
9992 Instruction::End,
9993 ];
9994
9995 scheduler.spawn(program);
9996 run_to_idle(&mut scheduler);
9997
9998 let process = scheduler.processes.get(&Pid(0)).unwrap();
9999 assert_eq!(process.registers[1], Value::Int(0x12));
10000 assert_eq!(process.registers[2], Value::Int(0x3456));
10001 assert_eq!(process.registers[3], Value::Binary(vec![0x78]));
10002 }
10003
10004 #[test]
10005 fn test_binary_match_little_endian() {
10006 let mut scheduler = Scheduler::new();
10007
10008 let program = vec![
10009 Instruction::MakeBinary {
10010 bytes: vec![0x34, 0x12],
10011 dest: Register(0),
10012 },
10013 Instruction::BinaryMatchStart {
10014 source: Register(0),
10015 },
10016 Instruction::BinaryMatchSegment {
10017 segment: crate::BitSegment::integer(16).little(),
10018 dest: Register(1),
10019 fail_target: 100,
10020 },
10021 Instruction::End,
10022 ];
10023
10024 scheduler.spawn(program);
10025 run_to_idle(&mut scheduler);
10026
10027 let process = scheduler.processes.get(&Pid(0)).unwrap();
10028 assert_eq!(process.registers[1], Value::Int(0x1234));
10029 }
10030
10031 #[test]
10032 fn test_binary_match_signed() {
10033 let mut scheduler = Scheduler::new();
10034
10035 let program = vec![
10036 Instruction::MakeBinary {
10038 bytes: vec![0xFF],
10039 dest: Register(0),
10040 },
10041 Instruction::BinaryMatchStart {
10042 source: Register(0),
10043 },
10044 Instruction::BinaryMatchSegment {
10045 segment: crate::BitSegment::integer(8).signed(),
10046 dest: Register(1),
10047 fail_target: 100,
10048 },
10049 Instruction::End,
10050 ];
10051
10052 scheduler.spawn(program);
10053 run_to_idle(&mut scheduler);
10054
10055 let process = scheduler.processes.get(&Pid(0)).unwrap();
10056 assert_eq!(process.registers[1], Value::Int(-1));
10057 }
10058
10059 #[test]
10060 fn test_binary_match_float64() {
10061 let mut scheduler = Scheduler::new();
10062
10063 let f: f64 = 3.14159;
10065 let bytes = f.to_be_bytes().to_vec();
10066
10067 let program = vec![
10068 Instruction::MakeBinary {
10069 bytes: bytes.clone(),
10070 dest: Register(0),
10071 },
10072 Instruction::BinaryMatchStart {
10073 source: Register(0),
10074 },
10075 Instruction::BinaryMatchSegment {
10076 segment: crate::BitSegment::float64(),
10077 dest: Register(1),
10078 fail_target: 100,
10079 },
10080 Instruction::End,
10081 ];
10082
10083 scheduler.spawn(program);
10084 run_to_idle(&mut scheduler);
10085
10086 let process = scheduler.processes.get(&Pid(0)).unwrap();
10087 assert_eq!(process.registers[1], Value::Float(3.14159));
10088 }
10089
10090 #[test]
10091 fn test_binary_match_fail_not_enough_bytes() {
10092 let mut scheduler = Scheduler::new();
10093
10094 let program = vec![
10095 Instruction::MakeBinary {
10096 bytes: vec![0x12],
10097 dest: Register(0),
10098 },
10099 Instruction::BinaryMatchStart {
10100 source: Register(0),
10101 },
10102 Instruction::BinaryMatchSegment {
10104 segment: crate::BitSegment::integer(16),
10105 dest: Register(1),
10106 fail_target: 5, },
10108 Instruction::LoadInt {
10109 value: 999, dest: Register(2),
10111 },
10112 Instruction::End,
10113 Instruction::LoadInt {
10115 value: -1, dest: Register(2),
10117 },
10118 Instruction::End,
10119 ];
10120
10121 scheduler.spawn(program);
10122 run_to_idle(&mut scheduler);
10123
10124 let process = scheduler.processes.get(&Pid(0)).unwrap();
10125 assert_eq!(process.registers[2], Value::Int(-1));
10126 }
10127
10128 #[test]
10129 fn test_binary_get_integer() {
10130 let mut scheduler = Scheduler::new();
10131
10132 let program = vec![
10133 Instruction::MakeBinary {
10134 bytes: vec![0x00, 0x12, 0x34, 0x56],
10135 dest: Register(0),
10136 },
10137 Instruction::LoadInt {
10138 value: 8, dest: Register(1),
10140 },
10141 Instruction::BinaryGetInteger {
10142 bin: Register(0),
10143 bit_offset: Register(1),
10144 segment: crate::BitSegment::integer(16),
10145 dest: Register(2),
10146 },
10147 Instruction::End,
10148 ];
10149
10150 scheduler.spawn(program);
10151 run_to_idle(&mut scheduler);
10152
10153 let process = scheduler.processes.get(&Pid(0)).unwrap();
10154 assert_eq!(process.registers[2], Value::Int(0x1234));
10155 }
10156
10157 #[test]
10158 fn test_binary_put_integer() {
10159 let mut scheduler = Scheduler::new();
10160
10161 let program = vec![
10162 Instruction::MakeBinary {
10163 bytes: vec![0x00, 0x00, 0x00, 0x00],
10164 dest: Register(0),
10165 },
10166 Instruction::LoadInt {
10167 value: 8, dest: Register(1),
10169 },
10170 Instruction::LoadInt {
10171 value: 0xABCD, dest: Register(2),
10173 },
10174 Instruction::BinaryPutInteger {
10175 bin: Register(0),
10176 bit_offset: Register(1),
10177 value: Register(2),
10178 segment: crate::BitSegment::integer(16),
10179 dest: Register(3),
10180 },
10181 Instruction::End,
10182 ];
10183
10184 scheduler.spawn(program);
10185 run_to_idle(&mut scheduler);
10186
10187 let process = scheduler.processes.get(&Pid(0)).unwrap();
10188 assert_eq!(
10189 process.registers[3],
10190 Value::Binary(vec![0x00, 0xAB, 0xCD, 0x00])
10191 );
10192 }
10193
10194 #[test]
10195 fn test_binary_construct_and_match_roundtrip() {
10196 let mut scheduler = Scheduler::new();
10197
10198 let program = vec![
10200 Instruction::BinaryConstructSegments {
10202 segments: vec![
10203 (
10204 crate::SegmentSource::Int(0x12),
10205 crate::BitSegment::integer(8),
10206 ),
10207 (
10208 crate::SegmentSource::Int(0x3456),
10209 crate::BitSegment::integer(16),
10210 ),
10211 (
10212 crate::SegmentSource::Int(0x78),
10213 crate::BitSegment::integer(8),
10214 ),
10215 ],
10216 dest: Register(0),
10217 },
10218 Instruction::BinaryMatchStart {
10220 source: Register(0),
10221 },
10222 Instruction::BinaryMatchSegment {
10223 segment: crate::BitSegment::integer(8),
10224 dest: Register(1),
10225 fail_target: 100,
10226 },
10227 Instruction::BinaryMatchSegment {
10228 segment: crate::BitSegment::integer(16),
10229 dest: Register(2),
10230 fail_target: 100,
10231 },
10232 Instruction::BinaryMatchSegment {
10233 segment: crate::BitSegment::integer(8),
10234 dest: Register(3),
10235 fail_target: 100,
10236 },
10237 Instruction::End,
10238 ];
10239
10240 scheduler.spawn(program);
10241 run_to_idle(&mut scheduler);
10242
10243 let process = scheduler.processes.get(&Pid(0)).unwrap();
10244 assert_eq!(
10245 process.registers[0],
10246 Value::Binary(vec![0x12, 0x34, 0x56, 0x78])
10247 );
10248 assert_eq!(process.registers[1], Value::Int(0x12));
10249 assert_eq!(process.registers[2], Value::Int(0x3456));
10250 assert_eq!(process.registers[3], Value::Int(0x78));
10251 }
10252}