2024-09-20 16:04:30 -07:00
|
|
|
// TODO obj.rs - remove the warning suppression
|
2024-09-23 20:59:00 -07:00
|
|
|
#![allow(dead_code)]
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-24 09:03:34 -07:00
|
|
|
mod macros;
|
|
|
|
|
// Leave this comment here - macros must come first
|
|
|
|
|
pub mod function;
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
use std::any::Any;
|
2024-09-25 10:22:03 -07:00
|
|
|
use std::cell::RefCell;
|
2024-09-20 16:04:30 -07:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::fmt::{self, Debug, Display};
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
use std::rc::Rc;
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
use gc::{Finalize, Gc, GcCell, Trace};
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-24 09:03:34 -07:00
|
|
|
use crate::obj::function::*;
|
|
|
|
|
use crate::obj::macros::*;
|
2024-09-25 10:22:03 -07:00
|
|
|
use crate::vm::{Argc, Vm};
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
pub type Ptr<T> = Gc<GcCell<T>>;
|
|
|
|
|
pub type ObjP = Ptr<dyn Obj>;
|
|
|
|
|
pub type Attrs = HashMap<String, ObjP>;
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 08:48:11 -07:00
|
|
|
// TODO obj::with_obj_downcast - optimize downcasts of "known" types with an unchecked downcast
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
/// 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
|
|
|
|
|
where
|
|
|
|
|
T: Obj + 'static,
|
|
|
|
|
{
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
let borrowed = ptr.borrow();
|
|
|
|
|
if let Some(obj) = borrowed.as_any().downcast_ref::<T>() {
|
2024-09-20 16:04:30 -07:00
|
|
|
closure(obj)
|
|
|
|
|
} else {
|
|
|
|
|
panic!(
|
|
|
|
|
"could not downcast '{:?}' to {}",
|
WIP: move mutability to be internal to the object instead of the pointer
I'm not super happy with this. But, the RwLock has been moved to the
`BaseObjInst::attrs` member. Although this is not exactly how it appears
in code, it basically does this:
type Ptr<T> = Arc<RwLock<T>>;
struct BaseObjInst {
attr: HashMap<String, Ptr<dyn Obj>>,
// etc
}
becomes
type Ptr<T> = Arc<T>;
struct BaseObjInst {
attr: RwLock<HashMap<String, ObjP>>,
// etc
}
This makes things a lot more ergonomic (don't have to use try_read() and
try_write() everywhere), but it also eliminates compile-time errors that
would catch mutability errors. This is currently rearing its ugly head
when initializing the typesystem, since `Type` needs to hold a circular
reference itself (which it already shouldn't be doing since it's a
reference-counted pointer!). Currently, all tests are failing because of
this limitation.
There are a couple of ways around this limitation.
The first solution would be just copying all of the object
instantiation code into the `init_types` function and avoid calling
`some_base_type.instantiate()`. This would probably be literal
copy-pasting, or maybe an (ugly) macro, and probably a nightmare to
maintain long-term. I don't like this option, but it would make
everything "just work" with reference-counted pointers.
The second solution would be to write our own garbage collector, which
would allow for circular references and (hypothetically) mutably
updating these references. This is something that I am looking into,
because I really want a RefCell that you can pass around in a more
ergonomic way.
I think the fundamental error that I'm running into is trying to borrow
the same value multiple times mutably, which you *really* shouldn't be
doing. I believe I need to write better code and does the same thing.
The only unsolved problem is circular references. This is not a problem
right now because I'm not writing code that has circular references
besides the base typesystem (which is not a problem because they need to
live the entire lifetime of the program), but it will be a latent
problem until it gets fixed.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-22 20:40:15 -07:00
|
|
|
ptr,
|
2024-09-20 16:04:30 -07:00
|
|
|
std::any::type_name::<T>()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
/// Downcast an object pointer to a concrete type, and do something with that object.
|
|
|
|
|
pub fn with_obj_downcast_mut<T, Out>(ptr: ObjP, closure: impl FnOnce(&mut T) -> Out) -> Out
|
|
|
|
|
where
|
|
|
|
|
T: Obj + 'static,
|
|
|
|
|
{
|
|
|
|
|
let mut borrowed = ptr.borrow_mut();
|
|
|
|
|
if let Some(obj) = borrowed.as_any_mut().downcast_mut::<T>() {
|
|
|
|
|
closure(obj)
|
|
|
|
|
} else {
|
|
|
|
|
panic!(
|
|
|
|
|
"could not downcast '{:?}' to {}",
|
|
|
|
|
ptr,
|
|
|
|
|
std::any::type_name::<T>()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
pub fn obj_is_inst<T>(ptr: &ObjP) -> bool
|
|
|
|
|
where
|
|
|
|
|
T: Obj + 'static,
|
|
|
|
|
{
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
let borrowed = ptr.borrow();
|
|
|
|
|
borrowed.as_any().downcast_ref::<T>().is_some()
|
|
|
|
|
}
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
pub fn upcast_obj<T: Obj>(ptr: Ptr<T>) -> ObjP {
|
|
|
|
|
unsafe {
|
|
|
|
|
let ptr = Ptr::into_raw(ptr) as *const GcCell<dyn Obj>;
|
|
|
|
|
Ptr::from_raw(ptr)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
thread_local! {
|
|
|
|
|
pub static BUILTINS: RefCell<HashMap<String, ObjP>> = RefCell::new(HashMap::default());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn init_types() {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#![allow(non_snake_case)]
|
|
|
|
|
macro_rules! types {
|
2024-09-23 20:59:00 -07:00
|
|
|
(
|
|
|
|
|
base_type: $base_type:ident,
|
|
|
|
|
$(
|
|
|
|
|
$name:ident {
|
|
|
|
|
$( $vtable_name:ident => $vtable_value:expr ),* $(,)?
|
|
|
|
|
}
|
|
|
|
|
),* $(,)?
|
|
|
|
|
) => {{
|
2024-09-20 16:04:30 -07:00
|
|
|
$(
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
let $name = make_ptr(TypeInst::new(stringify!($name)));
|
2024-09-25 10:22:03 -07:00
|
|
|
BUILTINS.with_borrow_mut(|builtins| builtins.insert(stringify!($name).to_string(), $name.clone()));
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
)*
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
// We have to instantiate these objects all by hand. This is because the `instantiate`
|
|
|
|
|
// function does some stuff that may accidentally cause infinite recursion while we are
|
|
|
|
|
// setting up these fundamental types.
|
|
|
|
|
$({
|
|
|
|
|
let base_type = $base_type.clone();
|
|
|
|
|
$name.borrow_mut().set_attr("__type__", base_type);
|
|
|
|
|
with_obj_downcast_mut($name.clone(), |type_inst: &mut TypeInst| { type_inst.base.is_instantiated = true; });
|
|
|
|
|
})*
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
$({
|
|
|
|
|
$(
|
|
|
|
|
let vtable_name = stringify!($vtable_name);
|
|
|
|
|
let vtable_value = $vtable_value;
|
2024-09-23 21:34:10 -07:00
|
|
|
with_obj_downcast_mut($name.clone(), |type_inst: &mut TypeInst| {
|
2024-09-23 20:59:00 -07:00
|
|
|
type_inst.vtable.insert(vtable_name.to_string(), vtable_value);
|
|
|
|
|
});
|
|
|
|
|
)*
|
|
|
|
|
})*
|
|
|
|
|
}};
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
}
|
2024-09-23 20:59:00 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
types! {
|
|
|
|
|
// base type
|
|
|
|
|
base_type: Type,
|
|
|
|
|
// type definitions
|
2024-09-23 20:59:00 -07:00
|
|
|
Type {
|
2024-09-23 21:34:10 -07:00
|
|
|
// Method conversion
|
2024-09-25 10:22:03 -07:00
|
|
|
to_str => BuiltinFunctionInst::create("to_str", BaseObjInst::to_str, 1),
|
|
|
|
|
to_repr => BuiltinFunctionInst::create("to_repr", BaseObjInst::to_repr, 1),
|
|
|
|
|
to_bool => BuiltinFunctionInst::create("to_bool", BaseObjInst::to_bool, 1),
|
|
|
|
|
len => BuiltinFunctionInst::create("len", BaseObjInst::not_implemented_un, 1),
|
2024-09-23 21:34:10 -07:00
|
|
|
// Operators
|
2024-09-25 10:22:03 -07:00
|
|
|
__add__ => BuiltinFunctionInst::create("__add__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__sub__ => BuiltinFunctionInst::create("__sub__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__mul__ => BuiltinFunctionInst::create("__mul__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__div__ => BuiltinFunctionInst::create("__div__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__and__ => BuiltinFunctionInst::create("__and__", BaseObjInst::and, 2),
|
|
|
|
|
__or__ => BuiltinFunctionInst::create("__or__", BaseObjInst::or, 2),
|
|
|
|
|
__ne__ => BuiltinFunctionInst::create("__ne__", BaseObjInst::ne, 2),
|
|
|
|
|
__eq__ => BuiltinFunctionInst::create("__eq__", BaseObjInst::eq, 2),
|
|
|
|
|
__gt__ => BuiltinFunctionInst::create("__gt__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__ge__ => BuiltinFunctionInst::create("__ge__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__lt__ => BuiltinFunctionInst::create("__lt__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__le__ => BuiltinFunctionInst::create("__le__", BaseObjInst::not_implemented_bin, 2),
|
|
|
|
|
__pos__ => BuiltinFunctionInst::create("__pos__", BaseObjInst::not_implemented_un, 1),
|
|
|
|
|
__neg__ => BuiltinFunctionInst::create("__neg__", BaseObjInst::not_implemented_un, 1),
|
|
|
|
|
__not__ => BuiltinFunctionInst::create("__not__", BaseObjInst::not, 1),
|
2024-09-23 20:59:00 -07:00
|
|
|
},
|
|
|
|
|
Obj { },
|
2024-09-24 14:47:44 -07:00
|
|
|
Str {
|
2024-09-25 10:22:03 -07:00
|
|
|
to_str => BuiltinFunctionInst::create("to_str", StrInst::to_str, 1),
|
|
|
|
|
to_repr => BuiltinFunctionInst::create("to_repr", StrInst::to_repr, 1),
|
|
|
|
|
len => BuiltinFunctionInst::create("len", StrInst::len, 1),
|
2024-09-24 14:47:44 -07:00
|
|
|
// Operators
|
2024-09-25 10:22:03 -07:00
|
|
|
__add__ => BuiltinFunctionInst::create("__add__", StrInst::add, 2),
|
|
|
|
|
__mul__ => BuiltinFunctionInst::create("__mul__", StrInst::mul, 2),
|
2024-09-25 08:48:11 -07:00
|
|
|
// .lower, .upper, .slice, etc
|
2024-09-24 14:47:44 -07:00
|
|
|
},
|
2024-09-24 16:57:22 -07:00
|
|
|
Int {
|
|
|
|
|
// Operators
|
2024-09-25 10:22:03 -07:00
|
|
|
__add__ => BuiltinFunctionInst::create("__add__", IntInst::add, 2),
|
|
|
|
|
__sub__ => BuiltinFunctionInst::create("__sub__", IntInst::sub, 2),
|
|
|
|
|
__mul__ => BuiltinFunctionInst::create("__mul__", IntInst::mul, 2),
|
|
|
|
|
__div__ => BuiltinFunctionInst::create("__div__", IntInst::div, 2),
|
|
|
|
|
__gt__ => BuiltinFunctionInst::create("__gt__", IntInst::gt, 2),
|
|
|
|
|
__ge__ => BuiltinFunctionInst::create("__ge__", IntInst::ge, 2),
|
|
|
|
|
__lt__ => BuiltinFunctionInst::create("__lt__", IntInst::lt, 2),
|
|
|
|
|
__le__ => BuiltinFunctionInst::create("__le__", IntInst::le, 2),
|
|
|
|
|
__pos__ => BuiltinFunctionInst::create("__pos__", IntInst::pos, 1),
|
|
|
|
|
__neg__ => BuiltinFunctionInst::create("__neg__", IntInst::neg, 1),
|
2024-09-24 16:57:22 -07:00
|
|
|
},
|
2024-09-23 20:59:00 -07:00
|
|
|
Float { },
|
|
|
|
|
Bool { },
|
|
|
|
|
Nil { },
|
|
|
|
|
BuiltinFunction { },
|
|
|
|
|
UserFunction { },
|
|
|
|
|
Method { },
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
}
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Convenience function for creating pointers, in case the `Arc<RwLock<T>>` pointer type has to
|
|
|
|
|
/// change.
|
|
|
|
|
///
|
|
|
|
|
/// 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.
|
2024-09-23 20:59:00 -07:00
|
|
|
pub fn make_ptr<T: Obj>(obj: T) -> ObjP {
|
|
|
|
|
upcast_obj(Ptr::new(GcCell::new(obj)))
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Obj
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
pub trait Obj: Debug + Display + Any + Trace {
|
2024-09-25 10:22:03 -07:00
|
|
|
fn instantiate(&mut self);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
fn is_instantiated(&self) -> bool;
|
|
|
|
|
fn attrs(&self) -> &Attrs;
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
fn attrs_mut(&mut self) -> &mut Attrs;
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
fn set_attr(&mut self, name: &str, value: ObjP) {
|
|
|
|
|
self.attrs_mut().insert(name.to_string(), value);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_attr(&self, name: &str) -> Option<ObjP> {
|
2024-09-25 10:22:03 -07:00
|
|
|
self.attrs().get(name).map(ObjP::clone)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_vtable_attr(&self, self_ptr: ObjP, name: &str) -> Option<ObjP> {
|
2024-09-23 20:59:00 -07:00
|
|
|
// check attrs, then check vtable
|
2024-09-25 10:22:03 -07:00
|
|
|
let attr = self.attrs().get(name).map(ObjP::clone);
|
2024-09-23 20:59:00 -07:00
|
|
|
if attr.is_some() {
|
|
|
|
|
return attr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut type_inst = self.type_inst();
|
|
|
|
|
loop {
|
2024-09-23 21:48:44 -07:00
|
|
|
let vtable_entry =
|
|
|
|
|
with_obj_downcast_mut(type_inst.clone(), |type_inst: &mut TypeInst| {
|
|
|
|
|
type_inst.vtable.get(name).cloned()
|
|
|
|
|
})
|
|
|
|
|
.map(|vtable_entry| {
|
|
|
|
|
let ptr = if obj_is_inst::<BuiltinFunctionInst>(&vtable_entry)
|
|
|
|
|
|| obj_is_inst::<UserFunctionInst>(&vtable_entry)
|
|
|
|
|
{
|
2024-09-25 10:22:03 -07:00
|
|
|
MethodInst::create(self_ptr.clone(), vtable_entry)
|
2024-09-23 21:48:44 -07:00
|
|
|
} else {
|
|
|
|
|
vtable_entry
|
|
|
|
|
};
|
2024-09-25 10:22:03 -07:00
|
|
|
// TODO Obj::get_attr - cache the vtable result somehow? we aren't caching for
|
|
|
|
|
// speed, but rather so we don't have a million different method objects
|
|
|
|
|
// floating around.
|
|
|
|
|
//self.set_attr(name, ptr.clone());
|
2024-09-23 21:48:44 -07:00
|
|
|
ptr
|
|
|
|
|
});
|
|
|
|
|
if vtable_entry.is_some() {
|
|
|
|
|
return vtable_entry;
|
|
|
|
|
}
|
2024-09-23 20:59:00 -07:00
|
|
|
let type_inst_copy = type_inst.borrow().type_inst();
|
|
|
|
|
if type_inst.borrow().equals(&*type_inst_copy.borrow()) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
type_inst = type_inst_copy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
fn type_inst(&self) -> ObjP;
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
fn type_name(&self) -> Rc<String> {
|
2024-09-20 16:04:30 -07:00
|
|
|
with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
Rc::clone(&type_inst.name)
|
2024-09-20 16:04:30 -07:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn arity(&self) -> Option<Argc> {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
fn call(&self, _vm: &mut Vm, _argc: Argc) {
|
2024-09-20 16:04:30 -07:00
|
|
|
// TODO Obj::call - need to handle "this object cannot be called" errors
|
|
|
|
|
// BLOCKED-ON: exceptions
|
|
|
|
|
todo!("Raise some kind of not implemented/not callable error for non-callable objects")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool;
|
|
|
|
|
|
|
|
|
|
fn as_any(&self) -> &dyn Any;
|
|
|
|
|
|
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// BaseObjInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2024-09-24 12:37:46 -07:00
|
|
|
#[derive(Default, Debug, Trace)]
|
2024-09-24 11:50:51 -07:00
|
|
|
pub(crate) struct BaseObjInst {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
attrs: Attrs,
|
2024-09-20 16:04:30 -07:00
|
|
|
is_instantiated: bool,
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for BaseObjInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
WIP: move mutability to be internal to the object instead of the pointer
I'm not super happy with this. But, the RwLock has been moved to the
`BaseObjInst::attrs` member. Although this is not exactly how it appears
in code, it basically does this:
type Ptr<T> = Arc<RwLock<T>>;
struct BaseObjInst {
attr: HashMap<String, Ptr<dyn Obj>>,
// etc
}
becomes
type Ptr<T> = Arc<T>;
struct BaseObjInst {
attr: RwLock<HashMap<String, ObjP>>,
// etc
}
This makes things a lot more ergonomic (don't have to use try_read() and
try_write() everywhere), but it also eliminates compile-time errors that
would catch mutability errors. This is currently rearing its ugly head
when initializing the typesystem, since `Type` needs to hold a circular
reference itself (which it already shouldn't be doing since it's a
reference-counted pointer!). Currently, all tests are failing because of
this limitation.
There are a couple of ways around this limitation.
The first solution would be just copying all of the object
instantiation code into the `init_types` function and avoid calling
`some_base_type.instantiate()`. This would probably be literal
copy-pasting, or maybe an (ugly) macro, and probably a nightmare to
maintain long-term. I don't like this option, but it would make
everything "just work" with reference-counted pointers.
The second solution would be to write our own garbage collector, which
would allow for circular references and (hypothetically) mutably
updating these references. This is something that I am looking into,
because I really want a RefCell that you can pass around in a more
ergonomic way.
I think the fundamental error that I'm running into is trying to borrow
the same value multiple times mutably, which you *really* shouldn't be
doing. I believe I need to write better code and does the same thing.
The only unsolved problem is circular references. This is not a problem
right now because I'm not writing code that has circular references
besides the base typesystem (which is not a problem because they need to
live the entire lifetime of the program), but it will be a latent
problem until it gets fixed.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-22 20:40:15 -07:00
|
|
|
impl Clone for BaseObjInst {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
attrs: self.attrs.clone(),
|
WIP: move mutability to be internal to the object instead of the pointer
I'm not super happy with this. But, the RwLock has been moved to the
`BaseObjInst::attrs` member. Although this is not exactly how it appears
in code, it basically does this:
type Ptr<T> = Arc<RwLock<T>>;
struct BaseObjInst {
attr: HashMap<String, Ptr<dyn Obj>>,
// etc
}
becomes
type Ptr<T> = Arc<T>;
struct BaseObjInst {
attr: RwLock<HashMap<String, ObjP>>,
// etc
}
This makes things a lot more ergonomic (don't have to use try_read() and
try_write() everywhere), but it also eliminates compile-time errors that
would catch mutability errors. This is currently rearing its ugly head
when initializing the typesystem, since `Type` needs to hold a circular
reference itself (which it already shouldn't be doing since it's a
reference-counted pointer!). Currently, all tests are failing because of
this limitation.
There are a couple of ways around this limitation.
The first solution would be just copying all of the object
instantiation code into the `init_types` function and avoid calling
`some_base_type.instantiate()`. This would probably be literal
copy-pasting, or maybe an (ugly) macro, and probably a nightmare to
maintain long-term. I don't like this option, but it would make
everything "just work" with reference-counted pointers.
The second solution would be to write our own garbage collector, which
would allow for circular references and (hypothetically) mutably
updating these references. This is something that I am looking into,
because I really want a RefCell that you can pass around in a more
ergonomic way.
I think the fundamental error that I'm running into is trying to borrow
the same value multiple times mutably, which you *really* shouldn't be
doing. I believe I need to write better code and does the same thing.
The only unsolved problem is circular references. This is not a problem
right now because I'm not writing code that has circular references
besides the base typesystem (which is not a problem because they need to
live the entire lifetime of the program), but it will be a latent
problem until it gets fixed.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-22 20:40:15 -07:00
|
|
|
is_instantiated: self.is_instantiated,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Display for BaseObjInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "<BaseObjInst at {:x}>", (self as *const _ as usize))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for BaseObjInst {
|
2024-09-25 10:22:03 -07:00
|
|
|
fn instantiate(&mut self) {
|
2024-09-20 16:04:30 -07:00
|
|
|
self.is_instantiated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_instantiated(&self) -> bool {
|
|
|
|
|
self.is_instantiated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn attrs(&self) -> &Attrs {
|
|
|
|
|
&self.attrs
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
fn attrs_mut(&mut self) -> &mut Attrs {
|
|
|
|
|
&mut self.attrs
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
fn type_inst(&self) -> ObjP {
|
|
|
|
|
self.get_attr("__type__").expect("no __type__")
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<BaseObjInst>() {
|
|
|
|
|
// compare all attrs
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
self.attrs.iter().all(|(k1, v1)| {
|
|
|
|
|
other
|
|
|
|
|
.attrs
|
2024-09-20 16:04:30 -07:00
|
|
|
.get(k1)
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
.map(|v2| v2.borrow().equals(&*v1.borrow()))
|
2024-09-20 16:04:30 -07:00
|
|
|
.unwrap_or(false)
|
|
|
|
|
}) && self.is_instantiated == other.is_instantiated
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// ObjInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Debug, Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct ObjInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ObjInst {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!();
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for ObjInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Display for ObjInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "<ObjInst at {:x}>", (self as *const _ as usize))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for ObjInst {
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<ObjInst>() {
|
|
|
|
|
self.base.equals(&other.base)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Obj);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// TypeInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct TypeInst {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[unsafe_ignore_trace]
|
|
|
|
|
name: Rc<String>,
|
2024-09-20 16:04:30 -07:00
|
|
|
base: BaseObjInst,
|
|
|
|
|
vtable: HashMap<String, ObjP>,
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for TypeInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl TypeInst {
|
|
|
|
|
pub fn new(name: impl ToString) -> Self {
|
|
|
|
|
Self {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
name: Rc::new(name.to_string()),
|
2024-09-20 16:04:30 -07:00
|
|
|
base: Default::default(),
|
|
|
|
|
vtable: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!(name: impl ToString);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
pub fn name(&self) -> &Rc<String> {
|
2024-09-20 16:04:30 -07:00
|
|
|
&self.name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Debug for TypeInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
fmt,
|
|
|
|
|
"<Type {} at {:x}>",
|
|
|
|
|
self.name,
|
|
|
|
|
(self as *const _ as usize)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for TypeInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
fmt,
|
|
|
|
|
"<Type {} at {:x}>",
|
|
|
|
|
self.name,
|
|
|
|
|
(self as *const _ as usize)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for TypeInst {
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<TypeInst>() {
|
|
|
|
|
// TODO TypeInst::equals : something more robust than this
|
|
|
|
|
// Types should hold equality if they have the same name
|
|
|
|
|
// the problem is that Type.get_attr("__type__") is going to return itself, so we have
|
|
|
|
|
// to go through attributes to specially exclude to the __type__ attribute if it points
|
|
|
|
|
// to ourself.
|
|
|
|
|
// How do we detect that it's pointing to ourself? I suppose pointers are the way
|
|
|
|
|
self.name == other.name
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Type);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// StrInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Debug, Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct StrInst {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[unsafe_ignore_trace]
|
|
|
|
|
str_value: Rc<String>,
|
2024-09-20 16:04:30 -07:00
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl StrInst {
|
|
|
|
|
pub fn new(str_value: impl ToString) -> Self {
|
|
|
|
|
Self {
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
str_value: Rc::new(str_value.to_string()),
|
2024-09-20 16:04:30 -07:00
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!(str_value: impl ToString);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
pub fn str_value(&self) -> &Rc<String> {
|
2024-09-20 16:04:30 -07:00
|
|
|
&self.str_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for StrInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for StrInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.str_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Obj for StrInst {
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
!self.str_value.is_empty()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<StrInst>() {
|
|
|
|
|
self.str_value == other.str_value
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Str);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// IntInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Debug, Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct IntInst {
|
|
|
|
|
int_value: i64,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IntInst {
|
|
|
|
|
pub fn new(int_value: i64) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
int_value,
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!(int_value: i64);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
pub fn int_value(&self) -> i64 {
|
|
|
|
|
self.int_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for IntInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Display for IntInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.int_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for IntInst {
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
self.int_value != 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<IntInst>() {
|
|
|
|
|
self.int_value == other.int_value
|
|
|
|
|
} else if let Some(other) = other.as_any().downcast_ref::<FloatInst>() {
|
|
|
|
|
self.int_value as f64 == other.float_value
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Int);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// FloatInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Debug, Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct FloatInst {
|
|
|
|
|
float_value: f64,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FloatInst {
|
|
|
|
|
pub fn new(float_value: f64) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
float_value,
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!(float_value: f64);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
pub fn float_value(&self) -> f64 {
|
|
|
|
|
self.float_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for FloatInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Display for FloatInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.float_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for FloatInst {
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
self.float_value != 0.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<FloatInst>() {
|
|
|
|
|
self.float_value == other.float_value
|
|
|
|
|
} else if let Some(other) = other.as_any().downcast_ref::<IntInst>() {
|
|
|
|
|
self.float_value == other.int_value as f64
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Float);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// BoolInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Debug, Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct BoolInst {
|
|
|
|
|
bool_value: bool,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BoolInst {
|
|
|
|
|
pub fn new(bool_value: bool) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
bool_value,
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!(bool_value: bool);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
pub fn bool_value(&self) -> bool {
|
|
|
|
|
self.bool_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for BoolInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Display for BoolInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.bool_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for BoolInst {
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
self.bool_value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<BoolInst>() {
|
|
|
|
|
self.bool_value == other.bool_value
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Bool);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// NilInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
#[derive(Debug, Default, Trace)]
|
2024-09-20 16:04:30 -07:00
|
|
|
pub struct NilInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl NilInst {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Default::default()
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 20:59:00 -07:00
|
|
|
impl_create!();
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
impl Finalize for NilInst {
|
|
|
|
|
fn finalize(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 16:04:30 -07:00
|
|
|
impl Display for NilInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for NilInst {
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
other.as_any().downcast_ref::<NilInst>().is_some()
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
impl_base_obj!(Nil);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Tests
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_new_objects() {
|
2024-09-25 10:22:03 -07:00
|
|
|
init_types();
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let type_value = TypeInst::create("Type");
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert_eq!(&*type_value.borrow().type_name(), "Type");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let str_value = StrInst::create("asdfasdfasdfasdfasdf");
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert_eq!(&*str_value.borrow().type_name(), "Str");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let int_value = IntInst::create(1234);
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert_eq!(&*int_value.borrow().type_name(), "Int");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let float_value = FloatInst::create(1234.5678);
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert_eq!(&*float_value.borrow().type_name(), "Float");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let nil_value = NilInst::create();
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert_eq!(&*nil_value.borrow().type_name(), "Nil");
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_obj_equals() {
|
2024-09-25 10:22:03 -07:00
|
|
|
init_types();
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let int1 = IntInst::create(1234);
|
|
|
|
|
let int2 = IntInst::create(1234);
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(int1.borrow().equals(&*int2.borrow()));
|
|
|
|
|
assert!(int2.borrow().equals(&*int1.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let float1 = FloatInst::create(1234.0);
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(int1.borrow().equals(&*float1.borrow()));
|
|
|
|
|
assert!(float1.borrow().equals(&*int2.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
// self-equality
|
2024-09-25 10:22:03 -07:00
|
|
|
let str1 = StrInst::create("1234");
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(str1.borrow().equals(&*str1.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let str2 = StrInst::create("1234");
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(str1.borrow().equals(&*str2.borrow()));
|
|
|
|
|
assert!(str2.borrow().equals(&*str1.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(!str1.borrow().equals(&*float1.borrow()));
|
|
|
|
|
assert!(!str1.borrow().equals(&*int1.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
2024-09-25 10:22:03 -07:00
|
|
|
let obj1 = ObjInst::create();
|
|
|
|
|
let obj2 = ObjInst::create();
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(obj1.borrow().equals(&*obj2.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
// these objects aren't equal anymore
|
2024-09-23 20:59:00 -07:00
|
|
|
obj1.borrow_mut().set_attr("my_attr", str2.clone());
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(!obj1.borrow().equals(&*obj2.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
// but now they are!
|
2024-09-23 20:59:00 -07:00
|
|
|
obj2.borrow_mut().set_attr("my_attr", str2.clone());
|
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>
2024-09-23 18:12:32 -07:00
|
|
|
assert!(obj2.borrow().equals(&*obj1.borrow()));
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
2024-09-23 20:59:00 -07:00
|
|
|
|
2024-09-23 21:48:44 -07:00
|
|
|
#[test]
|
2024-09-23 20:59:00 -07:00
|
|
|
fn test_obj_vtable() {
|
2024-09-25 10:22:03 -07:00
|
|
|
init_types();
|
|
|
|
|
let str1 = StrInst::create("asdfasdfasdf");
|
|
|
|
|
|
|
|
|
|
let to_string_ptr = str1.borrow_mut().get_vtable_attr(str1.clone(), "to_str");
|
2024-09-23 20:59:00 -07:00
|
|
|
assert!(to_string_ptr.is_some());
|
|
|
|
|
|
|
|
|
|
let to_string_ptr = to_string_ptr.unwrap();
|
|
|
|
|
assert!(obj_is_inst::<MethodInst>(&to_string_ptr));
|
|
|
|
|
with_obj_downcast(to_string_ptr.clone(), |method: &MethodInst| {
|
2024-09-24 09:05:31 -07:00
|
|
|
assert!(method.self_binding().borrow().equals(&*str1.borrow()));
|
2024-09-23 20:59:00 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// now get the method's to_string ptr
|
2024-09-25 10:22:03 -07:00
|
|
|
let method_to_string_ptr = to_string_ptr
|
|
|
|
|
.borrow_mut()
|
|
|
|
|
.get_vtable_attr(to_string_ptr.clone(), "to_str");
|
2024-09-23 20:59:00 -07:00
|
|
|
assert!(method_to_string_ptr.is_some());
|
|
|
|
|
|
|
|
|
|
// this is like doing "asdfasdfasdf".to_string().to_string()
|
|
|
|
|
let method_to_string_ptr = method_to_string_ptr.unwrap();
|
|
|
|
|
assert!(obj_is_inst::<MethodInst>(&method_to_string_ptr));
|
|
|
|
|
with_obj_downcast(method_to_string_ptr.clone(), |method: &MethodInst| {
|
|
|
|
|
assert!(method
|
2024-09-24 09:05:31 -07:00
|
|
|
.self_binding()
|
2024-09-23 20:59:00 -07:00
|
|
|
.borrow()
|
|
|
|
|
.equals(&*to_string_ptr.borrow()));
|
|
|
|
|
});
|
|
|
|
|
}
|