use crate::value::Value; use crate::vm::{Creature, Panic}; use ran::ran_u8; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; const ANIMALS: [&str; 24] = [ "tortoise", "hare", "squirrel", "hawk", "woodpecker", "cardinal", "coyote", "raccoon", "rat", "axolotl", "cormorant", "duck", "orca", "humbpack", "tern", "quokka", "koala", "kangaroo", "zebra", "hyena", "giraffe", "leopard", "lion", "hippopotamus", ]; #[derive(Debug, Clone, PartialEq)] enum Status { Empty, Borrowed, Nested(Creature), } #[derive(Debug, Clone, PartialEq)] struct Zoo { procs: Vec, empty: Vec, ids: HashMap<&'static str, usize>, dead: Vec<&'static str>, active: usize, } impl Zoo { pub fn new() -> Zoo { Zoo { procs: vec![], empty: vec![], ids: HashMap::new(), dead: vec![], active: 0, } } fn random_id(&self) -> String { let rand = ran_u8() as usize % 24; let idx = self.procs.len(); format!("{}_{idx}", ANIMALS[rand]) } fn new_id(&self) -> &'static str { let mut new = self.random_id(); while self.dead.iter().any(|old| *old == new) { new = self.random_id(); } new.leak() } pub fn put(&mut self, mut proc: Creature) -> &'static str { if self.empty.is_empty() { let id = self.new_id(); let idx = self.procs.len(); proc.id = id; self.procs.push(Status::Nested(proc)); self.ids.insert(id, idx); id } else { let idx = self.empty.pop().unwrap(); let rand = ran_u8() as usize % 24; let id = format!("{}_{idx}", ANIMALS[rand]).leak(); proc.id = id; self.ids.insert(id, idx); self.procs[idx] = Status::Nested(proc); id } } pub fn kill(&mut self, id: &'static str) { if let Some(idx) = self.ids.get(id) { self.procs[*idx] = Status::Empty; self.empty.push(*idx); self.ids.remove(&id); self.dead.push(id); } } pub fn catch(&mut self, id: &'static str) -> Creature { if let Some(idx) = self.ids.get(id) { let mut proc = Status::Borrowed; std::mem::swap(&mut proc, &mut self.procs[*idx]); let Status::Nested(proc) = proc else { unreachable!("tried to borrow an empty or already-borrowed process"); }; proc } else { unreachable!("tried to borrow a non-existent process"); } } pub fn release(&mut self, proc: Creature) { let id = proc.id; if let Some(idx) = self.ids.get(id) { let mut proc = Status::Nested(proc); std::mem::swap(&mut proc, &mut self.procs[*idx]); } else { unreachable!("tried to return a process the world doesn't know about"); } } pub fn next(&mut self, curr_id: &'static str) -> &'static str { let idx = self.ids.get(curr_id).unwrap(); if *idx != self.active { panic!( "tried to get next creature after {curr_id} while {} is active", self.active ); } self.active = (self.active + 1) % self.procs.len(); while self.procs[self.active] != Status::Empty { self.active = (self.active + 1) % self.procs.len(); } match &self.procs[self.active] { Status::Empty => unreachable!(), Status::Borrowed => panic!( "encountered unexpectedly borrowed process at idx {}", self.active ), Status::Nested(proc) => proc.id, } } } pub type Grasp = Option>>; #[derive(Debug, Clone, PartialEq)] pub struct World { zoo: Zoo, active: Option, main: &'static str, handle: Grasp, pub result: Option>, } impl World { pub fn new(proc: Creature) -> Rc> { let mut zoo = Zoo::new(); let id = zoo.put(proc); let world = World { zoo, active: None, main: id, handle: None, result: None, }; let handle = Rc::new(RefCell::new(world)); let grasped = handle.clone(); let mut world = grasped.as_ref().borrow_mut(); world.handle = Some(grasped.clone()); let mut caught = world.zoo.catch(id); caught.world = world.handle.clone(); world.zoo.release(caught); handle } pub fn spawn(&mut self, proc: Creature) -> Value { let id = self.zoo.put(proc); Value::Keyword(id) } pub fn send_msg(&mut self, id: &'static str, msg: Value) { let mut proc = self.zoo.catch(id); proc.receive(msg); self.zoo.release(proc); } fn next(&mut self) { let id = self.zoo.next(self.active.as_ref().unwrap().id); let mut active = None; std::mem::swap(&mut active, &mut self.active); let mut holding_pen = self.zoo.catch(id); let mut active = active.unwrap(); std::mem::swap(&mut active, &mut holding_pen); self.zoo.release(holding_pen); let mut active = Some(active); std::mem::swap(&mut active, &mut self.active); } // pub fn sleep(&mut self, id: &'static str) { // // check if the id is the actually active process // if self.active.id != id { // panic!("attempted to sleep a process from outside that process: active = {}; to sleep: = {id}", self.active.id); // } // self.next(id); // } // pub fn panic(&mut self, id: &'static str, panic: Panic) { // // TODO: devise some way of linking processes (study the BEAM on this) // // check if the id is active // if self.active.id != id { // panic!("attempted to panic from a process from outside that process: active = {}; panicking = {id}; panic = {panic}", self.active.id); // } // // check if the process is `main`, and crash the program if it is // if self.main == id { // self.result = self.active.result.clone(); // } // // kill the process // self.zoo.kill(id); // self.next(id); // } // pub fn complete(&mut self) { // if self.main == self.active.id { // self.result = self.active.result.clone(); // } // self.next(id); // } pub fn run(&mut self) { loop { if self.active.is_none() { let main = self.zoo.catch(self.main); self.active = Some(main); } self.active.as_mut().unwrap().interpret(); match self.active.as_ref().unwrap().result { None => (), Some(_) => { if self.active.as_ref().unwrap().id == self.main { self.result = self.active.as_ref().unwrap().result.clone(); return; } self.zoo.kill(self.active.as_ref().unwrap().id); } } self.next(); } } // TODO: // * [ ] Maybe I need to write this from the bottom up? // What do processes need to do? // - [ ] send a message to another process // - [ ] tell the world to spawn a new process, get the pid back // - [ ] receive its messages (always until something matches, or sleep if nothing matches) // - [ ] delete a message from the mbx if it's a match (by idx) // - [ ] yield // - [ ] panic // - [ ] complete // Thus the other side of this looks like: // * [x] Spawn a process // * [x] } // Okay, some more thinking // The world and process can't have mutable references to one another // They will each need an Rc> // All the message passing and world/proc communication will happen through there // And ownership goes World -> Process A -> World -> Process B // Both the world and a process will have an endless `loop`. // But I already have three terms: Zoo, Creature, and World // That should be enough indirection? // To solve tomorrow.