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;
|
const DEBUG_PRELUDE_RUN: bool = false;
|
||||||
|
|
||||||
mod base;
|
mod base;
|
||||||
|
mod world;
|
||||||
|
|
||||||
mod spans;
|
mod spans;
|
||||||
use crate::spans::Spanned;
|
use crate::spans::Spanned;
|
||||||
|
@ -34,7 +35,7 @@ mod value;
|
||||||
use value::Value;
|
use value::Value;
|
||||||
|
|
||||||
mod vm;
|
mod vm;
|
||||||
use vm::Vm;
|
use vm::Creature;
|
||||||
|
|
||||||
const PRELUDE: &str = include_str!("../assets/test_prelude.ld");
|
const PRELUDE: &str = include_str!("../assets/test_prelude.ld");
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ fn prelude() -> HashMap<&'static str, Value> {
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
|
|
||||||
let chunk = compiler.chunk;
|
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();
|
let prelude = vm.run().clone().unwrap();
|
||||||
match prelude {
|
match prelude {
|
||||||
Value::Dict(hashmap) => *hashmap,
|
Value::Dict(hashmap) => *hashmap,
|
||||||
|
@ -143,7 +144,7 @@ pub fn ludus(src: String) -> String {
|
||||||
|
|
||||||
let vm_chunk = compiler.chunk;
|
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 result = vm.run();
|
||||||
|
|
||||||
let console = postlude.get("console").unwrap();
|
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;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[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 {
|
pub enum Panic {
|
||||||
Str(&'static str),
|
Str(&'static str),
|
||||||
String(String),
|
String(String),
|
||||||
|
@ -44,6 +36,7 @@ pub struct Trace {
|
||||||
pub src: &'static str,
|
pub src: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct CallFrame {
|
pub struct CallFrame {
|
||||||
pub function: Value,
|
pub function: Value,
|
||||||
pub arity: u8,
|
pub arity: u8,
|
||||||
|
@ -80,7 +73,8 @@ fn combine_bytes(high: u8, low: u8) -> usize {
|
||||||
out as usize
|
out as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
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,
|
||||||
|
@ -91,10 +85,11 @@ pub struct Vm {
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Creature {
|
||||||
pub fn new(chunk: Chunk, debug: bool) -> Vm {
|
pub fn new(chunk: Chunk, debug: bool) -> Creature {
|
||||||
let lfn = LFn::Defined {
|
let lfn = LFn::Defined {
|
||||||
name: "user script",
|
name: "user script",
|
||||||
doc: None,
|
doc: None,
|
||||||
|
@ -110,7 +105,7 @@ impl Vm {
|
||||||
ip: 0,
|
ip: 0,
|
||||||
arity: 0,
|
arity: 0,
|
||||||
};
|
};
|
||||||
Vm {
|
Creature {
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
call_stack: Vec::with_capacity(64),
|
call_stack: Vec::with_capacity(64),
|
||||||
frame: base_frame,
|
frame: base_frame,
|
||||||
|
@ -121,6 +116,7 @@ impl Vm {
|
||||||
result: None,
|
result: None,
|
||||||
debug,
|
debug,
|
||||||
last_code: 0,
|
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