2024-09-20 16:04:30 -07:00
|
|
|
// TODO obj.rs - remove the warning suppression
|
|
|
|
|
#![allow(unused_variables, dead_code)]
|
|
|
|
|
|
|
|
|
|
use std::any::Any;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::fmt::{self, Debug, Display};
|
|
|
|
|
use std::ptr;
|
|
|
|
|
use std::sync::{Arc, LazyLock, Mutex, RwLock};
|
|
|
|
|
|
|
|
|
|
use common_macros::hash_map;
|
|
|
|
|
|
|
|
|
|
use crate::vm::{Argc, Chunk, Frame, Vm};
|
|
|
|
|
|
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
|
|
|
pub type Ptr<T> = Arc<T>;
|
2024-09-20 16:04:30 -07:00
|
|
|
pub type ObjP = Ptr<dyn Obj + 'static>;
|
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
|
|
|
pub type Attrs = RwLock<HashMap<String, ObjP>>;
|
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,
|
|
|
|
|
{
|
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
|
|
|
if let Some(obj) = ptr.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>()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn obj_is_inst<T>(ptr: &ObjP) -> bool
|
|
|
|
|
where
|
|
|
|
|
T: Obj + 'static,
|
|
|
|
|
{
|
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.as_any().downcast_ref::<T>().is_some()
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Builtin types macro
|
|
|
|
|
macro_rules! builtin_types {
|
|
|
|
|
(
|
|
|
|
|
$(
|
|
|
|
|
$type_name:ident => { $( $vtable_name:ident => $vtable_value:expr ),* $(,)? }
|
|
|
|
|
),+ $(,)?
|
|
|
|
|
) => {
|
|
|
|
|
pub static TYPES: LazyLock<RwLock<HashMap<String, Ptr<TypeInst>>>> = LazyLock::new(|| {
|
|
|
|
|
RwLock::new(hash_map! {
|
|
|
|
|
$(
|
|
|
|
|
stringify!($type_name).to_string() => make_ptr(TypeInst::new(stringify!($type_name)))
|
|
|
|
|
),+
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
static TYPE_SYSTEM_INIT: LazyLock<Mutex<bool>> = LazyLock::new(|| Mutex::new(false));
|
|
|
|
|
|
|
|
|
|
/// Initialize types.
|
|
|
|
|
///
|
|
|
|
|
/// This should only be called once.
|
|
|
|
|
fn init_base_types() {
|
|
|
|
|
// instantiate
|
|
|
|
|
$(
|
|
|
|
|
if stringify!($type_name) != "Type" {
|
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
|
|
|
let mut types_ptr = TYPES.try_write().unwrap();
|
|
|
|
|
let mut ty = types_ptr.get_mut(stringify!($type_name)).unwrap();
|
|
|
|
|
Arc::get_mut(&mut ty).unwrap().instantiate();
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
)+
|
|
|
|
|
|
|
|
|
|
// __name__
|
|
|
|
|
$(
|
|
|
|
|
{
|
|
|
|
|
let name = StrInst::create(stringify!($type_name));
|
|
|
|
|
let ty = Ptr::clone(&TYPES.try_read().unwrap()[stringify!($type_name)]);
|
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
|
|
|
ty.set_attr("__name__", name);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
)+
|
|
|
|
|
|
|
|
|
|
// vtable
|
|
|
|
|
$(
|
|
|
|
|
{
|
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
|
|
|
let ptr = Ptr::clone(&TYPES.try_write().unwrap()[stringify!($type_name)]);
|
2024-09-20 16:04:30 -07:00
|
|
|
$(
|
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.vtable.insert($vtable_name.into(), $vtable_value);
|
2024-09-20 16:04:30 -07:00
|
|
|
)*
|
|
|
|
|
}
|
|
|
|
|
)+
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn init_types() {
|
|
|
|
|
// Taking the lock here will lock the entire function from being run twice
|
|
|
|
|
// simulataneously and prevent race conditions.
|
|
|
|
|
//
|
|
|
|
|
// Race conditions really can only happen during testing so this is just a precaution.
|
|
|
|
|
let mut lock_guard = TYPE_SYSTEM_INIT.lock().unwrap();
|
|
|
|
|
if *lock_guard {
|
|
|
|
|
if cfg!(test) {
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
panic!("do not initialize type system twice");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Init type_type here
|
|
|
|
|
{
|
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
|
|
|
let types_ptr = TYPES.try_read().unwrap();
|
|
|
|
|
let type_ptr = types_ptr.get("Type").unwrap();
|
|
|
|
|
type_ptr.set_attr(
|
2024-09-20 16:04:30 -07:00
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP,
|
|
|
|
|
);
|
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
|
|
|
drop(types_ptr);
|
|
|
|
|
//let mut types_ptr = TYPES.try_write().unwrap();
|
|
|
|
|
//let mut type_ptr = types_ptr.get_mut("Type").unwrap();
|
|
|
|
|
//Arc::get_mut(&mut type_ptr).unwrap().base.is_instantiated = true;
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Init the rest of the types
|
|
|
|
|
init_base_types();
|
|
|
|
|
|
|
|
|
|
*lock_guard = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn placeholder(_: &mut Vm, _: Vec<ObjP>) -> ObjP {
|
|
|
|
|
NilInst::create()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn to_string(_: &mut Vm, args: Vec<ObjP>) -> ObjP {
|
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
|
|
|
let str_value = format!("{}", args[0]);
|
2024-09-20 16:04:30 -07:00
|
|
|
StrInst::create(str_value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builtin_types! {
|
|
|
|
|
Type => {},
|
|
|
|
|
Obj => {},
|
|
|
|
|
Str => {},
|
|
|
|
|
Int => {},
|
|
|
|
|
Float => {},
|
|
|
|
|
Bool => {},
|
|
|
|
|
Nil => {},
|
|
|
|
|
BuiltinFunction => {},
|
|
|
|
|
UserFunction => {},
|
|
|
|
|
Method => {},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
|
pub fn make_ptr<T: Obj>(obj: T) -> Ptr<T> {
|
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
|
|
|
Arc::new(obj)
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Obj
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
pub trait Obj: Debug + Display + Any + Send + Sync {
|
|
|
|
|
fn instantiate(&mut self);
|
|
|
|
|
|
|
|
|
|
fn is_instantiated(&self) -> bool;
|
|
|
|
|
fn attrs(&self) -> &Attrs;
|
|
|
|
|
|
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
|
|
|
fn set_attr(&self, name: &str, value: ObjP) {
|
|
|
|
|
let mut borrowed = self.attrs().try_write().unwrap();
|
|
|
|
|
borrowed.insert(name.to_string(), value);
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_attr(&self, name: &str) -> Option<ObjP> {
|
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
|
|
|
let borrowed = self.attrs().try_read().unwrap();
|
|
|
|
|
borrowed.get(name).map(Arc::clone)
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn type_inst(&self) -> ObjP {
|
|
|
|
|
self.get_attr("__type__").unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn type_name(&self) -> Arc<String> {
|
|
|
|
|
with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| {
|
|
|
|
|
Arc::clone(&type_inst.name)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn arity(&self) -> Option<Argc> {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn call(&self, vm: &mut Vm, argc: Argc) {
|
|
|
|
|
// 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
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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
|
|
|
#[derive(Debug, Default)]
|
2024-09-20 16:04:30 -07:00
|
|
|
struct BaseObjInst {
|
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
|
|
|
attrs: RwLock<HashMap<String, ObjP>>,
|
2024-09-20 16:04:30 -07:00
|
|
|
is_instantiated: bool,
|
|
|
|
|
}
|
|
|
|
|
|
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 {
|
|
|
|
|
attrs: RwLock::new(self.attrs.try_read().unwrap().clone()),
|
|
|
|
|
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 {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
if self.get_attr("__type__").is_none() {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Obj"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO BaseObjInst::instantiate - instantiate VTable
|
|
|
|
|
// Okay, we are running into a little snag here:
|
|
|
|
|
// * TypeInst::vtable holds a collection of named objects that will get copied into the
|
|
|
|
|
// attributes during instatiation.
|
|
|
|
|
// * If an object that gets copied is a function (UserFunctionInst, BuiltinFunctionInst),
|
|
|
|
|
// it will be wrapped by a MethodInst
|
|
|
|
|
// * MethodInst requires a pointer to the function being wrapped, as well as a pointer to
|
|
|
|
|
// the "self" object.
|
|
|
|
|
// * This is the root of the problem - ***we need the pointer to the object that we are
|
|
|
|
|
// currently instantiating.***
|
|
|
|
|
/*
|
|
|
|
|
let type_inst_ptr = Ptr::clone(&self.type_inst());
|
|
|
|
|
with_obj_downcast(type_inst_ptr, |type_inst: &TypeInst| {
|
|
|
|
|
for (key, value_ptr) in type_inst.vtable.iter() {
|
|
|
|
|
// copy functions over as MethodInst
|
|
|
|
|
if obj_is_inst::<BuiltinFunctionInst>(&value_ptr)
|
|
|
|
|
|| obj_is_inst::<UserFunctionInst>(&value_ptr)
|
|
|
|
|
{
|
|
|
|
|
self.set_attr(key, MethodInst::create(value_ptr));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
self.is_instantiated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_instantiated(&self) -> bool {
|
|
|
|
|
self.is_instantiated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn attrs(&self) -> &Attrs {
|
|
|
|
|
&self.attrs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<BaseObjInst>() {
|
|
|
|
|
// compare all attrs
|
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
|
|
|
let borrowed = self.attrs.try_read().unwrap();
|
|
|
|
|
borrowed.iter().all(|(k1, v1)| {
|
|
|
|
|
let borrowed = other.attrs.try_read().unwrap();
|
|
|
|
|
borrowed
|
2024-09-20 16:04:30 -07:00
|
|
|
.get(k1)
|
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
|
|
|
.map(|v2| v2.equals(v1.as_ref()))
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
macro_rules! impl_base_obj {
|
|
|
|
|
($base_name:ident) => {
|
|
|
|
|
fn is_instantiated(&self) -> bool {
|
|
|
|
|
self.$base_name.is_instantiated()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn attrs(&self) -> &Attrs {
|
|
|
|
|
self.$base_name.attrs()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
() => {
|
|
|
|
|
impl_base_obj! { base }
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// ObjInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct ObjInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ObjInst {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create() -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new();
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 instantiate(&mut self) {
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<ObjInst>() {
|
|
|
|
|
self.base.equals(&other.base)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// TypeInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
pub struct TypeInst {
|
|
|
|
|
name: Arc<String>,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
vtable: HashMap<String, ObjP>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TypeInst {
|
|
|
|
|
pub fn new(name: impl ToString) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
name: Arc::new(name.to_string()),
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
vtable: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(name: impl ToString) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(name);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn name(&self) -> &Arc<String> {
|
|
|
|
|
&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 instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// StrInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct StrInst {
|
|
|
|
|
str_value: Arc<String>,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for StrInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.str_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl StrInst {
|
|
|
|
|
pub fn new(str_value: impl ToString) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
str_value: Arc::new(str_value.to_string()),
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(str_value: impl ToString) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(str_value);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn str_value(&self) -> &Arc<String> {
|
|
|
|
|
&self.str_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for StrInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Str"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// IntInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct IntInst {
|
|
|
|
|
int_value: i64,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IntInst {
|
|
|
|
|
pub fn new(int_value: i64) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
int_value,
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(int_value: i64) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(int_value);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn int_value(&self) -> i64 {
|
|
|
|
|
self.int_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for IntInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.int_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for IntInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Int"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// FloatInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct FloatInst {
|
|
|
|
|
float_value: f64,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FloatInst {
|
|
|
|
|
pub fn new(float_value: f64) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
float_value,
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(float_value: f64) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(float_value);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn float_value(&self) -> f64 {
|
|
|
|
|
self.float_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for FloatInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.float_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for FloatInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Float"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// BoolInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct BoolInst {
|
|
|
|
|
bool_value: bool,
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BoolInst {
|
|
|
|
|
pub fn new(bool_value: bool) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
bool_value,
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(bool_value: bool) -> Ptr<Self> {
|
|
|
|
|
// TODO BoolInst::create : interning
|
|
|
|
|
let mut new = Self::new(bool_value);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn bool_value(&self) -> bool {
|
|
|
|
|
self.bool_value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for BoolInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "{}", self.bool_value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for BoolInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Bool"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// NilInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
|
pub struct NilInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl NilInst {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Default::default()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create() -> Ptr<Self> {
|
|
|
|
|
// TODO NilInst::create : interning
|
|
|
|
|
let mut new = Self::new();
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for NilInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(fmt, "nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for NilInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Nil"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_truthy(&self) -> bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
other.as_any().downcast_ref::<NilInst>().is_some()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// BuiltinFunctionInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec<ObjP>) -> ObjP;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct BuiltinFunctionInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
name: String,
|
|
|
|
|
function: BuiltinFunctionPtr,
|
|
|
|
|
arity: Argc,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BuiltinFunctionInst {
|
|
|
|
|
pub fn new(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
name,
|
|
|
|
|
function,
|
|
|
|
|
arity,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(name, function, arity);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn name(&self) -> &String {
|
|
|
|
|
&self.name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for BuiltinFunctionInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
fmt,
|
|
|
|
|
"<BuiltinFunction {}/{} at 0x{:x}>",
|
|
|
|
|
self.name(),
|
|
|
|
|
self.arity().unwrap(),
|
|
|
|
|
self.function as *const BuiltinFunctionPtr as usize
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for BuiltinFunctionInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn arity(&self) -> Option<Argc> {
|
|
|
|
|
Some(self.arity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn call(&self, vm: &mut Vm, argc: Argc) {
|
|
|
|
|
// args
|
|
|
|
|
let mut args = Vec::with_capacity(argc as usize);
|
|
|
|
|
for _ in 0..argc {
|
|
|
|
|
args.push(vm.pop());
|
|
|
|
|
}
|
|
|
|
|
args.reverse();
|
|
|
|
|
// callee (self)
|
|
|
|
|
vm.pop();
|
|
|
|
|
let result = (self.function)(vm, args);
|
|
|
|
|
vm.push(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
// TODO BuiltinFunctionInst::equals : need something more robust than checking addr_eq,
|
|
|
|
|
// maybe check the self_binding pointer too?
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<BuiltinFunctionInst>() {
|
|
|
|
|
ptr::addr_eq(self, other)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// UserFunctionInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct UserFunctionInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
name: Arc<String>,
|
|
|
|
|
chunk: Arc<Chunk>,
|
|
|
|
|
arity: Argc,
|
|
|
|
|
captures: Vec<ObjP>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UserFunctionInst {
|
|
|
|
|
pub fn new(chunk: Chunk, arity: Argc) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
name: Arc::new("(anonymous)".to_string()),
|
|
|
|
|
chunk: Arc::new(chunk),
|
|
|
|
|
arity,
|
|
|
|
|
captures: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(chunk: Chunk, arity: Argc) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(chunk, arity);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn name(&self) -> &String {
|
|
|
|
|
&self.name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_name(&mut self, name: Arc<String>) {
|
|
|
|
|
self.name = name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn chunk(&self) -> &Chunk {
|
|
|
|
|
&self.chunk
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn push_capture(&mut self, value: ObjP) {
|
|
|
|
|
self.captures.push(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for UserFunctionInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
fmt,
|
|
|
|
|
"<UserFunction {}/{} at 0x{:x}>",
|
|
|
|
|
self.name(),
|
|
|
|
|
self.arity().unwrap(),
|
|
|
|
|
self as *const _ as usize
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for UserFunctionInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn arity(&self) -> Option<Argc> {
|
|
|
|
|
Some(self.arity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn call(&self, vm: &mut Vm, argc: Argc) {
|
|
|
|
|
assert_eq!(argc, self.arity, "argc must match arity");
|
|
|
|
|
let new_frame = Frame {
|
|
|
|
|
name: Arc::clone(&self.name),
|
|
|
|
|
chunk: Arc::clone(&self.chunk),
|
|
|
|
|
ip: 0,
|
|
|
|
|
stack_base: vm.stack().len() - (argc as usize),
|
|
|
|
|
};
|
|
|
|
|
vm.push_frame(new_frame);
|
|
|
|
|
for capture in &self.captures {
|
|
|
|
|
vm.push(Ptr::clone(&capture));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<UserFunctionInst>() {
|
|
|
|
|
// TODO UserFunctionInst::equals : need something more robust than checking addr_eq.
|
|
|
|
|
ptr::addr_eq(self, other)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// MethodInst
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct MethodInst {
|
|
|
|
|
base: BaseObjInst,
|
|
|
|
|
self_binding: ObjP,
|
|
|
|
|
function: ObjP,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MethodInst {
|
|
|
|
|
pub fn new(self_binding: ObjP, function: ObjP) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
base: Default::default(),
|
|
|
|
|
self_binding,
|
|
|
|
|
function,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(self_binding: ObjP, function: ObjP) -> Ptr<Self> {
|
|
|
|
|
let mut new = Self::new(self_binding, function);
|
|
|
|
|
new.instantiate();
|
|
|
|
|
make_ptr(new)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn self_binding(&self) -> &ObjP {
|
|
|
|
|
&self.self_binding
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for MethodInst {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
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
|
|
|
write!(fmt, "{}", self.function)
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj for MethodInst {
|
|
|
|
|
fn instantiate(&mut self) {
|
|
|
|
|
self.set_attr(
|
|
|
|
|
"__type__",
|
|
|
|
|
Ptr::clone(&TYPES.try_read().unwrap()["Method"]) as ObjP,
|
|
|
|
|
);
|
|
|
|
|
self.base.instantiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn arity(&self) -> Option<Argc> {
|
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
|
|
|
self.function.arity()
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn call(&self, vm: &mut Vm, argc: Argc) {
|
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
|
|
|
self.function.call(vm, argc)
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn equals(&self, other: &dyn Obj) -> bool {
|
|
|
|
|
if let Some(other) = other.as_any().downcast_ref::<MethodInst>() {
|
|
|
|
|
ptr::addr_eq(&*self.self_binding, &*other.self_binding)
|
|
|
|
|
&& ptr::addr_eq(&*self.function, &*other.function)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_base_obj!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Tests
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_new_objects() {
|
|
|
|
|
init_types();
|
|
|
|
|
|
|
|
|
|
let type_value = TypeInst::create("Type");
|
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
|
|
|
assert_eq!(&*type_value.type_name(), "Type");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let str_value = StrInst::create("asdfasdfasdfasdfasdf");
|
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
|
|
|
assert_eq!(&*str_value.type_name(), "Str");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let int_value = IntInst::create(1234);
|
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
|
|
|
assert_eq!(&*int_value.type_name(), "Int");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let float_value = FloatInst::create(1234.5678);
|
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
|
|
|
assert_eq!(&*float_value.type_name(), "Float");
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let nil_value = NilInst::create();
|
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
|
|
|
assert_eq!(&*nil_value.type_name(), "Nil");
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_obj_equals() {
|
|
|
|
|
init_types();
|
|
|
|
|
|
|
|
|
|
let int1 = IntInst::create(1234);
|
|
|
|
|
let int2 = IntInst::create(1234);
|
|
|
|
|
|
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
|
|
|
assert!(int1.equals(int2.as_ref()));
|
|
|
|
|
assert!(int2.equals(int1.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let float1 = FloatInst::create(1234.0);
|
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
|
|
|
assert!(int1.equals(float1.as_ref()));
|
|
|
|
|
assert!(float1.equals(int2.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
// self-equality
|
|
|
|
|
let str1 = StrInst::create("1234");
|
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
|
|
|
assert!(str1.equals(str1.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let str2 = StrInst::create("1234");
|
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
|
|
|
assert!(str1.equals(str2.as_ref()));
|
|
|
|
|
assert!(str2.equals(str1.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
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
|
|
|
assert!(!str1.equals(float1.as_ref()));
|
|
|
|
|
assert!(!str1.equals(int1.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
let obj1 = ObjInst::create();
|
|
|
|
|
let obj2 = ObjInst::create();
|
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
|
|
|
assert!(obj1.equals(obj2.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
// these objects aren't equal anymore
|
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
|
|
|
obj1.set_attr("my_attr", Ptr::clone(&str2) as ObjP);
|
|
|
|
|
assert!(!obj1.equals(obj2.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
|
|
|
|
|
// but now they are!
|
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
|
|
|
obj2.set_attr("my_attr", Ptr::clone(&str2) as ObjP);
|
|
|
|
|
assert!(obj2.equals(obj1.as_ref()));
|
2024-09-20 16:04:30 -07:00
|
|
|
}
|