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

55
Cargo.lock generated
View File

@@ -88,7 +88,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.77",
] ]
[[package]] [[package]]
@@ -109,6 +109,27 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6d59c71e7dc3af60f0af9db32364d96a16e9310f3f5db2b55ed642162dd35" checksum = "f3f6d59c71e7dc3af60f0af9db32364d96a16e9310f3f5db2b55ed642162dd35"
[[package]]
name = "gc"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73a03797d58caede765f4b19522d1a63d737088bbef81de06d2dd117d7bc8c3"
dependencies = [
"gc_derive",
]
[[package]]
name = "gc_derive"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb6f1e0d69658b2806f60864bad1a80a9634fc0bc17f7ed55828e3b9c45bf61e"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@@ -128,6 +149,7 @@ dependencies = [
"assert_matches", "assert_matches",
"clap", "clap",
"common_macros", "common_macros",
"gc",
"thiserror", "thiserror",
] ]
@@ -155,6 +177,17 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.77" version = "2.0.77"
@@ -166,6 +199,18 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.63" version = "1.0.63"
@@ -183,7 +228,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.77",
] ]
[[package]] [[package]]
@@ -192,6 +237,12 @@ version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"

View File

@@ -8,3 +8,4 @@ assert_matches = "1.5.0"
clap = { version = "4.5.8", features = ["derive"] } clap = { version = "4.5.8", features = ["derive"] }
common_macros = "0.1.1" common_macros = "0.1.1"
thiserror = "1.0.63" thiserror = "1.0.63"
gc = { version = "0.5", features = ["derive"] }

View File

@@ -1,13 +1,34 @@
//! Builtin functions. //! Builtin functions.
use crate::obj::{NilInst, ObjP}; use std::collections::HashMap;
use crate::obj::*;
use crate::vm::Vm; use crate::vm::Vm;
pub(crate) fn println(_vm: &mut Vm, args: Vec<ObjP>) -> ObjP { pub(crate) fn println(vm: &mut Vm, args: Vec<ObjP>) -> ObjP {
println!("{}", args[0]); println!("{}", args[0].borrow());
NilInst::create() vm.create_nil()
} }
pub(crate) fn print(_vm: &mut Vm, args: Vec<ObjP>) -> ObjP { pub(crate) fn print(vm: &mut Vm, args: Vec<ObjP>) -> ObjP {
print!("{}", args[0]); print!("{}", args[0].borrow());
NilInst::create() vm.create_nil()
}
pub fn init_builtins(builtins: &mut HashMap<String, ObjP>) {
macro_rules! builtins {
($($builtin:ident / $argc:expr),* $(,)?) => {
$({
let builtin_function = builtins.create_builtin_function(stringify!($builtin), $builtin, $argc);
builtins.insert(
stringify!($builtin).to_string(),
builtin_function
);
})*
}
}
builtins! {
print/1,
println/1,
}
} }

View File

