use crate::chunk::Chunk; 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), } impl std::fmt::Display for Status { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Status::Empty => write!(f, "empty"), Status::Borrowed => write!(f, "borrowed"), Status::Nested(creature) => write!(f, "nested {creature}"), } } } impl Status { pub fn receive(&mut self, msg: Value) { match self { Status::Nested(creature) => creature.receive(msg), Status::Borrowed => println!("sending a message to a borrowed process"), Status::Empty => println!("sending a message to a dead process"), } } } #[derive(Debug, Clone, PartialEq)] pub struct Zoo { procs: Vec, empty: Vec, ids: HashMap<&'static str, usize>, dead: Vec<&'static str>, kill_list: Vec<&'static str>, active: usize, } impl Zoo { pub fn new() -> Zoo { Zoo { procs: vec![], empty: vec![], ids: HashMap::new(), kill_list: vec![], 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) { self.kill_list.push(id); } pub fn clean_up(&mut self) { while let Some(id) = self.kill_list.pop() { 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 { println!("getting next process from {curr_id}"); println!( "current procs in zoo:\n{}", self.procs .iter() .map(|proc| proc.to_string()) .collect::>() .join("//") ); println!("ids: {:?}", self.ids); let idx = self.ids.get(curr_id).unwrap(); println!("current idx: {idx}"); 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(); println!("active idx is now: {}", self.active); while self.procs[self.active] == Status::Empty { let new_active_idx = (self.active + 1) % self.procs.len(); println!("new active idx: {new_active_idx}"); println!("new active process is: {}", self.procs[new_active_idx]); self.active = (self.active + 1) % self.procs.len(); } println!("found next proc: {}", &self.procs[self.active]); 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 fn send_msg(&mut self, id: &'static str, msg: Value) { let Some(idx) = self.ids.get(id) else { return; }; self.procs[*idx].receive(msg); } } #[derive(Debug, Clone, PartialEq)] pub struct World { zoo: Rc>, active: Option, main: &'static str, pub result: Option>, } impl World { pub fn new(chunk: Chunk, debug: bool) -> World { let zoo = Rc::new(RefCell::new(Zoo::new())); let main = Creature::new(chunk, zoo.clone(), debug); let id = zoo.as_ref().borrow_mut().put(main); World { zoo, active: None, main: id, result: None, } } // 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 .as_ref() .borrow_mut() .next(self.active.as_ref().unwrap().id); println!("next id is {id}"); let mut active = None; std::mem::swap(&mut active, &mut self.active); let mut holding_pen = self.zoo.as_ref().borrow_mut().catch(id); let mut active = active.unwrap(); std::mem::swap(&mut active, &mut holding_pen); println!("now in the holding pen: {}", holding_pen.id); holding_pen.reset_reductions(); self.zoo.as_ref().borrow_mut().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 activate_main(&mut self) { let main = self.zoo.as_ref().borrow_mut().catch(self.main); self.active = Some(main); } pub fn active_id(&mut self) -> &'static str { self.active.as_ref().unwrap().id } pub fn kill_active(&mut self) { let id = self.active_id(); self.zoo.as_ref().borrow_mut().kill(id); } pub fn active_result(&mut self) -> &Option> { &self.active.as_ref().unwrap().result } pub fn clean_up(&mut self) { self.zoo.as_ref().borrow_mut().clean_up() } pub fn run(&mut self) { self.activate_main(); loop { println!("entering world loop"); self.active.as_mut().unwrap().interpret(); println!("interpreted loop"); match self.active_result() { None => (), Some(_) => { if self.active_id() == self.main { self.result = self.active_result().clone(); return; } self.kill_active(); } } println!("getting next process"); self.next(); self.clean_up(); } } // 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.