rudus/src/world.rs

460 lines
13 KiB
Rust
Raw Normal View History

2025-06-27 00:30:40 +00:00
use crate::chunk::Chunk;
2025-06-26 05:28:33 +00:00
use crate::value::Value;
use crate::vm::{Creature, Panic};
2025-06-30 16:48:50 +00:00
use crate::io::{MsgOut, MsgIn, do_io};
2025-06-26 20:11:35 +00:00
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::mem::swap;
2025-06-26 20:11:35 +00:00
use std::rc::Rc;
2025-06-30 22:59:59 +00:00
use wasm_bindgen::prelude::*;
2025-06-26 05:28:33 +00:00
2025-06-30 22:59:59 +00:00
// Grab some JS stuff
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
#[wasm_bindgen(js_namespace = Math)]
fn random() -> f64;
#[wasm_bindgen(js_namespace = Date)]
fn now() -> f64;
}
const ANIMALS: [&str; 32] = [
2025-06-26 05:28:33 +00:00
"tortoise",
"hare",
"squirrel",
"hawk",
"woodpecker",
"cardinal",
"coyote",
"raccoon",
"rat",
"axolotl",
"cormorant",
"duck",
"orca",
"humbpack",
"tern",
"quokka",
"koala",
"kangaroo",
"zebra",
"hyena",
"giraffe",
2025-06-26 20:11:35 +00:00
"hippopotamus",
2025-06-30 22:59:59 +00:00
"capybara",
"python",
"gopher",
"crab",
"trout",
"osprey",
"lemur",
"wobbegong",
"walrus",
"opossum",
2025-06-26 05:28:33 +00:00
];
#[derive(Debug, Clone, PartialEq)]
enum Status {
Empty,
Borrowed,
Nested(Creature),
}
2025-06-27 00:30:40 +00:00
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"),
}
}
}
2025-06-26 05:28:33 +00:00
#[derive(Debug, Clone, PartialEq)]
2025-06-27 00:30:40 +00:00
pub struct Zoo {
2025-06-26 05:28:33 +00:00
procs: Vec<Status>,
empty: Vec<usize>,
ids: HashMap<&'static str, usize>,
dead: HashSet<&'static str>,
2025-06-27 00:30:40 +00:00
kill_list: Vec<&'static str>,
2025-06-30 22:59:59 +00:00
sleeping: HashMap<&'static str, f64>,
active_idx: usize,
active_id: &'static str,
2025-06-26 05:28:33 +00:00
}
impl Zoo {
pub fn new() -> Zoo {
Zoo {
procs: vec![],
empty: vec![],
ids: HashMap::new(),
2025-06-27 00:30:40 +00:00
kill_list: vec![],
dead: HashSet::new(),
sleeping: HashMap::new(),
active_idx: 0,
active_id: "",
2025-06-26 05:28:33 +00:00
}
}
2025-06-26 20:11:35 +00:00
fn random_id(&self) -> String {
2025-06-30 22:59:59 +00:00
let rand_idx = (random() * 32.0) as usize;
2025-06-26 20:11:35 +00:00
let idx = self.procs.len();
2025-06-30 22:59:59 +00:00
format!("{}_{idx}", ANIMALS[rand_idx])
2025-06-26 20:11:35 +00:00
}
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.pid = id;
2025-06-26 05:28:33 +00:00
self.procs.push(Status::Nested(proc));
self.ids.insert(id, idx);
id
} else {
let idx = self.empty.pop().unwrap();
2025-06-30 22:59:59 +00:00
let rand = (random() * 32.0) as usize;
2025-06-26 05:28:33 +00:00
let id = format!("{}_{idx}", ANIMALS[rand]).leak();
proc.pid = id;
2025-06-26 05:28:33 +00:00
self.ids.insert(id, idx);
self.procs[idx] = Status::Nested(proc);
id
}
}
pub fn kill(&mut self, id: &'static str) {
2025-06-27 00:30:40 +00:00
self.kill_list.push(id);
}
2025-06-30 22:59:59 +00:00
pub fn sleep(&mut self, id: &'static str, ms: f64) {
self.sleeping
2025-06-30 22:59:59 +00:00
.insert(id, now() + ms);
}
pub fn is_alive(&self, id: &'static str) -> bool {
if self.kill_list.contains(&id) {
return false;
}
let idx = self.ids.get(id);
match idx {
Some(idx) => match self.procs.get(*idx) {
Some(proc) => match proc {
Status::Empty => false,
Status::Borrowed => true,
Status::Nested(_) => true,
},
None => false,
},
None => false,
}
}
2025-06-27 00:30:40 +00:00
pub fn clean_up(&mut self) {
while let Some(id) = self.kill_list.pop() {
if let Some(idx) = self.ids.get(id) {
println!("buried process {id}");
2025-06-27 00:30:40 +00:00
self.procs[*idx] = Status::Empty;
self.empty.push(*idx);
self.ids.remove(id);
self.dead.insert(id);
2025-06-27 00:30:40 +00:00
}
2025-06-26 05:28:33 +00:00
}
self.sleeping
2025-06-30 22:59:59 +00:00
.retain(|_, wakeup_time| now() < *wakeup_time);
println!(
"currently sleeping processes: {}",
self.sleeping
.keys()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(" | ")
);
2025-06-26 05:28:33 +00:00
}
pub fn catch(&mut self, id: &'static str) -> Creature {
if let Some(idx) = self.ids.get(id) {
let mut proc = Status::Borrowed;
swap(&mut proc, &mut self.procs[*idx]);
2025-06-26 05:28:33 +00:00
let Status::Nested(proc) = proc else {
unreachable!("tried to borrow an empty or already-borrowed process {id}");
2025-06-26 05:28:33 +00:00
};
proc
} else {
unreachable!("tried to borrow a non-existent process {id}");
2025-06-26 05:28:33 +00:00
}
}
pub fn release(&mut self, proc: Creature) {
let id = proc.pid;
2025-06-26 05:28:33 +00:00
if let Some(idx) = self.ids.get(id) {
let mut proc = Status::Nested(proc);
swap(&mut proc, &mut self.procs[*idx]);
2025-06-26 05:28:33 +00:00
}
}
2025-06-26 20:11:35 +00:00
pub fn is_available(&self) -> bool {
match &self.procs[self.active_idx] {
Status::Empty => false,
Status::Borrowed => false,
Status::Nested(proc) => !self.sleeping.contains_key(proc.pid),
2025-06-26 20:11:35 +00:00
}
}
pub fn next(&mut self) -> &'static str {
self.clean_up();
let starting_idx = self.active_idx;
self.active_idx = (self.active_idx + 1) % self.procs.len();
while !self.is_available() {
2025-07-01 16:54:11 +00:00
// we've gone round the process queue already
// that means no process is active
// but we may have processes that are alive and asleep
// if nothing is active, yield back to the world's event loop
if self.active_idx == starting_idx {
return ""
}
self.active_idx = (self.active_idx + 1) % self.procs.len();
2025-06-26 20:11:35 +00:00
}
match &self.procs[self.active_idx] {
Status::Empty | Status::Borrowed => unreachable!(),
Status::Nested(proc) => proc.pid,
2025-06-26 20:11:35 +00:00
}
}
2025-06-26 05:28:33 +00:00
2025-06-27 00:30:40 +00:00
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);
}
}
2025-06-26 20:11:35 +00:00
2025-06-30 22:59:59 +00:00
#[derive(Debug, Clone, PartialEq)]
pub struct Buffers {
console: Value,
2025-07-01 18:35:36 +00:00
commands: Value,
2025-06-30 22:59:59 +00:00
// fetch_outbox: Value,
// fetch_inbox: Value,
input: Value,
}
impl Buffers {
pub fn new (prelude: imbl::HashMap<&'static str, Value>) -> Buffers {
Buffers {
console: prelude.get("console").unwrap().clone(),
2025-07-01 18:35:36 +00:00
commands: prelude.get("turtle_commands").unwrap().clone(),
2025-06-30 22:59:59 +00:00
// fetch_outbox: prelude.get("fetch_outbox").unwrap().clone(),
// fetch_inbox: prelude.get("fetch_inbox").unwrap().clone(),
input: prelude.get("input").unwrap().clone(),
}
}
pub fn console (&self) -> Rc<RefCell<Value>> {
self.console.as_box()
}
pub fn input (&self) -> Rc<RefCell<Value>> {
self.input.as_box()
}
2025-07-01 18:35:36 +00:00
pub fn commands (&self) -> Rc<RefCell<Value>> {
self.commands.as_box()
}
2025-06-30 22:59:59 +00:00
// pub fn fetch_outbox (&self) -> Rc<RefCell<Value>> {
// self.fetch_outbox.as_box()
// }
// pub fn fetch_inbox (&self) -> Rc<RefCell<Value>> {
// self.fetch_inbox.as_box()
// }
}
2025-06-26 20:11:35 +00:00
#[derive(Debug, Clone, PartialEq)]
2025-06-26 05:28:33 +00:00
pub struct World {
2025-06-27 00:30:40 +00:00
zoo: Rc<RefCell<Zoo>>,
2025-06-26 20:11:35 +00:00
active: Option<Creature>,
2025-06-26 05:28:33 +00:00
main: &'static str,
2025-06-26 20:11:35 +00:00
pub result: Option<Result<Value, Panic>>,
2025-06-30 22:59:59 +00:00
buffers: Buffers,
last_io: f64,
kill_signal: bool,
2025-06-26 05:28:33 +00:00
}
impl World {
2025-06-30 22:59:59 +00:00
pub fn new(chunk: Chunk, prelude: imbl::HashMap<&'static str, Value>, debug: bool) -> World {
2025-06-27 00:30:40 +00:00
let zoo = Rc::new(RefCell::new(Zoo::new()));
let main = Creature::new(chunk, zoo.clone(), debug);
2025-06-30 22:59:59 +00:00
let id = zoo.borrow_mut().put(main);
let buffers = Buffers::new(prelude);
2025-06-27 00:30:40 +00:00
World {
2025-06-26 20:11:35 +00:00
zoo,
active: None,
2025-06-26 05:28:33 +00:00
main: id,
2025-06-26 20:11:35 +00:00
result: None,
2025-06-30 22:59:59 +00:00
buffers,
last_io: 0.0,
kill_signal: false,
2025-06-27 00:30:40 +00:00
}
2025-06-26 05:28:33 +00:00
}
2025-06-26 20:11:35 +00:00
fn next(&mut self) {
let mut active = None;
swap(&mut active, &mut self.active);
let mut zoo = self.zoo.borrow_mut();
if let Some(active) = active {
zoo.release(active);
}
let new_active_id = zoo.next();
if new_active_id.is_empty() {
self.active = None;
return;
}
let mut new_active_proc = zoo.catch(new_active_id);
new_active_proc.reset_reductions();
let mut new_active_opt = Some(new_active_proc);
swap(&mut new_active_opt, &mut self.active);
2025-06-26 05:28:33 +00:00
}
2025-06-30 22:59:59 +00:00
fn activate_main(&mut self) {
let main = self.zoo.borrow_mut().catch(self.main);
self.active = Some(main);
2025-06-27 00:30:40 +00:00
}
fn active_id(&mut self) -> Option<&'static str> {
match &self.active {
Some(creature) => Some(creature.pid),
None => None,
}
2025-06-27 00:30:40 +00:00
}
2025-06-30 22:59:59 +00:00
fn kill_active(&mut self) {
if let Some(pid) = self.active_id() {
self.zoo.borrow_mut().kill(pid);
}
2025-06-27 00:30:40 +00:00
}
2025-06-30 22:59:59 +00:00
fn active_result(&mut self) -> &Option<Result<Value, Panic>> {
if self.active.is_none() { return &None; }
2025-06-27 00:30:40 +00:00
&self.active.as_ref().unwrap().result
}
2025-06-30 22:59:59 +00:00
fn flush_buffers(&mut self) -> Vec<MsgOut> {
let mut outbox = vec![];
if let Some(console) = self.flush_console() {
outbox.push(console);
}
2025-07-01 18:35:36 +00:00
if let Some(commands) = self.flush_commands() {
outbox.push(commands);
}
2025-06-30 22:59:59 +00:00
outbox
}
fn flush_console(&self) -> Option<MsgOut> {
let console = self.buffers.console();
let working_copy = RefCell::new(Value::new_list());
console.swap(&working_copy);
let working_value = working_copy.borrow();
if working_value.as_list().is_empty() { return None; }
Some(MsgOut::Console(working_value.clone()))
}
2025-07-01 18:35:36 +00:00
fn flush_commands(&self) -> Option<MsgOut> {
let commands = self.buffers.commands();
let working_copy = RefCell::new(Value::new_list());
commands.swap(&working_copy);
let commands = working_copy.borrow();
if commands.as_list().is_empty() {
None
} else {
Some(MsgOut::Commands(commands.clone()))
}
}
2025-06-30 22:59:59 +00:00
fn complete_main(&mut self) -> Vec<MsgOut> {
let mut outbox = self.flush_buffers();
// TODO: if we have a panic, actually add the panic message to the console
let result = self.active_result().clone().unwrap();
self.result = Some(result.clone());
outbox.push(MsgOut::Complete(result));
outbox
}
fn interpret_active(&mut self) {
self.active.as_mut().unwrap().interpret();
}
async fn maybe_do_io(&mut self) {
if self.last_io + 10.0 < now() {
2025-06-30 22:59:59 +00:00
let outbox = self.flush_buffers();
let inbox = do_io(outbox).await;
self.fill_buffers(inbox);
self.last_io = now();
2025-06-30 22:59:59 +00:00
}
}
fn fill_input(&mut self, str: String) {
let value = Value::string(str);
let working = RefCell::new(value);
let input = self.buffers.input();
input.swap(&working);
}
fn fill_buffers(&mut self, inbox: Vec<MsgIn>) {
for msg in inbox {
match msg {
MsgIn::Input(str) => self.fill_input(str),
MsgIn::Kill => self.kill_signal = true,
_ => todo!()
}
}
}
pub async fn run(&mut self) {
2025-06-27 00:30:40 +00:00
self.activate_main();
2025-06-26 20:11:35 +00:00
loop {
self.maybe_do_io().await;
2025-06-30 22:59:59 +00:00
if self.kill_signal {
let outbox = self.flush_buffers();
do_io(outbox).await;
2025-06-30 16:48:50 +00:00
return;
}
if self.active.is_some() {
self.interpret_active();
}
2025-06-30 22:59:59 +00:00
if self.active_result().is_some() {
if self.active_id().unwrap() == self.main {
2025-06-30 22:59:59 +00:00
let outbox = self.complete_main();
do_io(outbox).await;
return;
2025-06-26 20:11:35 +00:00
}
2025-06-30 22:59:59 +00:00
self.kill_active();
2025-06-26 20:11:35 +00:00
}
self.next();
}
2025-06-26 05:28:33 +00:00
}
}