use crate::value::Value; use crate::vm::{Creature, Panic}; use ran::ran_u8; use std::collections::{HashMap, VecDeque}; const ANIMALS: [&str; 24] = [ "turtle", "tortoise", "hare", "squirrel", "hawk", "woodpecker", "cardinal", "coyote", "raccoon", "rat", "axolotl", "cormorant", "duck", "orca", "humbpack", "tern", "quokka", "koala", "kangaroo", "zebra", "hyena", "giraffe", "leopard", "lion", ]; #[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>, } impl Zoo { pub fn new() -> Zoo { Zoo { procs: vec![], empty: vec![], ids: HashMap::new(), dead: vec![], } } pub fn put(&mut self, mut proc: Creature) -> &'static str { if self.empty.is_empty() { let rand = ran_u8() as usize % 24; let idx = self.procs.len(); let id = format!("{}_{idx}", ANIMALS[rand]).leak(); 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 struct World { procs: Zoo, mbxes: HashMap>, active: Creature, // TODO: we need a lifetime here main: &'static str, } impl World { pub fn new(proc: Creature) -> World { let mut creatures = Zoo::new(); let id = creatures.put(proc); let caught = creatures.catch(id); World { procs: creatures, mbxes: HashMap::new(), active: caught, main: id, } } pub fn spawn(&mut self, proc: Creature) -> Value { let id = self.procs.put(proc); Value::Keyword(id) } pub fn send_msg(&mut self, id: &'static str, msg: Value) { let mbx = self.mbxes.get_mut(id).unwrap(); mbx.push_back(msg); } pub fn sleep(&mut self, id: &'static str) { // check if the id is the actually active process // return it to the nursery // get the next process from the nursery } pub fn get_msg(&self, id: &'static str) -> Option<(usize, Value)> { // check if the id is of the active process todo!() } pub fn match_msg(&mut self, id: &'static str, idx: usize) { // again, check for activity // delete the message at idx, which we gave along with the value as the tuple in get_msg } pub fn r#yield(&mut self, id: &'static str) { // check if the id is active // swap out the currently active process for the next one } pub fn panic(&mut self, id: &'static str) { // TODO: devise some way of linking processes (study the BEAM on this) // check if the id is active // check if the process is `main`, and crash the program if it is // kill the process // swap out this process for the next one } pub fn complete(&mut self, id: &'static str, value: Value) { // check if the id is active // check if the process is main // if it is: stash the value somehow and exit the program cleanly } pub fn run(&mut self) -> Result { todo!() } // 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.