start work on actor model
This commit is contained in:
parent
0c17b64fd7
commit
b5528ced8f
|
@ -8,6 +8,7 @@ const DEBUG_PRELUDE_COMPILE: bool = false;
|
|||
const DEBUG_PRELUDE_RUN: bool = false;
|
||||
|
||||
mod base;
|
||||
mod world;
|
||||
|
||||
mod spans;
|
||||
use crate::spans::Spanned;
|
||||
|
@ -34,7 +35,7 @@ mod value;
|
|||
use value::Value;
|
||||
|
||||
mod vm;
|
||||
use vm::Vm;
|
||||
use vm::Creature;
|
||||
|
||||
const PRELUDE: &str = include_str!("../assets/test_prelude.ld");
|
||||
|
||||
|
@ -81,7 +82,7 @@ fn prelude() -> HashMap<&'static str, Value> {
|
|||
compiler.compile();
|
||||
|
||||
let chunk = compiler.chunk;
|
||||
let mut vm = Vm::new(chunk, DEBUG_PRELUDE_RUN);
|
||||
let mut vm = Creature::new(chunk, DEBUG_PRELUDE_RUN);
|
||||
let prelude = vm.run().clone().unwrap();
|
||||
match prelude {
|
||||
Value::Dict(hashmap) => *hashmap,
|
||||
|
@ -143,7 +144,7 @@ pub fn ludus(src: String) -> String {
|
|||
|
||||
let vm_chunk = compiler.chunk;
|
||||
|
||||
let mut vm = Vm::new(vm_chunk, DEBUG_SCRIPT_RUN);
|
||||
let mut vm = Creature::new(vm_chunk, DEBUG_SCRIPT_RUN);
|
||||
let result = vm.run();
|
||||
|
||||
let console = postlude.get("console").unwrap();
|
||||
|
|
20
src/vm.rs
20
src/vm.rs
|
@ -12,14 +12,6 @@ use std::mem::swap;
|
|||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
// pub struct Panic {
|
||||
// pub input: &'static str,
|
||||
// pub src: &'static str,
|
||||
// pub msg: String,
|
||||
// pub span: SimpleSpan,
|
||||
// pub trace: Vec<Trace>,
|
||||
// pub extra: String,
|
||||
// }
|
||||
pub enum Panic {
|
||||
Str(&'static str),
|
||||
String(String),
|
||||
|
@ -44,6 +36,7 @@ pub struct Trace {
|
|||
pub src: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CallFrame {
|
||||
pub function: Value,
|
||||
pub arity: u8,
|
||||
|
@ -80,7 +73,8 @@ fn combine_bytes(high: u8, low: u8) -> usize {
|
|||
out as usize
|
||||
}
|
||||
|
||||
pub struct Vm {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Creature {
|
||||
pub stack: Vec<Value>,
|
||||
pub call_stack: Vec<CallFrame>,
|
||||
pub frame: CallFrame,
|
||||
|
@ -91,10 +85,11 @@ pub struct Vm {
|
|||
pub result: Option<Result<Value, Panic>>,
|
||||
debug: bool,
|
||||
last_code: usize,
|
||||
pub id: &'static str,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
pub fn new(chunk: Chunk, debug: bool) -> Vm {
|
||||
impl Creature {
|
||||
pub fn new(chunk: Chunk, debug: bool) -> Creature {
|
||||
let lfn = LFn::Defined {
|
||||
name: "user script",
|
||||
doc: None,
|
||||
|
@ -110,7 +105,7 @@ impl Vm {
|
|||
ip: 0,
|
||||
arity: 0,
|
||||
};
|
||||
Vm {
|
||||
Creature {
|
||||
stack: vec![],
|
||||
call_stack: Vec::with_capacity(64),
|
||||
frame: base_frame,
|
||||
|
@ -121,6 +116,7 @@ impl Vm {
|
|||
result: None,
|
||||
debug,
|
||||
last_code: 0,
|
||||
id: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
205
src/world.rs
Normal file
205
src/world.rs
Normal file
|
@ -0,0 +1,205 @@
|
|||
use crate::value::Value;
|
||||
use crate::vm::{Creature, Panic};
|
||||
use ran::ran_u8;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
const ANIMALS: [&str; 24] = [
|
||||
"turtle",
|
||||
"tortoise",
|
||||
"hare",
|
||||
"squirrel",
|
||||
"hawk",
|
||||
"woodpecker",
|
||||
"cardinal",
|
||||
"coyote",
|
||||
"raccoon",
|
||||
"rat",
|
||||
"axolotl",
|
||||
"cormorant",
|
||||
"duck",
|
||||
"orca",
|
||||
"humbpack",
|
||||
"tern",
|
||||
"quokka",
|
||||
"koala",
|
||||
"kangaroo",
|
||||
"zebra",
|
||||
"hyena",
|
||||
"giraffe",
|
||||
"leopard",
|
||||
"lion",
|
||||
];
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
impl Zoo {
|
||||
pub fn new() -> Zoo {
|
||||
Zoo {
|
||||
procs: vec![],
|
||||
empty: vec![],
|
||||
ids: HashMap::new(),
|
||||
dead: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put(&mut self, mut proc: Creature) -> &'static str {
|
||||
if self.empty.is_empty() {
|
||||
let rand = ran_u8() as usize % 24;
|
||||
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);
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
procs: Zoo,
|
||||
mbxes: HashMap<String, VecDeque<Value>>,
|
||||
active: Creature,
|
||||
// TODO: we need a lifetime here
|
||||
main: &'static str,
|
||||
}
|
||||
|
||||
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,
|
||||
main: id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(&mut self, proc: Creature) -> Value {
|
||||
let id = self.procs.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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn get_msg(&self, id: &'static str) -> Option<(usize, Value)> {
|
||||
// check if the id is of the active process
|
||||
todo!()
|
||||
}
|
||||
|
||||
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 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 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<Value, Panic> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// 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.
|
Loading…
Reference in New Issue
Block a user