send messages, motherfucker!

This commit is contained in:
Scott Richmond 2025-06-26 20:30:40 -04:00
parent c144702b98
commit 888f5b62da
7 changed files with 284 additions and 81 deletions

View File

@ -1211,7 +1211,35 @@ fn penwidth {
box state = nil box state = nil
fn self {
"Returns the current process's pid, as a keyword."
() -> base :process (:self)
}
fn send {
"Sends a message to the specified process."
(pid as :keyword, msg) -> base :process (:send, pid, msg)
}
fn spawn! {
"Spawns a new process running the function passed in."
(f as :fn) -> base :process (:spawn, f)
}
fn yield! {
"Forces a process to yield."
() -> base :process (:yield)
}
fn msgs () -> base :process (:msgs)
#{ #{
self
send
msgs
spawn!
yield!
abs abs
abs abs
add add

View File

@ -1 +1,11 @@
let true = false let pid = spawn! (fn () -> {
print! (self ())
print! (msgs ())
})
send (pid, :foo)
send (pid, :bar)
yield! ()
:done

View File

@ -609,6 +609,7 @@ pub fn make_base() -> Value {
("number", Value::BaseFn(BaseFn::Unary("number", number))), ("number", Value::BaseFn(BaseFn::Unary("number", number))),
("pi", Value::Number(std::f64::consts::PI)), ("pi", Value::Number(std::f64::consts::PI)),
("print!", Value::BaseFn(BaseFn::Unary("print!", print))), ("print!", Value::BaseFn(BaseFn::Unary("print!", print))),
("process", Value::Process),
("random", Value::BaseFn(BaseFn::Nullary("random", random))), ("random", Value::BaseFn(BaseFn::Nullary("random", random))),
("range", Value::BaseFn(BaseFn::Binary("range", range))), ("range", Value::BaseFn(BaseFn::Binary("range", range))),
("rest", Value::BaseFn(BaseFn::Unary("rest", rest))), ("rest", Value::BaseFn(BaseFn::Unary("rest", rest))),

View File

@ -20,6 +20,12 @@ pub struct Chunk {
pub msgs: Vec<String>, pub msgs: Vec<String>,
} }
impl std::fmt::Display for Chunk {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Chunk.")
}
}
impl Chunk { impl Chunk {
pub fn dissasemble_instr(&self, i: &mut usize) { pub fn dissasemble_instr(&self, i: &mut usize) {
let op = Op::from_u8(self.bytecode[*i]).unwrap(); let op = Op::from_u8(self.bytecode[*i]).unwrap();

View File

@ -3,7 +3,7 @@ use imbl::HashMap;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
const DEBUG_SCRIPT_COMPILE: bool = false; const DEBUG_SCRIPT_COMPILE: bool = false;
const DEBUG_SCRIPT_RUN: bool = false; const DEBUG_SCRIPT_RUN: bool = true;
const DEBUG_PRELUDE_COMPILE: bool = false; const DEBUG_PRELUDE_COMPILE: bool = false;
const DEBUG_PRELUDE_RUN: bool = false; const DEBUG_PRELUDE_RUN: bool = false;
@ -84,8 +84,9 @@ fn prelude() -> HashMap<&'static str, Value> {
compiler.compile(); compiler.compile();
let chunk = compiler.chunk; let chunk = compiler.chunk;
let mut vm = Creature::new(chunk, DEBUG_PRELUDE_RUN); let mut world = World::new(chunk, DEBUG_PRELUDE_RUN);
let prelude = vm.run().clone().unwrap(); world.run();
let prelude = world.result.unwrap().unwrap();
match prelude { match prelude {
Value::Dict(hashmap) => *hashmap, Value::Dict(hashmap) => *hashmap,
_ => unreachable!(), _ => unreachable!(),
@ -146,10 +147,8 @@ pub fn ludus(src: String) -> String {
let vm_chunk = compiler.chunk; let vm_chunk = compiler.chunk;
let vm = Creature::new(vm_chunk, DEBUG_SCRIPT_RUN); let mut world = World::new(vm_chunk, DEBUG_SCRIPT_RUN);
let grip = World::new(vm); world.run();
grip.borrow_mut().run();
let world = grip.borrow();
let result = world.result.clone().unwrap(); let result = world.result.clone().unwrap();
let console = postlude.get("console").unwrap(); let console = postlude.get("console").unwrap();

143
src/vm.rs
View File

@ -4,7 +4,7 @@ use crate::op::Op;
use crate::parser::Ast; use crate::parser::Ast;
use crate::spans::Spanned; use crate::spans::Spanned;
use crate::value::{LFn, Value}; use crate::value::{LFn, Value};
use crate::world::Grasp; use crate::world::Zoo;
use imbl::{HashMap, Vector}; use imbl::{HashMap, Vector};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use std::cell::RefCell; use std::cell::RefCell;
@ -77,26 +77,35 @@ fn combine_bytes(high: u8, low: u8) -> usize {
out as usize out as usize
} }
const REGISTER_SIZE: usize = 8;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Creature { pub struct Creature {
pub stack: Vec<Value>, pub stack: Vec<Value>,
pub call_stack: Vec<CallFrame>, pub call_stack: Vec<CallFrame>,
pub frame: CallFrame, pub frame: CallFrame,
pub ip: usize, pub ip: usize,
pub return_register: [Value; 8], pub register: [Value; REGISTER_SIZE],
pub matches: bool, pub matches: bool,
pub match_depth: u8, pub match_depth: u8,
pub result: Option<Result<Value, Panic>>, pub result: Option<Result<Value, Panic>>,
debug: bool, debug: bool,
last_code: usize, last_code: usize,
pub id: &'static str, pub id: &'static str,
pub world: Grasp,
pub mbx: VecDeque<Value>, pub mbx: VecDeque<Value>,
pub reductions: usize, pub reductions: usize,
pub zoo: Rc<RefCell<Zoo>>,
pub r#yield: bool,
}
impl std::fmt::Display for Creature {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Creature. {} @{}", self.id, self.ip)
}
} }
impl Creature { impl Creature {
pub fn new(chunk: Chunk, debug: bool) -> Creature { pub fn new(chunk: Chunk, zoo: Rc<RefCell<Zoo>>, debug: bool) -> Creature {
let lfn = LFn::Defined { let lfn = LFn::Defined {
name: "user script", name: "user script",
doc: None, doc: None,
@ -106,8 +115,12 @@ impl Creature {
closed: RefCell::new(vec![]), closed: RefCell::new(vec![]),
}; };
let base_fn = Value::Fn(Rc::new(lfn)); let base_fn = Value::Fn(Rc::new(lfn));
Creature::spawn(base_fn, zoo, debug)
}
pub fn spawn(function: Value, zoo: Rc<RefCell<Zoo>>, debug: bool) -> Creature {
let base_frame = CallFrame { let base_frame = CallFrame {
function: base_fn.clone(), function,
stack_base: 0, stack_base: 0,
ip: 0, ip: 0,
arity: 0, arity: 0,
@ -117,16 +130,17 @@ impl Creature {
call_stack: Vec::with_capacity(64), call_stack: Vec::with_capacity(64),
frame: base_frame, frame: base_frame,
ip: 0, ip: 0,
return_register: [const { Value::Nothing }; 8], register: [const { Value::Nothing }; REGISTER_SIZE],
matches: false, matches: false,
match_depth: 0, match_depth: 0,
result: None, result: None,
debug, debug,
last_code: 0, last_code: 0,
id: "", id: "",
world: None, zoo,
mbx: VecDeque::new(), mbx: VecDeque::new(),
reductions: 0, reductions: 0,
r#yield: false,
} }
} }
@ -134,6 +148,11 @@ impl Creature {
self.reductions += 1; self.reductions += 1;
} }
pub fn reset_reductions(&mut self) {
self.reductions = 0;
self.r#yield = false;
}
pub fn receive(&mut self, value: Value) { pub fn receive(&mut self, value: Value) {
self.mbx.push_back(value); self.mbx.push_back(value);
} }
@ -165,12 +184,21 @@ impl Creature {
} }
let inner = inner.join("|"); let inner = inner.join("|");
let register = self let register = self
.return_register .register
.iter() .iter()
.map(|val| val.to_string()) .map(|val| val.to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(","); .join(",");
println!("{:04}: [{inner}] ({register})", self.last_code); let mbx = self
.mbx
.iter()
.map(|val| val.show())
.collect::<Vec<_>>()
.join("/");
println!(
"{:04}: [{inner}] ({register}) {} {{{mbx}}}",
self.last_code, self.id
);
} }
fn print_debug(&self) { fn print_debug(&self) {
@ -179,12 +207,12 @@ impl Creature {
self.chunk().dissasemble_instr(&mut ip); self.chunk().dissasemble_instr(&mut ip);
} }
pub fn run(&mut self) -> &Result<Value, Panic> { // pub fn run(&mut self) -> &Result<Value, Panic> {
while self.result.is_none() { // while self.result.is_none() {
self.interpret(); // self.interpret();
} // }
self.result.as_ref().unwrap() // self.result.as_ref().unwrap()
} // }
pub fn call_stack(&mut self) -> String { pub fn call_stack(&mut self) -> String {
let mut stack = format!(" calling {}", self.frame.function.show()); let mut stack = format!(" calling {}", self.frame.function.show());
@ -237,13 +265,54 @@ impl Creature {
self.ip >= self.chunk().bytecode.len() self.ip >= self.chunk().bytecode.len()
} }
fn handle_msg(&mut self, args: Vec<Value>) {
println!("message received! {}", args[0]);
let Value::Keyword(msg) = args.first().unwrap() else {
return self.panic("malformed message to Process");
};
match *msg {
"self" => self.push(Value::Keyword(self.id)),
"msgs" => {
let msgs = self.mbx.iter().cloned().collect::<Vec<_>>();
let msgs = Vector::from(msgs);
self.push(Value::List(Box::new(msgs)));
}
"send" => {
let Value::Keyword(pid) = args[1] else {
return self.panic("malformed pid");
};
if self.id == pid {
self.mbx.push_back(args[2].clone());
} else {
self.zoo
.as_ref()
.borrow_mut()
.send_msg(pid, args[2].clone());
}
self.push(Value::Nil);
}
"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);
self.push(Value::Keyword(id));
}
"yield" => {
self.r#yield = true;
self.push(Value::Nil);
}
msg => panic!("Process does not understand message: {msg}"),
}
}
pub fn interpret(&mut self) { pub fn interpret(&mut self) {
loop { loop {
if self.at_end() { if self.at_end() {
self.result = Some(Ok(self.stack.pop().unwrap())); self.result = Some(Ok(self.stack.pop().unwrap()));
return; return;
} }
if self.reductions >= MAX_REDUCTIONS { if self.reductions >= MAX_REDUCTIONS || self.r#yield {
return; return;
} }
let code = self.read(); let code = self.read();
@ -321,27 +390,27 @@ impl Creature {
self.push(value.clone()); self.push(value.clone());
} }
Store => { Store => {
self.return_register[0] = self.pop(); self.register[0] = self.pop();
} }
StoreN => { StoreN => {
let n = self.read() as usize; let n = self.read() as usize;
for i in (0..n).rev() { for i in (0..n).rev() {
self.return_register[i] = self.pop(); self.register[i] = self.pop();
} }
} }
Stash => { Stash => {
self.return_register[0] = self.peek().clone(); self.register[0] = self.peek().clone();
} }
Load => { Load => {
let mut value = Value::Nothing; let mut value = Value::Nothing;
swap(&mut self.return_register[0], &mut value); swap(&mut self.register[0], &mut value);
self.push(value); self.push(value);
} }
LoadN => { LoadN => {
let n = self.read() as usize; let n = self.read() as usize;
for i in 0..n { for i in 0..n {
let mut value = Value::Nothing; let mut value = Value::Nothing;
swap(&mut self.return_register[i], &mut value); swap(&mut self.register[i], &mut value);
self.push(value); self.push(value);
} }
} }
@ -814,6 +883,7 @@ impl Creature {
self.push(Value::Partial(Rc::new(partial))); self.push(Value::Partial(Rc::new(partial)));
} }
TailCall => { TailCall => {
self.reduce();
let arity = self.read(); let arity = self.read();
let called = self.pop(); let called = self.pop();
@ -826,6 +896,10 @@ impl Creature {
} }
match called { match called {
Value::Process => {
let args = self.stack.split_off(self.stack.len() - arity as usize);
self.handle_msg(args);
}
Value::Fn(_) => { Value::Fn(_) => {
if !called.as_fn().accepts(arity) { if !called.as_fn().accepts(arity) {
return self.panic_with(format!( return self.panic_with(format!(
@ -835,7 +909,7 @@ impl Creature {
} }
// first put the arguments in the register // first put the arguments in the register
for i in 0..arity as usize { for i in 0..arity as usize {
self.return_register[arity as usize - i - 1] = self.pop(); self.register[arity as usize - i - 1] = self.pop();
} }
// self.print_stack(); // self.print_stack();
@ -843,9 +917,9 @@ impl Creature {
self.stack.truncate(self.frame.stack_base); self.stack.truncate(self.frame.stack_base);
// then push the arguments back on the stack // then push the arguments back on the stack
let mut i = 0; let mut i = 0;
while i < 8 && self.return_register[i] != Value::Nothing { while i < 8 && self.register[i] != Value::Nothing {
let mut value = Value::Nothing; let mut value = Value::Nothing;
swap(&mut self.return_register[i], &mut value); swap(&mut self.register[i], &mut value);
self.push(value); self.push(value);
i += 1; i += 1;
} }
@ -936,6 +1010,7 @@ impl Creature {
} }
} }
Call => { Call => {
self.reduce();
let arity = self.read(); let arity = self.read();
let called = self.pop(); let called = self.pop();
@ -945,6 +1020,10 @@ impl Creature {
} }
match called { match called {
Value::Process => {
let args = self.stack.split_off(self.stack.len() - arity as usize);
self.handle_msg(args);
}
Value::Fn(_) => { Value::Fn(_) => {
if !called.as_fn().accepts(arity) { if !called.as_fn().accepts(arity) {
return self.panic_with(format!( return self.panic_with(format!(
@ -1030,11 +1109,19 @@ impl Creature {
if self.debug { if self.debug {
println!("== returning from {} ==", self.frame.function.show()) println!("== returning from {} ==", self.frame.function.show())
} }
self.frame = self.call_stack.pop().unwrap();
self.ip = self.frame.ip;
let mut value = Value::Nothing; let mut value = Value::Nothing;
swap(&mut self.return_register[0], &mut value); swap(&mut self.register[0], &mut value);
self.push(value); match self.call_stack.pop() {
Some(frame) => {
self.ip = frame.ip;
self.frame = frame;
self.push(value);
}
None => {
self.result = Some(Ok(value));
return;
}
}
} }
Print => { Print => {
println!("{}", self.pop().show()); println!("{}", self.pop().show());

View File

@ -1,3 +1,4 @@
use crate::chunk::Chunk;
use crate::value::Value; use crate::value::Value;
use crate::vm::{Creature, Panic}; use crate::vm::{Creature, Panic};
use ran::ran_u8; use ran::ran_u8;
@ -39,12 +40,33 @@ enum Status {
Nested(Creature), 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)] #[derive(Debug, Clone, PartialEq)]
struct Zoo { pub struct Zoo {
procs: Vec<Status>, procs: Vec<Status>,
empty: Vec<usize>, empty: Vec<usize>,
ids: HashMap<&'static str, usize>, ids: HashMap<&'static str, usize>,
dead: Vec<&'static str>, dead: Vec<&'static str>,
kill_list: Vec<&'static str>,
active: usize, active: usize,
} }
@ -54,6 +76,7 @@ impl Zoo {
procs: vec![], procs: vec![],
empty: vec![], empty: vec![],
ids: HashMap::new(), ids: HashMap::new(),
kill_list: vec![],
dead: vec![], dead: vec![],
active: 0, active: 0,
} }
@ -93,11 +116,17 @@ impl Zoo {
} }
pub fn kill(&mut self, id: &'static str) { pub fn kill(&mut self, id: &'static str) {
if let Some(idx) = self.ids.get(id) { self.kill_list.push(id);
self.procs[*idx] = Status::Empty; }
self.empty.push(*idx);
self.ids.remove(&id); pub fn clean_up(&mut self) {
self.dead.push(id); 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);
}
} }
} }
@ -125,7 +154,18 @@ impl Zoo {
} }
pub fn next(&mut self, curr_id: &'static str) -> &'static str { 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::<Vec<_>>()
.join("//")
);
println!("ids: {:?}", self.ids);
let idx = self.ids.get(curr_id).unwrap(); let idx = self.ids.get(curr_id).unwrap();
println!("current idx: {idx}");
if *idx != self.active { if *idx != self.active {
panic!( panic!(
"tried to get next creature after {curr_id} while {} is active", "tried to get next creature after {curr_id} while {} is active",
@ -133,9 +173,14 @@ impl Zoo {
); );
} }
self.active = (self.active + 1) % self.procs.len(); self.active = (self.active + 1) % self.procs.len();
while self.procs[self.active] != Status::Empty { 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(); self.active = (self.active + 1) % self.procs.len();
} }
println!("found next proc: {}", &self.procs[self.active]);
match &self.procs[self.active] { match &self.procs[self.active] {
Status::Empty => unreachable!(), Status::Empty => unreachable!(),
Status::Borrowed => panic!( Status::Borrowed => panic!(
@ -145,61 +190,63 @@ impl Zoo {
Status::Nested(proc) => proc.id, Status::Nested(proc) => proc.id,
} }
} }
}
pub type Grasp = Option<Rc<RefCell<World>>>; 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)] #[derive(Debug, Clone, PartialEq)]
pub struct World { pub struct World {
zoo: Zoo, zoo: Rc<RefCell<Zoo>>,
active: Option<Creature>, active: Option<Creature>,
main: &'static str, main: &'static str,
handle: Grasp,
pub result: Option<Result<Value, Panic>>, pub result: Option<Result<Value, Panic>>,
} }
impl World { impl World {
pub fn new(proc: Creature) -> Rc<RefCell<World>> { pub fn new(chunk: Chunk, debug: bool) -> World {
let mut zoo = Zoo::new(); let zoo = Rc::new(RefCell::new(Zoo::new()));
let id = zoo.put(proc); let main = Creature::new(chunk, zoo.clone(), debug);
let world = World { let id = zoo.as_ref().borrow_mut().put(main);
World {
zoo, zoo,
active: None, active: None,
main: id, main: id,
handle: None,
result: 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 { // pub fn spawn(&mut self, proc: Creature) -> Value {
let id = self.zoo.put(proc); // let id = self.zoo.put(proc);
Value::Keyword(id) // Value::Keyword(id)
} // }
pub fn send_msg(&mut self, id: &'static str, msg: Value) { // pub fn send_msg(&mut self, id: &'static str, msg: Value) {
let mut proc = self.zoo.catch(id); // let mut proc = self.zoo.catch(id);
proc.receive(msg); // proc.receive(msg);
self.zoo.release(proc); // self.zoo.release(proc);
} // }
fn next(&mut self) { fn next(&mut self) {
let id = self.zoo.next(self.active.as_ref().unwrap().id); let id = self
.zoo
.as_ref()
.borrow_mut()
.next(self.active.as_ref().unwrap().id);
println!("next id is {id}");
let mut active = None; let mut active = None;
std::mem::swap(&mut active, &mut self.active); std::mem::swap(&mut active, &mut self.active);
let mut holding_pen = self.zoo.catch(id); let mut holding_pen = self.zoo.as_ref().borrow_mut().catch(id);
let mut active = active.unwrap(); let mut active = active.unwrap();
std::mem::swap(&mut active, &mut holding_pen); std::mem::swap(&mut active, &mut holding_pen);
self.zoo.release(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); let mut active = Some(active);
std::mem::swap(&mut active, &mut self.active); std::mem::swap(&mut active, &mut self.active);
} }
@ -234,22 +281,47 @@ impl World {
// self.next(id); // self.next(id);
// } // }
pub fn run(&mut self) { pub fn activate_main(&mut self) {
let main = self.zoo.catch(self.main); let main = self.zoo.as_ref().borrow_mut().catch(self.main);
self.active = Some(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<Result<Value, Panic>> {
&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 { loop {
println!("entering world loop");
self.active.as_mut().unwrap().interpret(); self.active.as_mut().unwrap().interpret();
match self.active.as_ref().unwrap().result { println!("interpreted loop");
match self.active_result() {
None => (), None => (),
Some(_) => { Some(_) => {
if self.active.as_ref().unwrap().id == self.main { if self.active_id() == self.main {
self.result = self.active.as_ref().unwrap().result.clone(); self.result = self.active_result().clone();
return; return;
} }
self.zoo.kill(self.active.as_ref().unwrap().id); self.kill_active();
} }
} }
println!("getting next process");
self.next(); self.next();
self.clean_up();
} }
} }