Revamp object system, start using gc crate

Wow, what a ride. I think everything should be working now. In short:

* Objects use the `gc` crate, which as a `Gc` garbage-collected pointer
  type. I may choose to implement my own in contiguous memory in the
  future. We will see.
* The type system is no longer global. This is a bit of a burden,
  because now, whenever you want to create a new object, you need to
  pass its type object into the `Obj::instantiate` method, as well as
  its `::create` static method.
* This burden is somewhat alleviated by the `ObjFactory` trait, which
  helps create new objects as long as you have access to a `builtins`
  hashmap. So something that would normally look like this:

    fn init_builtins(builtins: &mut HashMap<String, ObjP>) {
        let print_builtin = upcast_obj(BuiltinFunctionInst::create(
            ObjP::clone(&builtins.get("BuiltinFunction").unwrap()),
            "print",
            print,
            1
        );
        builtins.insert("print".to_string(), print_builtin)
        // other builtins inserted here...
    }

  now looks like this:

    fn init_builtins(builtins: &mut HashMap<String, ObjP>) {
        let print_builtin = builtins.create_builtin_function("print", print, 1);
        builtins.insert("print".to_string(), print_builtin);
    }

(turns out, if all you need is a HashMap<String, ObjP>, you can
implement ObjFactory for HashMap<String, ObjP> itself(!))

Overall, I'm happier with this design, and I think this is what is going
to get merged. It's a little weird to be querying type names that are
used in the language itself to get those type objects, but whatever
works, I guess.

Next up is vtables.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-09-23 18:12:32 -07:00
parent 24b06851c7
commit 8b931e9d12
8 changed files with 502 additions and 356 deletions

View File

@@ -1,6 +1,6 @@
use std::sync::Arc;
use std::collections::HashMap;
use std::rc::Rc;
use crate::builtins;
use crate::obj::*;
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -62,14 +62,14 @@ pub struct Chunk {
#[derive(Debug)]
pub struct Frame {
pub(crate) name: Arc<String>,
pub(crate) chunk: Arc<Chunk>,
pub(crate) name: Rc<String>,
pub(crate) chunk: Rc<Chunk>,
pub(crate) ip: usize,
pub(crate) stack_base: usize,
}
impl Frame {
pub fn new(name: Arc<String>, chunk: Arc<Chunk>, stack_base: usize) -> Self {
pub fn new(name: Rc<String>, chunk: Rc<Chunk>, stack_base: usize) -> Self {
Self {
name,
chunk,
@@ -85,17 +85,20 @@ pub struct Vm {
globals: Vec<ObjP>,
stack: Vec<ObjP>,
frames: Vec<Frame>,
builtins: HashMap<String, ObjP>,
}
impl Vm {
/// Create a new virtual machine with the given chunk, constants, and global names.
pub fn new(chunk: Arc<Chunk>, constants: Vec<ObjP>, global_names: Vec<String>) -> Self {
pub fn new(
chunk: Rc<Chunk>,
constants: Vec<ObjP>,
global_names: Vec<String>,
builtins: HashMap<String, ObjP>,
) -> Self {
// set up globals
let nil = NilInst::create();
let mut globals: Vec<_> = global_names
.iter()
.map(|_| Ptr::clone(&nil) as ObjP)
.collect();
let nil = builtins.create_nil();
let mut globals: Vec<_> = global_names.iter().map(|_| ObjP::clone(&nil)).collect();
let mut register_global = |name: &str, value: ObjP| {
let index = global_names
@@ -105,14 +108,9 @@ impl Vm {
globals[index] = value;
};
register_global(
"print",
BuiltinFunctionInst::create("print".to_string(), builtins::print, 1),
);
register_global(
"println",
BuiltinFunctionInst::create("println".to_string(), builtins::println, 1),
);
for (name, builtin) in builtins.iter() {
register_global(&name, ObjP::clone(&builtin));
}
// stack and frames
let stack = Vec::new();
@@ -124,6 +122,7 @@ impl Vm {
globals,
stack,
frames,
builtins,
}
}
@@ -231,9 +230,9 @@ impl Vm {
// need both declarations to borrow cell value
let name_obj = Ptr::clone(&self.constants[constant_id as usize]);
let name =
with_obj_downcast(name_obj, |name: &StrInst| Arc::clone(&name.str_value()));
with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value()));
let owner = self.pop();
let value = owner.get_attr(&name);
let value = owner.borrow().get_attr(&name);
if let Some(value) = value {
self.push(value);
} else {
@@ -243,18 +242,18 @@ impl Vm {
todo!(
"throw an error because we couldn't read attr '{}' on '{}'",
name,
owner,
owner.borrow(),
);
}
}
Op::SetAttr(constant_id) => {
let name_obj = Ptr::clone(&self.constants[constant_id as usize]);
let name =
with_obj_downcast(name_obj, |name: &StrInst| Arc::clone(&name.str_value()));
with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value()));
let value = self.pop();
let target = self.pop();
target.set_attr(&name, value);
target.borrow_mut().set_attr(&name, value);
}
Op::Jump(offset) => {
let base = (self.ip() - 1) as JumpOpArg;
@@ -264,14 +263,14 @@ impl Vm {
Op::JumpFalse(offset) => {
let base = (self.ip() - 1) as JumpOpArg;
let value = self.peek();
if !value.is_truthy() {
if !value.borrow().is_truthy() {
self.set_ip((base + offset) as usize);
}
}
Op::JumpTrue(offset) => {
let base = (self.ip() - 1) as JumpOpArg;
let value = self.peek();
if value.is_truthy() {
if value.borrow().is_truthy() {
self.set_ip((base + offset) as usize);
}
}
@@ -280,19 +279,22 @@ impl Vm {
let index = self.stack.len() - argc - 1;
let fun_ptr = Ptr::clone(&self.stack[index]);
let arity = if let Some(arity) = fun_ptr.arity() {
let arity = if let Some(arity) = fun_ptr.borrow().arity() {
arity as usize
} else {
// TODO Vm::run, Op::Call - throw an exception when the value isn't
// callable
// BLOCKED-ON: exceptions
todo!("throw an error because we couldn't call {}", fun_ptr);
todo!(
"throw an error because we couldn't call {}",
fun_ptr.borrow()
);
};
// Methods with bound "self" parameter
// argc may be mutated
let mut argc = argc;
if let Some(method) = fun_ptr.as_any().downcast_ref::<MethodInst>() {
if let Some(method) = fun_ptr.borrow().as_any().downcast_ref::<MethodInst>() {
// shift all of the arguments over by one
// (duplicate the last item on the stack and then shift everyone else over)
self.stack
@@ -309,10 +311,10 @@ impl Vm {
// BLOCKED-ON: exceptions
todo!(
"throw an error because we passed the wrong number of arguments to {}",
fun_ptr
fun_ptr.borrow()
);
}
fun_ptr.call(self, argc as Argc);
fun_ptr.borrow().call(self, argc as Argc);
}
Op::Return => {
let return_value = self.pop();
@@ -337,7 +339,7 @@ impl Vm {
let stack_base = self.frames[frame_index].stack_base;
let value = Ptr::clone(&self.stack[stack_base + (slot as usize)]);
fun.push_capture(value);
self.push(make_ptr(fun));
self.push(upcast_obj(make_ptr(fun)));
}
Op::Halt => {
break;
@@ -346,3 +348,9 @@ impl Vm {
}
}
}
impl ObjFactory for Vm {
fn builtins(&self) -> &HashMap<String, ObjP> {
&self.builtins
}
}