2025-06-26 05:28:33 +00:00
|
|
|
use crate::value::Value;
|
|
|
|
use crate::vm::{Creature, Panic};
|
|
|
|
use ran::ran_u8;
|
2025-06-26 20:11:35 +00:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::rc::Rc;
|
2025-06-26 05:28:33 +00:00
|
|
|
|
|
|
|
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",
|
2025-06-26 20:11:35 +00:00
|
|
|
"hippopotamus",
|
2025-06-26 05:28:33 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
enum Status {
|
|
|
|
Empty,
|
|
|
|
Borrowed,
|
|
|
|
Nested(Creature),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
struct Zoo {
|
|
|
|
procs: Vec<Status>,
|
|
|
|
empty: Vec<usize>,
|
|
|
|
ids: HashMap<&'static str, usize>,
|
|
|
|
dead: Vec<&'static str>,
|
2025-06-26 20:11:35 +00:00
|
|
|
active: usize,
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Zoo {
|
|
|
|
pub fn new() -> Zoo {
|
|
|
|
Zoo {
|
|
|
|
procs: vec![],
|
|
|
|
empty: vec![],
|
|
|
|
ids: HashMap::new(),
|
|
|
|
dead: vec![],
|
2025-06-26 20:11:35 +00:00
|
|
|
active: 0,
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2025-06-26 05:28:33 +00:00
|
|
|
pub fn put(&mut self, mut proc: Creature) -> &'static str {
|
|
|
|
if self.empty.is_empty() {
|
2025-06-26 20:11:35 +00:00
|
|
|
let id = self.new_id();
|
2025-06-26 05:28:33 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
2025-06-26 20:11:35 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
pub type Grasp = Option<Rc<RefCell<World>>>;
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2025-06-26 05:28:33 +00:00
|
|
|
pub struct World {
|
2025-06-26 20:11:35 +00:00
|
|
|
zoo: Zoo,
|
|
|
|
active: Option<Creature>,
|
2025-06-26 05:28:33 +00:00
|
|
|
main: &'static str,
|
2025-06-26 20:11:35 +00:00
|
|
|
handle: Grasp,
|
|
|
|
pub result: Option<Result<Value, Panic>>,
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl World {
|
2025-06-26 20:11:35 +00:00
|
|
|
pub fn new(proc: Creature) -> Rc<RefCell<World>> {
|
|
|
|
let mut zoo = Zoo::new();
|
|
|
|
let id = zoo.put(proc);
|
|
|
|
let world = World {
|
|
|
|
zoo,
|
|
|
|
active: None,
|
2025-06-26 05:28:33 +00:00
|
|
|
main: id,
|
2025-06-26 20:11:35 +00:00
|
|
|
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
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn spawn(&mut self, proc: Creature) -> Value {
|
2025-06-26 20:11:35 +00:00
|
|
|
let id = self.zoo.put(proc);
|
2025-06-26 05:28:33 +00:00
|
|
|
Value::Keyword(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn send_msg(&mut self, id: &'static str, msg: Value) {
|
2025-06-26 20:11:35 +00:00
|
|
|
let mut proc = self.zoo.catch(id);
|
|
|
|
proc.receive(msg);
|
|
|
|
self.zoo.release(proc);
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
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);
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
// 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);
|
|
|
|
// }
|
2025-06-26 05:28:33 +00:00
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
// 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);
|
|
|
|
// }
|
2025-06-26 05:28:33 +00:00
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
// pub fn complete(&mut self) {
|
|
|
|
// if self.main == self.active.id {
|
|
|
|
// self.result = self.active.result.clone();
|
|
|
|
// }
|
|
|
|
// self.next(id);
|
|
|
|
// }
|
2025-06-26 05:28:33 +00:00
|
|
|
|
2025-06-26 20:11:35 +00:00
|
|
|
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();
|
|
|
|
}
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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<RefCell<PostOffice>>
|
|
|
|
// 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.
|