diff --git a/sandbox.ld b/sandbox.ld index 5687e36..08f6528 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,4 +1 @@ -repeat 1 { - fd! (100) - rt! (0.25) -} +:foobar diff --git a/src/lib.rs b/src/lib.rs index 5babf43..bbe9c50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,9 @@ const DEBUG_PRELUDE_COMPILE: bool = false; const DEBUG_PRELUDE_RUN: bool = false; mod base; + mod world; +use crate::world::World; mod spans; use crate::spans::Spanned; @@ -144,8 +146,11 @@ pub fn ludus(src: String) -> String { let vm_chunk = compiler.chunk; - let mut vm = Creature::new(vm_chunk, DEBUG_SCRIPT_RUN); - let result = vm.run(); + let vm = Creature::new(vm_chunk, DEBUG_SCRIPT_RUN); + let grip = World::new(vm); + grip.borrow_mut().run(); + let world = grip.borrow(); + let result = world.result.clone().unwrap(); let console = postlude.get("console").unwrap(); let Value::Box(console) = console else { @@ -165,7 +170,6 @@ pub fn ludus(src: String) -> String { unreachable!() }; let commands = commands.borrow(); - dbg!(&commands); let commands = commands.to_json().unwrap(); let output = match result { @@ -176,7 +180,7 @@ pub fn ludus(src: String) -> String { } }; if DEBUG_SCRIPT_RUN { - vm.print_stack(); + // vm.print_stack(); } // TODO: use serde_json to make this more robust? diff --git a/src/vm.rs b/src/vm.rs index 1fcbde1..bd1150d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -4,13 +4,17 @@ use crate::op::Op; use crate::parser::Ast; use crate::spans::Spanned; use crate::value::{LFn, Value}; +use crate::world::Grasp; use imbl::{HashMap, Vector}; use num_traits::FromPrimitive; use std::cell::RefCell; +use std::collections::VecDeque; use std::fmt; use std::mem::swap; use std::rc::Rc; +const MAX_REDUCTIONS: usize = 100; + #[derive(Debug, Clone, PartialEq)] pub enum Panic { Str(&'static str), @@ -86,6 +90,9 @@ pub struct Creature { debug: bool, last_code: usize, pub id: &'static str, + pub world: Grasp, + pub mbx: VecDeque, + pub reductions: usize, } impl Creature { @@ -117,9 +124,20 @@ impl Creature { debug, last_code: 0, id: "", + world: None, + mbx: VecDeque::new(), + reductions: 0, } } + pub fn reduce(&mut self) { + self.reductions += 1; + } + + pub fn receive(&mut self, value: Value) { + self.mbx.push_back(value); + } + pub fn chunk(&self) -> &Chunk { self.frame.chunk() } @@ -225,6 +243,9 @@ impl Creature { self.result = Some(Ok(self.stack.pop().unwrap())); return; } + if self.reductions >= MAX_REDUCTIONS { + return; + } let code = self.read(); if self.debug { self.last_code = self.ip - 1; diff --git a/src/world.rs b/src/world.rs index 61a5d97..2b094d5 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,10 +1,11 @@ use crate::value::Value; use crate::vm::{Creature, Panic}; use ran::ran_u8; -use std::collections::{HashMap, VecDeque}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; const ANIMALS: [&str; 24] = [ - "turtle", "tortoise", "hare", "squirrel", @@ -28,6 +29,7 @@ const ANIMALS: [&str; 24] = [ "giraffe", "leopard", "lion", + "hippopotamus", ]; #[derive(Debug, Clone, PartialEq)] @@ -43,6 +45,7 @@ struct Zoo { empty: Vec, ids: HashMap<&'static str, usize>, dead: Vec<&'static str>, + active: usize, } impl Zoo { @@ -52,14 +55,28 @@ impl Zoo { empty: vec![], ids: HashMap::new(), 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 rand = ran_u8() as usize % 24; + let id = self.new_id(); 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); @@ -106,76 +123,136 @@ impl Zoo { unreachable!("tried to return a process the world doesn't know about"); } } + + 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, + } + } } +pub type Grasp = Option>>; + +#[derive(Debug, Clone, PartialEq)] pub struct World { - procs: Zoo, - mbxes: HashMap>, - active: Creature, - // TODO: we need a lifetime here + zoo: Zoo, + active: Option, main: &'static str, + handle: Grasp, + pub result: Option>, } 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, + pub fn new(proc: Creature) -> Rc> { + let mut zoo = Zoo::new(); + let id = zoo.put(proc); + let world = World { + zoo, + active: None, main: id, - } + 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 } pub fn spawn(&mut self, proc: Creature) -> Value { - let id = self.procs.put(proc); + let id = self.zoo.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); + let mut proc = self.zoo.catch(id); + proc.receive(msg); + self.zoo.release(proc); } - 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 + 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); } - pub fn get_msg(&self, id: &'static str) -> Option<(usize, Value)> { - // check if the id is of the active process - todo!() - } + // 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 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 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 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 complete(&mut self) { + // if self.main == self.active.id { + // self.result = self.active.result.clone(); + // } + // self.next(id); + // } - 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!() + 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(); + } } // TODO: