diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 3dc08a0..b0eeb73 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -414,6 +414,7 @@ fn print! { "Sends a text representation of Ludus values to the console." (...args) -> { let line = do args > map (string, _) > join (_, " ") + base :print! (args) update! (console, append (_, line)) :ok } diff --git a/sandbox.ld b/sandbox.ld index 8e6f6cb..2a0efc4 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,16 +1,20 @@ -fn simple_reporter () -> { +fn reporter () -> { print! (self (), msgs ()) } -fn hanger () -> hanger () +fn printer () -> { + print!("LUDUS SAYS ==> hi") + sleep! (1) + printer () +} -let foo = spawn! (hanger) -let bar = spawn! (simple_reporter) -let baz = spawn! (fn () -> panic! :oops) -send (foo, [:foo, :bar, :baz]) -send (bar, (1, 2, 3)) +let foo = spawn! (printer) +let bar = spawn! (reporter) -yield! () +send (bar, [:foo, :bar, :baz]) + +& yield! () +sleep! (5) :done diff --git a/src/lib.rs b/src/lib.rs index 063bee8..af892a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use imbl::HashMap; use wasm_bindgen::prelude::*; const DEBUG_SCRIPT_COMPILE: bool = false; -const DEBUG_SCRIPT_RUN: bool = true; +const DEBUG_SCRIPT_RUN: bool = false; const DEBUG_PRELUDE_COMPILE: bool = false; const DEBUG_PRELUDE_RUN: bool = false; diff --git a/src/vm.rs b/src/vm.rs index f36c1ff..06f222b 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -292,14 +292,15 @@ impl Creature { self.push(Value::Keyword("ok")); } "spawn" => { - println!("spawning new process!"); let f = args[1].clone(); let proc = Creature::spawn(f, self.zoo.clone(), self.debug); let id = self.zoo.as_ref().borrow_mut().put(proc); + println!("spawning new process {id}!"); self.push(Value::Keyword(id)); } "yield" => { self.r#yield = true; + println!("yielding from {}", self.id); self.push(Value::Keyword("ok")); } "alive" => { @@ -318,7 +319,15 @@ impl Creature { self.mbx = VecDeque::new(); self.push(Value::Keyword("ok")); } - "sleep" => {} + "sleep" => { + println!("sleeping {} for {}", self.id, args[1]); + let Value::Number(ms) = args[1] else { + unreachable!() + }; + self.zoo.as_ref().borrow_mut().sleep(self.id, ms as usize); + self.r#yield = true; + self.push(Value::Keyword("ok")); + } msg => panic!("Process does not understand message: {msg}"), } } diff --git a/src/world.rs b/src/world.rs index f44515e..5034323 100644 --- a/src/world.rs +++ b/src/world.rs @@ -4,7 +4,9 @@ use crate::vm::{Creature, Panic}; use ran::ran_u8; use std::cell::RefCell; use std::collections::HashMap; +use std::mem::swap; use std::rc::Rc; +use std::time::{Duration, Instant}; const ANIMALS: [&str; 24] = [ "tortoise", @@ -67,7 +69,9 @@ pub struct Zoo { ids: HashMap<&'static str, usize>, dead: Vec<&'static str>, kill_list: Vec<&'static str>, - active: usize, + sleeping: HashMap<&'static str, (Instant, Duration)>, + active_idx: usize, + active_id: &'static str, } impl Zoo { @@ -78,7 +82,9 @@ impl Zoo { ids: HashMap::new(), kill_list: vec![], dead: vec![], - active: 0, + sleeping: HashMap::new(), + active_idx: 0, + active_id: "", } } @@ -119,27 +125,63 @@ impl Zoo { self.kill_list.push(id); } + pub fn sleep(&mut self, id: &'static str, ms: usize) { + self.sleeping + .insert(id, (Instant::now(), Duration::from_millis(ms as u64))); + } + + 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, + } + } + 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}"); self.procs[*idx] = Status::Empty; self.empty.push(*idx); self.ids.remove(id); self.dead.push(id); } } + + self.sleeping + .retain(|_, (instant, duration)| instant.elapsed() < *duration); + + println!( + "currently sleeping processes: {}", + self.sleeping + .keys() + .map(|id| id.to_string()) + .collect::>() + .join(" | ") + ); } 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]); + swap(&mut proc, &mut self.procs[*idx]); let Status::Nested(proc) = proc else { - unreachable!("tried to borrow an empty or already-borrowed process"); + unreachable!("tried to borrow an empty or already-borrowed process {id}"); }; proc } else { - unreachable!("tried to borrow a non-existent process"); + unreachable!("tried to borrow a non-existent process {id}"); } } @@ -147,46 +189,36 @@ impl Zoo { 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"); + swap(&mut proc, &mut self.procs[*idx]); + } + // Removed because, well, we shouldn't have creatures we don't know about + // And since zoo.next now cleans (and thus kills) before the world releases its active process + // We'll die if we execute this check + // else { + // unreachable!("tried to return a process the world doesn't know about"); + // } + } + + 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.id), } } - 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 + pub fn next(&mut self) -> &'static str { + self.active_idx = (self.active_idx + 1) % self.procs.len(); + while !self.is_available() { + self.clean_up(); + self.active_idx = (self.active_idx + 1) % self.procs.len(); + println!( + "testing process availability: {}", + self.procs[self.active_idx] ); } - 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 - ), + match &self.procs[self.active_idx] { + Status::Empty | Status::Borrowed => unreachable!(), Status::Nested(proc) => proc.id, } } @@ -231,26 +263,42 @@ impl World { // 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); + swap(&mut active, &mut self.active); + let mut zoo = self.zoo.as_ref().borrow_mut(); + zoo.release(active.unwrap()); + // at the moment, active is None, process is released. + // zoo should NOT need an id--it has a representation of the current active process + // world has an active process for memory reasons + // not for state-keeping reasons + let new_active_id = zoo.next(); + 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); } + // fn old_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 { @@ -299,16 +347,15 @@ impl World { &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"); + println!( + "entering world loop; active process is {}", + self.active_id() + ); self.active.as_mut().unwrap().interpret(); - println!("interpreted loop"); + println!("yielded from {}", self.active_id()); match self.active_result() { None => (), Some(_) => { @@ -316,12 +363,13 @@ impl World { self.result = self.active_result().clone(); return; } + println!("process died: {}", self.active_id()); self.kill_active(); } } println!("getting next process"); self.next(); - self.clean_up(); + // self.clean_up(); } }