@@ -1,6 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::sync::{Arc, LazyLock}; use std::rc::Rc;
use std::sync::LazyLock;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use common_macros::hash_map; use common_macros::hash_map;
@@ -405,25 +406,25 @@ impl Display for CompileError {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug)]
pub struct Compiler { pub struct Compiler<'b> {
chunks: Vec<Chunk>, chunks: Vec<Chunk>,
scopes: Vec<Scope>, scopes: Vec<Scope>,
constants: Vec<ObjP>, constants: Vec<ObjP>,
globals: Vec<String>, globals: Vec<String>,
builtins: &'b HashMap<String, ObjP>,
} }
impl Default for Compiler { impl<'b> Compiler<'b> {
fn default() -> Self { pub fn new(builtins: &'b HashMap<String, ObjP>) -> Self {
Compiler { Compiler {
chunks: Default::default(), chunks: Default::default(),
scopes: Default::default(), scopes: Default::default(),
constants: Default::default(), constants: Default::default(),
globals: vec!["print".to_string(), "println".to_string()], globals: builtins.keys().map(ToString::to_string).collect(),
} builtins,
} }
} }
impl Compiler {
fn chunk(&self) -> &Chunk { fn chunk(&self) -> &Chunk {
self.chunks.last().expect("no chunk") self.chunks.last().expect("no chunk")
} }
@@ -478,7 +479,7 @@ impl Compiler {
// simple interning - try to find a constant that is exactly equal to this one and just // simple interning - try to find a constant that is exactly equal to this one and just
// return its value instead // return its value instead
for (index, interned) in self.constants.iter().enumerate() { for (index, interned) in self.constants.iter().enumerate() {
if constant.equals(interned.as_ref()) { if constant.borrow().equals(&*interned.borrow()) {
return Ok(index as ConstantId); return Ok(index as ConstantId);
} }
} }
@@ -491,6 +492,8 @@ impl Compiler {
} }
.into()); .into());
} }
// convert this to a pointer, upcast, and then re-GC
self.constants.push(constant); self.constants.push(constant);
Ok(index as ConstantId) Ok(index as ConstantId)
} }
@@ -629,7 +632,7 @@ impl Compiler {
} }
} }
impl StmtVisitor for Compiler { impl StmtVisitor for Compiler<'_> {
fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<()> { fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<()> {
self.compile_expr(&stmt.expr)?; self.compile_expr(&stmt.expr)?;
self.emit(stmt_line_number(stmt), Op::Pop); self.emit(stmt_line_number(stmt), Op::Pop);
@@ -661,9 +664,13 @@ impl StmtVisitor for Compiler {
// TODO - maybe this would be smarter to set up in the AST. I'm 99% sure that the last // TODO - maybe this would be smarter to set up in the AST. I'm 99% sure that the last
// object created, if it were a function object, will be what we're assigning it to, but I // object created, if it were a function object, will be what we're assigning it to, but I
// want to be 100% sure instead of 99%. // want to be 100% sure instead of 99%.
let obj = Arc::get_mut(self.constants.last_mut().unwrap()).unwrap(); let obj = self.constants.last().unwrap().as_ref();
if let Some(fun) = obj.as_any_mut().downcast_mut::<UserFunctionInst>() { if let Some(fun) = obj
fun.set_name(Arc::new(name.to_string())); .borrow_mut()
.as_any_mut()
.downcast_mut::<UserFunctionInst>()
{
fun.set_name(Rc::new(name.to_string()));
} }
Ok(()) Ok(())
@@ -671,7 +678,7 @@ impl StmtVisitor for Compiler {
fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> { fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> {
self.compile_expr(&stmt.expr)?; self.compile_expr(&stmt.expr)?;
let name = self.insert_constant(StrInst::create(&stmt.name.text))?; let name = self.insert_constant(self.create_str(&stmt.name.text))?;
self.compile_expr(&stmt.rhs)?; self.compile_expr(&stmt.rhs)?;
self.emit(stmt_line_number(stmt), Op::SetAttr(name)); self.emit(stmt_line_number(stmt), Op::SetAttr(name));
Ok(()) Ok(())
@@ -690,7 +697,7 @@ impl StmtVisitor for Compiler {
if let Some(expr) = &stmt.expr { if let Some(expr) = &stmt.expr {
self.compile_expr(expr)?; self.compile_expr(expr)?;
} else { } else {
let nil = self.insert_constant(NilInst::create())?; let nil = self.insert_constant(self.create_nil())?;
self.emit(stmt_line_number(stmt), Op::PushConstant(nil)); self.emit(stmt_line_number(stmt), Op::PushConstant(nil));
} }
Ok(()) Ok(())
@@ -699,7 +706,7 @@ impl StmtVisitor for Compiler {
// condition // condition
self.compile_expr(&stmt.condition)?; self.compile_expr(&stmt.condition)?;
// call obj.__bool__() // call obj.__bool__()
let bool_attr = self.insert_constant(StrInst::create("__bool__"))?; let bool_attr = self.insert_constant(self.create_str("__bool__"))?;
self.emit(expr_line_number(&*stmt.condition), Op::GetAttr(bool_attr)); self.emit(expr_line_number(&*stmt.condition), Op::GetAttr(bool_attr));
self.emit(expr_line_number(&*stmt.condition), Op::Call(0)); self.emit(expr_line_number(&*stmt.condition), Op::Call(0));
let condition_patch_index = self.chunk().code.len(); let condition_patch_index = self.chunk().code.len();
@@ -746,7 +753,7 @@ impl StmtVisitor for Compiler {
} }
} }
impl ExprVisitor for Compiler { impl ExprVisitor for Compiler<'_> {
fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> { fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> {
static OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| { static OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! { hash_map! {
@@ -771,7 +778,7 @@ impl ExprVisitor for Compiler {
let mut exit_patch_index = 0; let mut exit_patch_index = 0;
if let TokenKind::And | TokenKind::Or = expr.op.kind { if let TokenKind::And | TokenKind::Or = expr.op.kind {
let constant_id = self.insert_constant(StrInst::create("__bool__"))?; let constant_id = self.insert_constant(self.create_str("__bool__"))?;
self.emit(expr_line_number(&*expr.lhs), Op::GetAttr(constant_id)); self.emit(expr_line_number(&*expr.lhs), Op::GetAttr(constant_id));
self.emit(expr_line_number(&*expr.lhs), Op::Call(0)); self.emit(expr_line_number(&*expr.lhs), Op::Call(0));
exit_patch_index = self.chunk().code.len(); exit_patch_index = self.chunk().code.len();
@@ -785,12 +792,12 @@ impl ExprVisitor for Compiler {
let name = OP_NAMES let name = OP_NAMES
.get(&expr.op.kind) .get(&expr.op.kind)
.expect("invalid binary operator"); .expect("invalid binary operator");
let constant_id = self.insert_constant(StrInst::create(name))?; let constant_id = self.insert_constant(self.create_str(name))?;
self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.emit(expr_line_number(expr), Op::GetAttr(constant_id));
// convert RHS to a bool if we're doing AND or OR // convert RHS to a bool if we're doing AND or OR
if let TokenKind::And | TokenKind::Or = expr.op.kind { if let TokenKind::And | TokenKind::Or = expr.op.kind {
let constant_id = self.insert_constant(StrInst::create("__bool__"))?; let constant_id = self.insert_constant(self.create_str("__bool__"))?;
self.emit(expr_line_number(&*expr.rhs), Op::GetAttr(constant_id)); self.emit(expr_line_number(&*expr.rhs), Op::GetAttr(constant_id));
self.emit(expr_line_number(&*expr.rhs), Op::Call(0)); self.emit(expr_line_number(&*expr.rhs), Op::Call(0));
} }
@@ -828,7 +835,7 @@ impl ExprVisitor for Compiler {
}); });
self.compile_expr(&expr.expr)?; self.compile_expr(&expr.expr)?;
let name = OP_NAMES.get(&expr.op.kind).expect("invalid unary operator"); let name = OP_NAMES.get(&expr.op.kind).expect("invalid unary operator");
let constant_id = self.insert_constant(StrInst::create(name))?; let constant_id = self.insert_constant(self.create_str(name))?;
self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.emit(expr_line_number(expr), Op::GetAttr(constant_id));
self.emit(expr_line_number(expr), Op::Call(0)); self.emit(expr_line_number(expr), Op::Call(0));
Ok(()) Ok(())
@@ -852,7 +859,7 @@ impl ExprVisitor for Compiler {
fn visit_get_expr(&mut self, expr: &GetExpr) -> Result<()> { fn visit_get_expr(&mut self, expr: &GetExpr) -> Result<()> {
self.compile_expr(&expr.expr)?; self.compile_expr(&expr.expr)?;
let constant_id = self.insert_constant(StrInst::create(&expr.name.text))?; let constant_id = self.insert_constant(self.create_str(&expr.name.text))?;
self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.emit(expr_line_number(expr), Op::GetAttr(constant_id));
Ok(()) Ok(())
} }
@@ -878,25 +885,25 @@ impl ExprVisitor for Compiler {
} }
TokenKind::Number => { TokenKind::Number => {
let obj = if expr.token.text.contains('.') { let obj = if expr.token.text.contains('.') {
FloatInst::create(expr.token.text.parse().unwrap()) as ObjP self.create_float(expr.token.text.parse().unwrap())
} else { } else {
IntInst::create(expr.token.text.parse().unwrap()) as ObjP self.create_int(expr.token.text.parse().unwrap())
}; };
let constant_id = self.insert_constant(obj)?; let constant_id = self.insert_constant(obj)?;
self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); self.emit(expr_line_number(expr), Op::PushConstant(constant_id));
} }
TokenKind::String => { TokenKind::String => {
let constant_id = let constant_id =
self.insert_constant(StrInst::create(unescape(&expr.token.text)))?; self.insert_constant(self.create_str(unescape(&expr.token.text)))?;
self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); self.emit(expr_line_number(expr), Op::PushConstant(constant_id));
} }
TokenKind::True | TokenKind::False => { TokenKind::True | TokenKind::False => {
let constant_id = let constant_id =
self.insert_constant(BoolInst::create(expr.token.kind == TokenKind::True))?; self.insert_constant(self.create_bool(expr.token.kind == TokenKind::True))?;
self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); self.emit(expr_line_number(expr), Op::PushConstant(constant_id));
} }
TokenKind::Nil => { TokenKind::Nil => {
let constant_id = self.insert_constant(NilInst::create())?; let constant_id = self.insert_constant(self.create_nil())?;
self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); self.emit(expr_line_number(expr), Op::PushConstant(constant_id));
} }
_ => unreachable!(), _ => unreachable!(),
@@ -954,7 +961,7 @@ impl ExprVisitor for Compiler {
} }
// always end with a "return nil" // always end with a "return nil"
let nil = self.insert_constant(NilInst::create())?; let nil = self.insert_constant(self.create_nil())?;
self.emit(end_line, Op::PushConstant(nil)); self.emit(end_line, Op::PushConstant(nil));
self.emit(end_line, Op::Return); self.emit(end_line, Op::Return);
@@ -962,7 +969,7 @@ impl ExprVisitor for Compiler {
// create the function // create the function
let chunk = self.chunks.pop().unwrap(); let chunk = self.chunks.pop().unwrap();
let fun = UserFunctionInst::create(chunk, expr.params.len() as Argc); let fun = self.create_user_function(chunk, expr.params.len() as Argc);
// register the function as a constant // register the function as a constant
let fun_constant = self.insert_constant(fun)?; let fun_constant = self.insert_constant(fun)?;
@@ -982,3 +989,9 @@ impl ExprVisitor for Compiler {
Ok(()) Ok(())
} }
} }
impl ObjFactory for Compiler<'_> {
fn builtins(&self) -> &HashMap<String, ObjP> {
&self.builtins
}
}

View File

@@ -33,7 +33,7 @@ fn disassemble_chunk(chunk: &Chunk, constants: &Vec<ObjP>, globals: &Vec<String>
} }
Op::PushConstant(constant_id) => { Op::PushConstant(constant_id) => {
op_str = "PUSH_CONSTANT"; op_str = "PUSH_CONSTANT";
arg = format!("{}", &constants[*constant_id as usize]); arg = format!("{}", &constants[*constant_id as usize].borrow());
info = format!("(constant ID {constant_id})"); info = format!("(constant ID {constant_id})");
} }
Op::GetLocal(local_id) => { Op::GetLocal(local_id) => {
@@ -60,12 +60,12 @@ fn disassemble_chunk(chunk: &Chunk, constants: &Vec<ObjP>, globals: &Vec<String>
} }
Op::GetAttr(constant_id) => { Op::GetAttr(constant_id) => {
op_str = "GET_ATTR"; op_str = "GET_ATTR";
arg = format!("{}", &constants[*constant_id as usize]); arg = format!("{}", &constants[*constant_id as usize].borrow());
info = format!("(constant ID {constant_id})"); info = format!("(constant ID {constant_id})");
} }
Op::SetAttr(constant_id) => { Op::SetAttr(constant_id) => {
op_str = "SET_ATTR"; op_str = "SET_ATTR";
arg = format!("{}", &constants[*constant_id as usize]); arg = format!("{}", &constants[*constant_id as usize].borrow());
info = format!("(constant ID {constant_id})"); info = format!("(constant ID {constant_id})");
} }
Op::Jump(jump_offset) => { Op::Jump(jump_offset) => {
@@ -145,7 +145,11 @@ pub fn disassemble(chunk: &Chunk, constants: &Vec<ObjP>, globals: &Vec<String>)
disassemble_chunk(chunk, constants, globals); disassemble_chunk(chunk, constants, globals);
for constant in constants { for constant in constants {
if let Some(fun) = constant.as_any().downcast_ref::<UserFunctionInst>() { if let Some(fun) = constant
.borrow()
.as_any()
.downcast_ref::<UserFunctionInst>()
{
println!(); println!();
println!( println!(
"== {} starting on line {}", "== {} starting on line {}",

View File

@@ -1,6 +1,7 @@
// trait_upcasting - https://github.com/rust-lang/rust/issues/65991 // trait_upcasting - https://github.com/rust-lang/rust/issues/65991
// stabilization in progress // stabilization in progress
#![feature(trait_upcasting)] #![feature(trait_upcasting)]
#![feature(coerce_unsized)]
mod ast; mod ast;
mod builtins; mod builtins;
@@ -11,6 +12,7 @@ mod parser;
mod token; mod token;
mod vm; mod vm;
use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
@@ -52,10 +54,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
// initialize type system // initialize type system
obj::init_types(); let mut builtins = HashMap::new();
obj::init_types(&mut builtins);
crate::builtins::init_builtins(&mut builtins);
// compile // compile
let (chunk, constants, globals) = compiler::Compiler::default().compile(&ast)?; let (chunk, constants, globals) = compiler::Compiler::new(&builtins).compile(&ast)?;
if args.disassemble { if args.disassemble {
disassemble::disassemble(&chunk, &constants, &globals); disassemble::disassemble(&chunk, &constants, &globals);
@@ -63,7 +67,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
// run // run
let mut vm = vm::Vm::new(chunk.into(), constants, globals); let mut vm = vm::Vm::new(chunk.into(), constants, globals, builtins);
vm.run(); vm.run();
Ok(()) Ok(())

View File

@@ -5,22 +5,23 @@ use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Debug, Display}; use std::fmt::{self, Debug, Display};
use std::ptr; use std::ptr;
use std::sync::{Arc, LazyLock, Mutex, RwLock}; use std::rc::Rc;
use common_macros::hash_map; use gc::{Finalize, Gc, GcCell, Trace};
use crate::vm::{Argc, Chunk, Frame, Vm}; use crate::vm::{Argc, Chunk, Frame, Vm};
pub type Ptr<T> = Arc<T>; pub type Ptr<T> = Gc<GcCell<T>>;
pub type ObjP = Ptr<dyn Obj + 'static>; pub type ObjP = Ptr<dyn Obj>;
pub type Attrs = RwLock<HashMap<String, ObjP>>; pub type Attrs = HashMap<String, ObjP>;
/// Downcast an object pointer to a concrete type, and do something with that object. /// Downcast an object pointer to a concrete type, and do something with that object.
pub fn with_obj_downcast<T, Out>(ptr: ObjP, closure: impl FnOnce(&T) -> Out) -> Out pub fn with_obj_downcast<T, Out>(ptr: ObjP, closure: impl FnOnce(&T) -> Out) -> Out
where where
T: Obj + 'static, T: Obj + 'static,
{ {
if let Some(obj) = ptr.as_any().downcast_ref::<T>() { let borrowed = ptr.borrow();
if let Some(obj) = borrowed.as_any().downcast_ref::<T>() {
closure(obj) closure(obj)
} else { } else {
panic!( panic!(
@@ -35,115 +36,136 @@ pub fn obj_is_inst<T>(ptr: &ObjP) -> bool
where where
T: Obj + 'static, T: Obj + 'static,
{ {
ptr.as_any().downcast_ref::<T>().is_some() let borrowed = ptr.borrow();
borrowed.as_any().downcast_ref::<T>().is_some()
} }
/// Builtin types macro pub fn upcast_obj<T: Obj>(ptr: Ptr<T>) -> ObjP {
macro_rules! builtin_types { unsafe {
( let ptr = Ptr::into_raw(ptr) as *const GcCell<dyn Obj>;
$( Ptr::from_raw(ptr)
$type_name:ident => { $( $vtable_name:ident => $vtable_value:expr ),* $(,)? }
),+ $(,)?
) => {
pub static TYPES: LazyLock<RwLock<HashMap<String, Ptr<TypeInst>>>> = LazyLock::new(|| {
RwLock::new(hash_map! {
$(
stringify!($type_name).to_string() => make_ptr(TypeInst::new(stringify!($type_name)))
),+
})
});
static TYPE_SYSTEM_INIT: LazyLock<Mutex<bool>> = LazyLock::new(|| Mutex::new(false));
/// Initialize types.
///
/// This should only be called once.
fn init_base_types() {
// instantiate
$(
if stringify!($type_name) != "Type" {
let mut types_ptr = TYPES.try_write().unwrap();
let mut ty = types_ptr.get_mut(stringify!($type_name)).unwrap();
Arc::get_mut(&mut ty).unwrap().instantiate();
} }
)+
// __name__
$(
{
let name = StrInst::create(stringify!($type_name));
let ty = Ptr::clone(&TYPES.try_read().unwrap()[stringify!($type_name)]);
ty.set_attr("__name__", name);
} }
)+
// vtable pub fn init_types(builtins: &mut HashMap<String, ObjP>) {
#![allow(non_snake_case)]
macro_rules! types {
(base_type: $base_type:ident, $($name:ident),* $(,)?) => {
$( $(
{ let $name = make_ptr(TypeInst::new(stringify!($name)));
let ptr = Ptr::clone(&TYPES.try_write().unwrap()[stringify!($type_name)]); )*
$(
ptr.vtable.insert($vtable_name.into(), $vtable_value); $(
$name.borrow_mut().instantiate(upcast_obj(Gc::clone(&$base_type)));
)*
$(
builtins.insert(stringify!($name).to_string(), upcast_obj($name));
)* )*
}
)+
}
}; };
} }
types! {
pub(crate) fn init_types() { // base type
// Taking the lock here will lock the entire function from being run twice base_type: Type,
// simulataneously and prevent race conditions. // type definitions
// Type,
// Race conditions really can only happen during testing so this is just a precaution. Obj,
let mut lock_guard = TYPE_SYSTEM_INIT.lock().unwrap(); Str,
if *lock_guard { Int,
if cfg!(test) { Float,
return; Bool,
} else { Nil,
panic!("do not initialize type system twice"); BuiltinFunction,
UserFunction,
Method,
} }
} }
// Init type_type here pub trait ObjFactory {
{ fn builtins(&self) -> &HashMap<String, ObjP>;
let types_ptr = TYPES.try_read().unwrap();
let type_ptr = types_ptr.get("Type").unwrap(); fn create_obj(&self) -> ObjP {
type_ptr.set_attr( upcast_obj(ObjInst::create(ObjP::clone(
"__type__", self.builtins().get("Obj").unwrap(),
Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP, )))
);
drop(types_ptr);
//let mut types_ptr = TYPES.try_write().unwrap();
//let mut type_ptr = types_ptr.get_mut("Type").unwrap();
//Arc::get_mut(&mut type_ptr).unwrap().base.is_instantiated = true;
} }
// Init the rest of the types fn create_type(&self, name: impl ToString) -> ObjP {
init_base_types(); upcast_obj(TypeInst::create(
ObjP::clone(self.builtins().get("Type").unwrap()),
*lock_guard = true; name,
))
} }
fn placeholder(_: &mut Vm, _: Vec<ObjP>) -> ObjP { fn create_str(&self, str_value: impl ToString) -> ObjP {
NilInst::create() upcast_obj(StrInst::create(
ObjP::clone(self.builtins().get("Str").unwrap()),
str_value,
))
} }
fn to_string(_: &mut Vm, args: Vec<ObjP>) -> ObjP { fn create_int(&self, int_value: i64) -> ObjP {
let str_value = format!("{}", args[0]); upcast_obj(IntInst::create(
StrInst::create(str_value) ObjP::clone(self.builtins().get("Int").unwrap()),
int_value,
))
} }
builtin_types! { fn create_float(&self, float_value: f64) -> ObjP {
Type => {}, upcast_obj(FloatInst::create(
Obj => {}, ObjP::clone(self.builtins().get("Float").unwrap()),
Str => {}, float_value,
Int => {}, ))
Float => {}, }
Bool => {},
Nil => {}, fn create_bool(&self, bool_value: bool) -> ObjP {
BuiltinFunction => {}, upcast_obj(BoolInst::create(
UserFunction => {}, ObjP::clone(self.builtins().get("Bool").unwrap()),
Method => {}, bool_value,
))
}
fn create_nil(&self) -> ObjP {
upcast_obj(NilInst::create(ObjP::clone(
self.builtins().get("Nil").unwrap(),
)))
}
fn create_builtin_function(
&self,
name: impl ToString,
function: BuiltinFunctionPtr,
arity: Argc,
) -> ObjP {
upcast_obj(BuiltinFunctionInst::create(
ObjP::clone(self.builtins().get("BuiltinFunction").unwrap()),
name,
function,
arity,
))
}
fn create_user_function(&self, chunk: Chunk, arity: Argc) -> ObjP {
upcast_obj(UserFunctionInst::create(
ObjP::clone(self.builtins().get("UserFunction").unwrap()),
chunk,
arity,
))
}
fn create_method(&self, self_binding: ObjP, function: ObjP) -> ObjP {
upcast_obj(MethodInst::create(
ObjP::clone(self.builtins().get("Method").unwrap()),
self_binding,
function,
))
}
}
impl ObjFactory for HashMap<String, ObjP> {
fn builtins(&self) -> &Self {
self
}
} }
/// Convenience function for creating pointers, in case the `Arc<RwLock<T>>` pointer type has to /// Convenience function for creating pointers, in case the `Arc<RwLock<T>>` pointer type has to
@@ -152,36 +174,35 @@ builtin_types! {
/// I would implement this as a `From<T>` but it doesn't seem to work for a foreign type, and I'm /// I would implement this as a `From<T>` but it doesn't seem to work for a foreign type, and I'm
/// not sure why. /// not sure why.
pub fn make_ptr<T: Obj>(obj: T) -> Ptr<T> { pub fn make_ptr<T: Obj>(obj: T) -> Ptr<T> {
Arc::new(obj) Ptr::new(GcCell::new(obj))
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Obj // Obj
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
pub trait Obj: Debug + Display + Any + Send + Sync { pub trait Obj: Debug + Display + Any + Trace {
fn instantiate(&mut self); fn instantiate(&mut self, ty: ObjP);
fn is_instantiated(&self) -> bool; fn is_instantiated(&self) -> bool;
fn attrs(&self) -> &Attrs; fn attrs(&self) -> &Attrs;
fn attrs_mut(&mut self) -> &mut Attrs;
fn set_attr(&self, name: &str, value: ObjP) { fn set_attr(&mut self, name: &str, value: ObjP) {
let mut borrowed = self.attrs().try_write().unwrap(); self.attrs_mut().insert(name.to_string(), value);
borrowed.insert(name.to_string(), value);
} }
fn get_attr(&self, name: &str) -> Option<ObjP> { fn get_attr(&self, name: &str) -> Option<ObjP> {
let borrowed = self.attrs().try_read().unwrap(); self.attrs().get(name).map(Ptr::clone)
borrowed.get(name).map(Arc::clone)
} }
fn type_inst(&self) -> ObjP { fn type_inst(&self) -> ObjP {
self.get_attr("__type__").unwrap() self.get_attr("__type__").unwrap()
} }
fn type_name(&self) -> Arc<String> { fn type_name(&self) -> Rc<String> {
with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| { with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| {
Arc::clone(&type_inst.name) Rc::clone(&type_inst.name)
}) })
} }
@@ -210,16 +231,20 @@ pub trait Obj: Debug + Display + Any + Send + Sync {
// BaseObjInst // BaseObjInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Default)] #[derive(Debug, Default, Trace)]
struct BaseObjInst { struct BaseObjInst {
attrs: RwLock<HashMap<String, ObjP>>, attrs: Attrs,
is_instantiated: bool, is_instantiated: bool,
} }
impl Finalize for BaseObjInst {
fn finalize(&self) {}
}
impl Clone for BaseObjInst { impl Clone for BaseObjInst {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
attrs: RwLock::new(self.attrs.try_read().unwrap().clone()), attrs: self.attrs.clone(),
is_instantiated: self.is_instantiated, is_instantiated: self.is_instantiated,
} }
} }
@@ -232,13 +257,8 @@ impl Display for BaseObjInst {
} }
impl Obj for BaseObjInst { impl Obj for BaseObjInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
if self.get_attr("__type__").is_none() { self.set_attr("__type__", ty);
self.set_attr(
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Obj"]) as ObjP,
);
}
// TODO BaseObjInst::instantiate - instantiate VTable // TODO BaseObjInst::instantiate - instantiate VTable
// Okay, we are running into a little snag here: // Okay, we are running into a little snag here:
@@ -275,15 +295,18 @@ impl Obj for BaseObjInst {
&self.attrs &self.attrs
} }
fn attrs_mut(&mut self) -> &mut Attrs {
&mut self.attrs
}
fn equals(&self, other: &dyn Obj) -> bool { fn equals(&self, other: &dyn Obj) -> bool {
if let Some(other) = other.as_any().downcast_ref::<BaseObjInst>() { if let Some(other) = other.as_any().downcast_ref::<BaseObjInst>() {
// compare all attrs // compare all attrs
let borrowed = self.attrs.try_read().unwrap(); self.attrs.iter().all(|(k1, v1)| {
borrowed.iter().all(|(k1, v1)| { other
let borrowed = other.attrs.try_read().unwrap(); .attrs
borrowed
.get(k1) .get(k1)
.map(|v2| v2.equals(v1.as_ref())) .map(|v2| v2.borrow().equals(&*v1.borrow()))
.unwrap_or(false) .unwrap_or(false)
}) && self.is_instantiated == other.is_instantiated }) && self.is_instantiated == other.is_instantiated
} else { } else {
@@ -310,6 +333,10 @@ macro_rules! impl_base_obj {
self.$base_name.attrs() self.$base_name.attrs()
} }
fn attrs_mut(&mut self) -> &mut Attrs {
self.$base_name.attrs_mut()
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
@@ -327,7 +354,7 @@ macro_rules! impl_base_obj {
// ObjInst // ObjInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct ObjInst { pub struct ObjInst {
base: BaseObjInst, base: BaseObjInst,
} }
@@ -339,13 +366,17 @@ impl ObjInst {
} }
} }
pub fn create() -> Ptr<Self> { pub fn create(ty: ObjP) -> Ptr<Self> {
let mut new = Self::new(); let mut new = Self::new();
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
} }
impl Finalize for ObjInst {
fn finalize(&self) {}
}
impl Display for ObjInst { impl Display for ObjInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "<ObjInst at {:x}>", (self as *const _ as usize)) write!(fmt, "<ObjInst at {:x}>", (self as *const _ as usize))
@@ -353,8 +384,8 @@ impl Display for ObjInst {
} }
impl Obj for ObjInst { impl Obj for ObjInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.base.instantiate(); self.base.instantiate(ty);
} }
fn equals(&self, other: &dyn Obj) -> bool { fn equals(&self, other: &dyn Obj) -> bool {
@@ -372,28 +403,34 @@ impl Obj for ObjInst {
// TypeInst // TypeInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Trace)]
pub struct TypeInst { pub struct TypeInst {
name: Arc<String>, #[unsafe_ignore_trace]
name: Rc<String>,
base: BaseObjInst, base: BaseObjInst,
vtable: HashMap<String, ObjP>, vtable: HashMap<String, ObjP>,
} }
impl Finalize for TypeInst {
fn finalize(&self) {}
}
impl TypeInst { impl TypeInst {
pub fn new(name: impl ToString) -> Self { pub fn new(name: impl ToString) -> Self {
Self { Self {
name: Arc::new(name.to_string()), name: Rc::new(name.to_string()),
base: Default::default(), base: Default::default(),
vtable: Default::default(), vtable: Default::default(),
} }
} }
pub fn create(name: impl ToString) -> Ptr<Self> { pub fn create(ty: ObjP, name: impl ToString) -> Ptr<Self> {
let mut new = Self::new(name); let mut new = Self::new(name);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
pub fn name(&self) -> &Arc<String> { pub fn name(&self) -> &Rc<String> {
&self.name &self.name
} }
} }
@@ -421,12 +458,8 @@ impl Display for TypeInst {
} }
impl Obj for TypeInst { impl Obj for TypeInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP,
);
self.base.instantiate();
} }
fn equals(&self, other: &dyn Obj) -> bool { fn equals(&self, other: &dyn Obj) -> bool {
@@ -450,44 +483,45 @@ impl Obj for TypeInst {
// StrInst // StrInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct StrInst { pub struct StrInst {
str_value: Arc<String>, #[unsafe_ignore_trace]
str_value: Rc<String>,
base: BaseObjInst, base: BaseObjInst,
} }
impl StrInst {
pub fn new(str_value: impl ToString) -> Self {
Self {
str_value: Rc::new(str_value.to_string()),
base: Default::default(),
}
}
pub fn create(ty: ObjP, str_value: impl ToString) -> Ptr<Self> {
let mut new = Self::new(str_value);
new.instantiate(ty);
make_ptr(new)
}
pub fn str_value(&self) -> &Rc<String> {
&self.str_value
}
}
impl Finalize for StrInst {
fn finalize(&self) {}
}
impl Display for StrInst { impl Display for StrInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.str_value) write!(fmt, "{}", self.str_value)
} }
} }
impl StrInst {
pub fn new(str_value: impl ToString) -> Self {
Self {
str_value: Arc::new(str_value.to_string()),
base: Default::default(),
}
}
pub fn create(str_value: impl ToString) -> Ptr<Self> {
let mut new = Self::new(str_value);
new.instantiate();
make_ptr(new)
}
pub fn str_value(&self) -> &Arc<String> {
&self.str_value
}
}
impl Obj for StrInst { impl Obj for StrInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Str"]) as ObjP,
);
self.base.instantiate();
} }
fn is_truthy(&self) -> bool { fn is_truthy(&self) -> bool {
@@ -509,7 +543,7 @@ impl Obj for StrInst {
// IntInst // IntInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct IntInst { pub struct IntInst {
int_value: i64, int_value: i64,
base: BaseObjInst, base: BaseObjInst,
@@ -523,9 +557,9 @@ impl IntInst {
} }
} }
pub fn create(int_value: i64) -> Ptr<Self> { pub fn create(ty: ObjP, int_value: i64) -> Ptr<Self> {
let mut new = Self::new(int_value); let mut new = Self::new(int_value);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
@@ -534,6 +568,10 @@ impl IntInst {
} }
} }
impl Finalize for IntInst {
fn finalize(&self) {}
}
impl Display for IntInst { impl Display for IntInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.int_value) write!(fmt, "{}", self.int_value)
@@ -541,12 +579,8 @@ impl Display for IntInst {
} }
impl Obj for IntInst { impl Obj for IntInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Int"]) as ObjP,
);
self.base.instantiate();
} }
fn is_truthy(&self) -> bool { fn is_truthy(&self) -> bool {
@@ -570,7 +604,7 @@ impl Obj for IntInst {
// FloatInst // FloatInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct FloatInst { pub struct FloatInst {
float_value: f64, float_value: f64,
base: BaseObjInst, base: BaseObjInst,
@@ -584,9 +618,9 @@ impl FloatInst {
} }
} }
pub fn create(float_value: f64) -> Ptr<Self> { pub fn create(ty: ObjP, float_value: f64) -> Ptr<Self> {
let mut new = Self::new(float_value); let mut new = Self::new(float_value);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
@@ -595,6 +629,10 @@ impl FloatInst {
} }
} }
impl Finalize for FloatInst {
fn finalize(&self) {}
}
impl Display for FloatInst { impl Display for FloatInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.float_value) write!(fmt, "{}", self.float_value)
@@ -602,12 +640,8 @@ impl Display for FloatInst {
} }
impl Obj for FloatInst { impl Obj for FloatInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Float"]) as ObjP,
);
self.base.instantiate();
} }
fn is_truthy(&self) -> bool { fn is_truthy(&self) -> bool {
@@ -631,7 +665,7 @@ impl Obj for FloatInst {
// BoolInst // BoolInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct BoolInst { pub struct BoolInst {
bool_value: bool, bool_value: bool,
base: BaseObjInst, base: BaseObjInst,
@@ -645,10 +679,10 @@ impl BoolInst {
} }
} }
pub fn create(bool_value: bool) -> Ptr<Self> { pub fn create(ty: ObjP, bool_value: bool) -> Ptr<Self> {
// TODO BoolInst::create : interning // TODO BoolInst::create : interning
let mut new = Self::new(bool_value); let mut new = Self::new(bool_value);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
@@ -657,6 +691,10 @@ impl BoolInst {
} }
} }
impl Finalize for BoolInst {
fn finalize(&self) {}
}
impl Display for BoolInst { impl Display for BoolInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.bool_value) write!(fmt, "{}", self.bool_value)
@@ -664,12 +702,8 @@ impl Display for BoolInst {
} }
impl Obj for BoolInst { impl Obj for BoolInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Bool"]) as ObjP,
);
self.base.instantiate();
} }
fn is_truthy(&self) -> bool { fn is_truthy(&self) -> bool {
@@ -691,7 +725,7 @@ impl Obj for BoolInst {
// NilInst // NilInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Default)] #[derive(Debug, Default, Trace)]
pub struct NilInst { pub struct NilInst {
base: BaseObjInst, base: BaseObjInst,
} }
@@ -701,14 +735,18 @@ impl NilInst {
Default::default() Default::default()
} }
pub fn create() -> Ptr<Self> { pub fn create(ty: ObjP) -> Ptr<Self> {
// TODO NilInst::create : interning // TODO NilInst::create : interning
let mut new = Self::new(); let mut new = Self::new();
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
} }
impl Finalize for NilInst {
fn finalize(&self) {}
}
impl Display for NilInst { impl Display for NilInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "nil") write!(fmt, "nil")
@@ -716,12 +754,8 @@ impl Display for NilInst {
} }
impl Obj for NilInst { impl Obj for NilInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Nil"]) as ObjP,
);
self.base.instantiate();
} }
fn is_truthy(&self) -> bool { fn is_truthy(&self) -> bool {
@@ -741,27 +775,33 @@ impl Obj for NilInst {
pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec<ObjP>) -> ObjP; pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec<ObjP>) -> ObjP;
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct BuiltinFunctionInst { pub struct BuiltinFunctionInst {
base: BaseObjInst, base: BaseObjInst,
name: String, name: String,
#[unsafe_ignore_trace]
function: BuiltinFunctionPtr, function: BuiltinFunctionPtr,
arity: Argc, arity: Argc,
} }
impl BuiltinFunctionInst { impl BuiltinFunctionInst {
pub fn new(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Self { pub fn new(name: impl ToString, function: BuiltinFunctionPtr, arity: Argc) -> Self {
Self { Self {
base: Default::default(), base: Default::default(),
name, name: name.to_string(),
function, function,
arity, arity,
} }
} }
pub fn create(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Ptr<Self> { pub fn create(
ty: ObjP,
name: impl ToString,
function: BuiltinFunctionPtr,
arity: Argc,
) -> Ptr<Self> {
let mut new = Self::new(name, function, arity); let mut new = Self::new(name, function, arity);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
@@ -770,6 +810,10 @@ impl BuiltinFunctionInst {
} }
} }
impl Finalize for BuiltinFunctionInst {
fn finalize(&self) {}
}
impl Display for BuiltinFunctionInst { impl Display for BuiltinFunctionInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
@@ -783,12 +827,8 @@ impl Display for BuiltinFunctionInst {
} }
impl Obj for BuiltinFunctionInst { impl Obj for BuiltinFunctionInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP,
);
self.base.instantiate();
} }
fn arity(&self) -> Option<Argc> { fn arity(&self) -> Option<Argc> {
@@ -825,11 +865,13 @@ impl Obj for BuiltinFunctionInst {
// UserFunctionInst // UserFunctionInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone)] #[derive(Debug, Clone, Trace)]
pub struct UserFunctionInst { pub struct UserFunctionInst {
base: BaseObjInst, base: BaseObjInst,
name: Arc<String>, #[unsafe_ignore_trace]
chunk: Arc<Chunk>, name: Rc<String>,
#[unsafe_ignore_trace]
chunk: Rc<Chunk>,
arity: Argc, arity: Argc,
captures: Vec<ObjP>, captures: Vec<ObjP>,
} }
@@ -838,16 +880,16 @@ impl UserFunctionInst {
pub fn new(chunk: Chunk, arity: Argc) -> Self { pub fn new(chunk: Chunk, arity: Argc) -> Self {
Self { Self {
base: Default::default(), base: Default::default(),
name: Arc::new("(anonymous)".to_string()), name: Rc::new("(anonymous)".to_string()),
chunk: Arc::new(chunk), chunk: Rc::new(chunk),
arity, arity,
captures: Default::default(), captures: Default::default(),
} }
} }
pub fn create(chunk: Chunk, arity: Argc) -> Ptr<Self> { pub fn create(ty: ObjP, chunk: Chunk, arity: Argc) -> Ptr<Self> {
let mut new = Self::new(chunk, arity); let mut new = Self::new(chunk, arity);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
@@ -855,7 +897,7 @@ impl UserFunctionInst {
&self.name &self.name
} }
pub fn set_name(&mut self, name: Arc<String>) { pub fn set_name(&mut self, name: Rc<String>) {
self.name = name; self.name = name;
} }
@@ -868,6 +910,10 @@ impl UserFunctionInst {
} }
} }
impl Finalize for UserFunctionInst {
fn finalize(&self) {}
}
impl Display for UserFunctionInst { impl Display for UserFunctionInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
@@ -881,12 +927,8 @@ impl Display for UserFunctionInst {
} }
impl Obj for UserFunctionInst { impl Obj for UserFunctionInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP,
);
self.base.instantiate();
} }
fn arity(&self) -> Option<Argc> { fn arity(&self) -> Option<Argc> {
@@ -896,8 +938,8 @@ impl Obj for UserFunctionInst {
fn call(&self, vm: &mut Vm, argc: Argc) { fn call(&self, vm: &mut Vm, argc: Argc) {
assert_eq!(argc, self.arity, "argc must match arity"); assert_eq!(argc, self.arity, "argc must match arity");
let new_frame = Frame { let new_frame = Frame {
name: Arc::clone(&self.name), name: Rc::clone(&self.name),
chunk: Arc::clone(&self.chunk), chunk: Rc::clone(&self.chunk),
ip: 0, ip: 0,
stack_base: vm.stack().len() - (argc as usize), stack_base: vm.stack().len() - (argc as usize),
}; };
@@ -923,7 +965,7 @@ impl Obj for UserFunctionInst {
// MethodInst // MethodInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[derive(Debug)] #[derive(Debug, Trace)]
pub struct MethodInst { pub struct MethodInst {
base: BaseObjInst, base: BaseObjInst,
self_binding: ObjP, self_binding: ObjP,
@@ -939,9 +981,9 @@ impl MethodInst {
} }
} }
pub fn create(self_binding: ObjP, function: ObjP) -> Ptr<Self> { pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> Ptr<Self> {
let mut new = Self::new(self_binding, function); let mut new = Self::new(self_binding, function);
new.instantiate(); new.instantiate(ty);
make_ptr(new) make_ptr(new)
} }
@@ -950,27 +992,27 @@ impl MethodInst {
} }
} }
impl Finalize for MethodInst {
fn finalize(&self) {}
}
impl Display for MethodInst { impl Display for MethodInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.function) write!(fmt, "{}", self.function.borrow())
} }
} }
impl Obj for MethodInst { impl Obj for MethodInst {
fn instantiate(&mut self) { fn instantiate(&mut self, ty: ObjP) {
self.set_attr( self.base.instantiate(ty);
"__type__",
Ptr::clone(&TYPES.try_read().unwrap()["Method"]) as ObjP,
);
self.base.instantiate();
} }
fn arity(&self) -> Option<Argc> { fn arity(&self) -> Option<Argc> {
self.function.arity() self.function.borrow().arity()
} }
fn call(&self, vm: &mut Vm, argc: Argc) { fn call(&self, vm: &mut Vm, argc: Argc) {
self.function.call(vm, argc) self.function.borrow().call(vm, argc)
} }
fn equals(&self, other: &dyn Obj) -> bool { fn equals(&self, other: &dyn Obj) -> bool {
@@ -991,58 +1033,60 @@ impl Obj for MethodInst {
#[test] #[test]
fn test_new_objects() { fn test_new_objects() {
init_types(); let mut builtins = HashMap::new();
init_types(&mut builtins);
let type_value = TypeInst::create("Type"); let type_value = builtins.create_type("Type");
assert_eq!(&*type_value.type_name(), "Type"); assert_eq!(&*type_value.borrow().type_name(), "Type");
let str_value = StrInst::create("asdfasdfasdfasdfasdf"); let str_value = builtins.create_str("asdfasdfasdfasdfasdf");
assert_eq!(&*str_value.type_name(), "Str"); assert_eq!(&*str_value.borrow().type_name(), "Str");
let int_value = IntInst::create(1234); let int_value = builtins.create_int(1234);
assert_eq!(&*int_value.type_name(), "Int"); assert_eq!(&*int_value.borrow().type_name(), "Int");
let float_value = FloatInst::create(1234.5678); let float_value = builtins.create_float(1234.5678);
assert_eq!(&*float_value.type_name(), "Float"); assert_eq!(&*float_value.borrow().type_name(), "Float");
let nil_value = NilInst::create(); let nil_value = builtins.create_nil();
assert_eq!(&*nil_value.type_name(), "Nil"); assert_eq!(&*nil_value.borrow().type_name(), "Nil");
} }
#[test] #[test]
fn test_obj_equals() { fn test_obj_equals() {
init_types(); let mut builtins = HashMap::new();
init_types(&mut builtins);
let int1 = IntInst::create(1234); let int1 = builtins.create_int(1234);
let int2 = IntInst::create(1234); let int2 = builtins.create_int(1234);
assert!(int1.equals(int2.as_ref())); assert!(int1.borrow().equals(&*int2.borrow()));
assert!(int2.equals(int1.as_ref())); assert!(int2.borrow().equals(&*int1.borrow()));
let float1 = FloatInst::create(1234.0); let float1 = builtins.create_float(1234.0);
assert!(int1.equals(float1.as_ref())); assert!(int1.borrow().equals(&*float1.borrow()));
assert!(float1.equals(int2.as_ref())); assert!(float1.borrow().equals(&*int2.borrow()));
// self-equality // self-equality
let str1 = StrInst::create("1234"); let str1 = builtins.create_str("1234");
assert!(str1.equals(str1.as_ref())); assert!(str1.borrow().equals(&*str1.borrow()));
let str2 = StrInst::create("1234"); let str2 = builtins.create_str("1234");
assert!(str1.equals(str2.as_ref())); assert!(str1.borrow().equals(&*str2.borrow()));
assert!(str2.equals(str1.as_ref())); assert!(str2.borrow().equals(&*str1.borrow()));
assert!(!str1.equals(float1.as_ref())); assert!(!str1.borrow().equals(&*float1.borrow()));
assert!(!str1.equals(int1.as_ref())); assert!(!str1.borrow().equals(&*int1.borrow()));
let obj1 = ObjInst::create(); let obj1 = builtins.create_obj();
let obj2 = ObjInst::create(); let obj2 = builtins.create_obj();
assert!(obj1.equals(obj2.as_ref())); assert!(obj1.borrow().equals(&*obj2.borrow()));
// these objects aren't equal anymore // these objects aren't equal anymore
obj1.set_attr("my_attr", Ptr::clone(&str2) as ObjP); obj1.borrow_mut().set_attr("my_attr", ObjP::clone(&str2));
assert!(!obj1.equals(obj2.as_ref())); assert!(!obj1.borrow().equals(&*obj2.borrow()));
// but now they are! // but now they are!
obj2.set_attr("my_attr", Ptr::clone(&str2) as ObjP); obj2.borrow_mut().set_attr("my_attr", ObjP::clone(&str2));
assert!(obj2.equals(obj1.as_ref())); assert!(obj2.borrow().equals(&*obj1.borrow()));
} }

View File

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