Most object types get their own file now
This is hopefully going to make navigating the source tree easier. Hopefully. The only types that don't get their own files are: * function types (UserFunction, BuiltinFunction, Method), which all live in obj/function.rs * Nil, which lives in obj.rs * Obj, which lives in obj.rs Type definitions and init_types now live in obj/ty.rs. New obj::prelude module for common imports. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
77
src/obj/bool.rs
Normal file
77
src/obj/bool.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use std::fmt::{self, Debug, Display};
|
||||
|
||||
use gc::{Finalize, Trace};
|
||||
|
||||
use crate::obj::macros::*;
|
||||
use crate::obj::prelude::*;
|
||||
use crate::obj::BaseObj;
|
||||
use crate::vm::Vm;
|
||||
|
||||
#[derive(Trace, Finalize)]
|
||||
pub struct Bool {
|
||||
base: BaseObj,
|
||||
bool_value: bool,
|
||||
}
|
||||
|
||||
impl Bool {
|
||||
pub fn new(bool_value: bool) -> Self {
|
||||
Self {
|
||||
bool_value,
|
||||
base: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl_create!(bool_value: bool);
|
||||
|
||||
pub fn bool_value(&self) -> bool {
|
||||
self.bool_value
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Bool {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.bool_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Bool {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.bool_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Bool {
|
||||
fn is_truthy(&self) -> bool {
|
||||
self.bool_value
|
||||
}
|
||||
|
||||
fn equals(&self, other: &dyn Object) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Bool>() {
|
||||
self.bool_value == other.bool_value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl_base_obj!(Bool);
|
||||
}
|
||||
|
||||
impl Bool {
|
||||
impl_do_call!(to_bool);
|
||||
|
||||
pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
// This is a no-op. We don't want the user-exposed `__init__` function to do anything,
|
||||
// instantiation is done in the `__call__` function.
|
||||
FunctionResult::ReturnPush(Nil::create())
|
||||
}
|
||||
|
||||
pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let bool_value = with_obj_downcast(vm.frame_stack()[0].clone(), Bool::bool_value);
|
||||
Int::create(bool_value as i64).into()
|
||||
}
|
||||
|
||||
pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let bool_value = with_obj_downcast(vm.frame_stack()[0].clone(), Bool::bool_value);
|
||||
Float::create(bool_value as i64 as f64).into()
|
||||
}
|
||||
}
|
||||
162
src/obj/float.rs
Normal file
162
src/obj/float.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
use std::fmt::{self, Debug, Display};
|
||||
|
||||
use gc::{Finalize, Trace};
|
||||
|
||||
use crate::obj::macros::*;
|
||||
use crate::obj::prelude::*;
|
||||
use crate::obj::BaseObj;
|
||||
use crate::vm::Vm;
|
||||
|
||||
#[derive(Trace, Finalize)]
|
||||
pub struct Float {
|
||||
base: BaseObj,
|
||||
float_value: f64,
|
||||
}
|
||||
|
||||
impl Float {
|
||||
pub fn new(float_value: f64) -> Self {
|
||||
Self {
|
||||
float_value,
|
||||
base: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl_create!(float_value: f64);
|
||||
|
||||
pub fn float_value(&self) -> f64 {
|
||||
self.float_value
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Float {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
// we want to force the .0 if it's a whole number
|
||||
if self.float_value == self.float_value.floor() {
|
||||
write!(fmt, "{}.0", self.float_value)
|
||||
} else {
|
||||
write!(fmt, "{}", self.float_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Float {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
Debug::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Float {
|
||||
fn is_truthy(&self) -> bool {
|
||||
self.float_value != 0.0
|
||||
}
|
||||
|
||||
fn equals(&self, other: &dyn Object) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Float>() {
|
||||
self.float_value == other.float_value
|
||||
} else if let Some(other) = other.as_any().downcast_ref::<Int>() {
|
||||
self.float_value == other.int_value as f64
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl_base_obj!(Float);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Float implementations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
macro_rules! float_bin_op_math {
|
||||
($function:ident, $op:tt) => {
|
||||
pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
|
||||
let lhs_value = with_obj_downcast(lhs, Float::float_value);
|
||||
|
||||
let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::<Int>() {
|
||||
Float::create(lhs_value $op int_inst.int_value() as f64)
|
||||
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
|
||||
Float::create(lhs_value $op float_inst.float_value())
|
||||
} else {
|
||||
// TODO Int arithmetic operator - throw an exception when RHS is not Int, Float
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
concat!("cannot use '", stringify!($op), "' operator with Float and {}"),
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
};
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! float_bin_op_logical {
|
||||
($function:ident, $op:tt) => {
|
||||
pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
|
||||
let lhs_value = with_obj_downcast(lhs, Float::float_value);
|
||||
|
||||
let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::<Int>() {
|
||||
Bool::create(lhs_value $op int_inst.int_value() as f64)
|
||||
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
|
||||
Bool::create(lhs_value $op float_inst.float_value())
|
||||
} else {
|
||||
// TODO Int logical operator - throw an exception when RHS is not Int, Float
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
concat!("cannot use '", stringify!($op), "' operator with Float and {}"),
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
};
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Float {
|
||||
impl_do_call!(to_float);
|
||||
|
||||
pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
// This is a no-op. We don't want the user-exposed `__init__` function to do anything,
|
||||
// instantiation is done in the `__call__` function.
|
||||
FunctionResult::ReturnPush(Nil::create())
|
||||
}
|
||||
|
||||
pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let float_value = with_obj_downcast(vm.frame_stack()[0].clone(), Float::float_value);
|
||||
Int::create(float_value as i64).into()
|
||||
}
|
||||
|
||||
pub(crate) fn to_float(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
FunctionResult::Return
|
||||
}
|
||||
|
||||
float_bin_op_math!(add, +);
|
||||
float_bin_op_math!(sub, -);
|
||||
float_bin_op_math!(mul, *);
|
||||
float_bin_op_math!(div, /);
|
||||
|
||||
// __eq__ will use the default .equals implementation
|
||||
//int_bin_op_logical!(eq, ==);
|
||||
// __ne__ will call __eq__ and negate it
|
||||
float_bin_op_logical!(gt, >);
|
||||
float_bin_op_logical!(ge, >=);
|
||||
float_bin_op_logical!(lt, <);
|
||||
float_bin_op_logical!(le, <=);
|
||||
|
||||
pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let value = with_obj_downcast(lhs, Float::float_value);
|
||||
Float::create(value.abs()).into()
|
||||
}
|
||||
|
||||
pub(crate) fn neg(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let value = with_obj_downcast(lhs, Float::float_value);
|
||||
Float::create(-value).into()
|
||||
}
|
||||
}
|
||||
186
src/obj/int.rs
Normal file
186
src/obj/int.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use std::fmt::{self, Debug, Display};
|
||||
|
||||
use gc::{Finalize, Trace};
|
||||
|
||||
use crate::obj::macros::*;
|
||||
use crate::obj::prelude::*;
|
||||
use crate::obj::BaseObj;
|
||||
use crate::vm::Vm;
|
||||
|
||||
#[derive(Trace, Finalize)]
|
||||
pub struct Int {
|
||||
base: BaseObj,
|
||||
pub(crate) int_value: i64,
|
||||
}
|
||||
|
||||
impl Int {
|
||||
pub fn new(int_value: i64) -> Self {
|
||||
Self {
|
||||
int_value,
|
||||
base: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl_create!(int_value: i64);
|
||||
|
||||
pub fn int_value(&self) -> i64 {
|
||||
self.int_value
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Int {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.int_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Int {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.int_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Int {
|
||||
fn is_truthy(&self) -> bool {
|
||||
self.int_value != 0
|
||||
}
|
||||
|
||||
fn equals(&self, other: &dyn Object) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Int>() {
|
||||
self.int_value == other.int_value
|
||||
} else if let Some(other) = other.as_any().downcast_ref::<Float>() {
|
||||
self.int_value as f64 == other.float_value()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl_base_obj!(Int);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Int implementations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
macro_rules! int_bin_op_math {
|
||||
($function:ident, $op:tt) => {
|
||||
pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
|
||||
let lhs_value = with_obj_downcast(lhs, Int::int_value);
|
||||
|
||||
let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::<Int>() {
|
||||
Int::create(lhs_value $op int_inst.int_value())
|
||||
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
|
||||
Float::create(lhs_value as f64 $op float_inst.float_value())
|
||||
} else {
|
||||
// TODO Int arithmetic operator - throw an exception when RHS is not Int, Float
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
concat!("cannot use '", stringify!($op), "' operator with Int and {}"),
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
};
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int_bin_op_logical {
|
||||
($function:ident, $op:tt) => {
|
||||
pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
|
||||
let lhs_value = with_obj_downcast(lhs, Int::int_value);
|
||||
|
||||
let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::<Int>() {
|
||||
Bool::create(lhs_value $op int_inst.int_value())
|
||||
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
|
||||
Bool::create((lhs_value as f64) $op float_inst.float_value())
|
||||
} else {
|
||||
// TODO Int logical operator - throw an exception when RHS is not Int, Float
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
concat!("cannot use '", stringify!($op), "' operator with Int and {}"),
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
};
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Int {
|
||||
pub(crate) fn to_int(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
FunctionResult::Return
|
||||
}
|
||||
|
||||
pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let int_value = with_obj_downcast(vm.frame_stack()[0].clone(), Int::int_value);
|
||||
Float::create(int_value as f64).into()
|
||||
}
|
||||
|
||||
impl_do_call!(to_int);
|
||||
|
||||
pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
// This is a no-op. We don't want the user-exposed `__init__` function to do anything,
|
||||
// instantiation is done in the `__call__` function.
|
||||
FunctionResult::ReturnPush(Nil::create())
|
||||
}
|
||||
|
||||
int_bin_op_math!(add, +);
|
||||
int_bin_op_math!(sub, -);
|
||||
|
||||
pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
// can't bin_op_math this one because it needs the string case
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
|
||||
let lhs_value = with_obj_downcast(lhs, Int::int_value);
|
||||
|
||||
let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::<Int>() {
|
||||
Int::create(lhs_value * int_inst.int_value())
|
||||
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
|
||||
Float::create(lhs_value as f64 * float_inst.float_value())
|
||||
} else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::<Str>() {
|
||||
// TODO Int::mul - maybe convert this to just call Str.mul with arguments reversed?
|
||||
// Just so we have the same logic here
|
||||
Str::create(str_inst.str_value().repeat(lhs_value as usize))
|
||||
} else {
|
||||
// TODO Int::mul - throw an exception when RHS is not Int, Float, Str
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
"cannot use '*' operator with Int and {}",
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
};
|
||||
result.into()
|
||||
}
|
||||
|
||||
// TODO Int::div - handle divide by zero
|
||||
// BLOCKED-ON: exceptions
|
||||
// NOTE - we will probably need to get rid of the macro here to handle that :(
|
||||
int_bin_op_math!(div, /);
|
||||
|
||||
// __eq__ will use the default .equals implementation
|
||||
//int_bin_op_logical!(eq, ==);
|
||||
// __ne__ will call __eq__ and negate it
|
||||
int_bin_op_logical!(gt, >);
|
||||
int_bin_op_logical!(ge, >=);
|
||||
int_bin_op_logical!(lt, <);
|
||||
int_bin_op_logical!(le, <=);
|
||||
|
||||
pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let value = with_obj_downcast(lhs, Int::int_value);
|
||||
Int::create(value.abs()).into()
|
||||
}
|
||||
|
||||
pub(crate) fn neg(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let value = with_obj_downcast(lhs, Int::int_value);
|
||||
Int::create(-value).into()
|
||||
}
|
||||
}
|
||||
@@ -38,13 +38,54 @@ macro_rules! impl_base_obj {
|
||||
|
||||
macro_rules! impl_create {
|
||||
($($arg:ident : $ty:ty),* $(,)?) => {
|
||||
// TODO - obj::macros::impl_create - remove this #[allow(dead_code)] designator
|
||||
#[allow(dead_code)]
|
||||
pub fn create($($arg : $ty ),*) -> $crate::obj::ObjP {
|
||||
let ptr = make_ptr(Self::new($($arg),*));
|
||||
let ptr = $crate::obj::make_ptr(Self::new($($arg),*));
|
||||
ptr.borrow_mut().instantiate();
|
||||
ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_do_call {
|
||||
($name:ident) => {
|
||||
pub(crate) fn do_call(
|
||||
vm: &mut $crate::vm::Vm,
|
||||
state: $crate::obj::function::FunctionState,
|
||||
) -> $crate::obj::function::FunctionResult {
|
||||
match state {
|
||||
$crate::obj::function::FunctionState::Begin => {
|
||||
// get the top item off the stack and call to_float on it
|
||||
let arg = vm.peek();
|
||||
let method = if let Some(method) =
|
||||
arg.borrow().get_vtable_attr(arg.clone(), stringify!($name))
|
||||
{
|
||||
method
|
||||
} else {
|
||||
// TODO builtins::do_call - throw exception when target doesn't have a
|
||||
// to_$name method
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
concat!("{} does not have a ", stringify!($name), " method"),
|
||||
arg.borrow().ty_name()
|
||||
);
|
||||
};
|
||||
vm.push(method.clone());
|
||||
method.borrow().call(vm, 0);
|
||||
|
||||
// resume execution
|
||||
$crate::obj::function::FunctionResult::Yield(0)
|
||||
}
|
||||
$crate::obj::function::FunctionState::Resume(0) => {
|
||||
$crate::obj::function::FunctionResult::Return
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use impl_base_obj;
|
||||
pub(crate) use impl_create;
|
||||
pub(crate) use impl_do_call;
|
||||
|
||||
144
src/obj/str.rs
Normal file
144
src/obj/str.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gc::{Finalize, Trace};
|
||||
|
||||
use crate::obj::macros::*;
|
||||
use crate::obj::prelude::*;
|
||||
use crate::obj::BaseObj;
|
||||
use crate::vm::Vm;
|
||||
|
||||
#[derive(Trace, Finalize)]
|
||||
pub struct Str {
|
||||
base: BaseObj,
|
||||
#[unsafe_ignore_trace]
|
||||
str_value: Rc<String>,
|
||||
}
|
||||
|
||||
impl Str {
|
||||
pub fn new(str_value: impl ToString) -> Self {
|
||||
Self {
|
||||
base: Default::default(),
|
||||
str_value: Rc::new(str_value.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
impl_create!(str_value: impl ToString);
|
||||
|
||||
pub fn str_value(&self) -> &Rc<String> {
|
||||
&self.str_value
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Str {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.str_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Str {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "'{}'", self.str_value.as_str().escape_default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Str {
|
||||
fn is_truthy(&self) -> bool {
|
||||
!self.str_value.is_empty()
|
||||
}
|
||||
|
||||
fn equals(&self, other: &dyn Object) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Str>() {
|
||||
self.str_value == other.str_value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl_base_obj!(Str);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Builtin implementations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
impl Str {
|
||||
impl_do_call!(to_str);
|
||||
|
||||
pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
// This is a no-op. We don't want the user-exposed `__init__` function to do anything,
|
||||
// instantiation is done in the `__call__` function.
|
||||
FunctionResult::ReturnPush(Nil::create())
|
||||
}
|
||||
|
||||
pub(crate) fn to_str(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
// top item of the stack should just be ourselves, so return immediately
|
||||
FunctionResult::Return
|
||||
}
|
||||
|
||||
pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let parsed: Result<i64, _> =
|
||||
with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| {
|
||||
str_inst.str_value().parse()
|
||||
});
|
||||
match parsed {
|
||||
Ok(int) => Int::create(int).into(),
|
||||
// TODO Str::to_int - throw an exception when we fail to parse an integer
|
||||
// BLOCKED-ON - exceptions
|
||||
Err(e) => todo!("error parsing string to an integer: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let parsed: Result<f64, _> =
|
||||
with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| {
|
||||
str_inst.str_value().parse()
|
||||
});
|
||||
match parsed {
|
||||
Ok(float) => Float::create(float).into(),
|
||||
// TODO Str::to_int - throw an exception when we fail to parse an integer
|
||||
// BLOCKED-ON - exceptions
|
||||
Err(e) => todo!("error parsing string to a float: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn len(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let len = with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| {
|
||||
str_inst.str_value().len() as i64
|
||||
});
|
||||
Int::create(len).into()
|
||||
}
|
||||
|
||||
pub(crate) fn add(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
if !obj_is_inst::<Str>(&rhs) {
|
||||
// TODO Str::add - throw an exception when the RHS is not a string
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
"can only concatenate Str, got {} instead",
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
}
|
||||
|
||||
let new = format!("{}{}", lhs.borrow(), rhs.borrow());
|
||||
Str::create(new).into()
|
||||
}
|
||||
|
||||
pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult {
|
||||
let lhs = vm.frame_stack()[0].clone();
|
||||
let rhs = vm.frame_stack()[1].clone();
|
||||
let repeat_count = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::<Int>() {
|
||||
int_inst.int_value()
|
||||
} else {
|
||||
// TODO Str::mul - throw an exception when the RHS is not an int
|
||||
// BLOCKED-ON: exceptions
|
||||
todo!(
|
||||
"can only repeat Str with Int, got {} instead",
|
||||
rhs.borrow().ty_name()
|
||||
)
|
||||
};
|
||||
let repeat_count = repeat_count.max(0) as usize;
|
||||
let new = format!("{}", lhs.borrow()).repeat(repeat_count);
|
||||
Str::create(new).into()
|
||||
}
|
||||
}
|
||||
270
src/obj/ty.rs
Normal file
270
src/obj/ty.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gc::{Finalize, Trace};
|
||||
|
||||
use crate::obj::macros::*;
|
||||
use crate::obj::prelude::*;
|
||||
use crate::obj::{BaseObj, BUILTINS};
|
||||
use crate::vm::{Argc, Vm};
|
||||
|
||||
#[derive(Trace, Finalize)]
|
||||
pub struct Ty {
|
||||
base: BaseObj,
|
||||
#[unsafe_ignore_trace]
|
||||
name: Rc<String>,
|
||||
vtable: HashMap<String, ObjP>,
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
pub fn new(name: impl ToString) -> Self {
|
||||
Self {
|
||||
name: Rc::new(name.to_string()),
|
||||
base: Default::default(),
|
||||
vtable: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Rc<String> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn vtable(&self) -> &HashMap<String, ObjP> {
|
||||
&self.vtable
|
||||
}
|
||||
|
||||
impl_create!(name: impl ToString);
|
||||
}
|
||||
|
||||
impl Debug for Ty {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
fmt,
|
||||
"<Ty {} at {:#x}>",
|
||||
self.name,
|
||||
(self as *const _ as usize)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ty {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
Debug::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Ty {
|
||||
fn equals(&self, other: &dyn Object) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Ty>() {
|
||||
// TODO Ty::equals : something more robust than this
|
||||
// Tys should hold equality if they have the same name
|
||||
// the problem is that Ty.get_attr("__ty__") is going to return itself, so we have
|
||||
// to go through attributes to specially exclude to the __ty__ 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
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, vm: &mut Vm, argc: Argc) {
|
||||
// TODO Object::call - need to handle "this object cannot be called" errors
|
||||
// BLOCKED-ON: exceptions
|
||||
|
||||
// I don't think there's any way we could call this *without* it being a method.
|
||||
// If you do e.g. `Int.__call__`, Int is an object, so it should be doing `__call__` as a
|
||||
// vtable value.
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
let index = vm.stack().len() - 1 - argc as usize;
|
||||
let this = vm.stack()[index].clone();
|
||||
assert!(
|
||||
std::ptr::addr_eq(&*this.borrow(), self),
|
||||
"calling {}.__call__ on type that is not ourselves",
|
||||
self.ty_name()
|
||||
);
|
||||
}
|
||||
|
||||
let function = self
|
||||
.vtable
|
||||
.get("__call__")
|
||||
.expect("Why does a type not have a __call__ member?");
|
||||
function.borrow().call(vm, argc);
|
||||
}
|
||||
|
||||
fn arity(&self) -> Option<Argc> {
|
||||
// HACK XXX NOTE Ty __call__ arity :
|
||||
// We need to tread carefully here. Normally, `__call__` would be wrapped as a method.
|
||||
// However, we have to get the `__call__` member directly from the vtable.
|
||||
// We are subtracting 1 from the arity, because whenever it *does* become a method, the
|
||||
// arity will match when we call `Ty` directly.
|
||||
self.vtable
|
||||
.get("__call__")
|
||||
.and_then(|function| function.borrow().arity())
|
||||
.map(|n| n - 1)
|
||||
}
|
||||
|
||||
impl_base_obj!(Ty);
|
||||
}
|
||||
|
||||
pub fn init_types() {
|
||||
#![allow(non_snake_case)]
|
||||
macro_rules! types {
|
||||
(
|
||||
base_type: $base_type:ident,
|
||||
$(
|
||||
$name:ident {
|
||||
$( $vtable_name:ident => $vtable_value:expr ),* $(,)?
|
||||
}
|
||||
),* $(,)?
|
||||
) => {{
|
||||
$(
|
||||
let $name = make_ptr(Ty::new(stringify!($name)));
|
||||
BUILTINS.with_borrow_mut(|builtins| builtins.insert(stringify!($name).to_string(), $name.clone()));
|
||||
)*
|
||||
|
||||
// We have to instantiate these objects all by hand. This is because the `instantiate`
|
||||
// function does some stuff that may accidentally cause infinite recursion while we are
|
||||
// setting up these fundamental types.
|
||||
$({
|
||||
let base_type = $base_type.clone();
|
||||
$name.borrow_mut().set_attr("__ty__", base_type);
|
||||
with_obj_downcast_mut($name.clone(), |ty: &mut Ty| { ty.base.is_instantiated = true; });
|
||||
})*
|
||||
|
||||
$({
|
||||
$(
|
||||
let vtable_name = stringify!($vtable_name);
|
||||
let vtable_value = $vtable_value;
|
||||
with_obj_downcast_mut($name.clone(), |ty: &mut Ty| {
|
||||
ty.vtable.insert(vtable_name.to_string(), vtable_value);
|
||||
});
|
||||
)*
|
||||
})*
|
||||
}};
|
||||
}
|
||||
|
||||
types! {
|
||||
// base type
|
||||
base_type: Ty,
|
||||
// type definitions
|
||||
Ty {
|
||||
// Conversion methods
|
||||
to_str => BuiltinFunction::create("to_str", BaseObj::to_str, 1),
|
||||
to_repr => BuiltinFunction::create("to_repr", BaseObj::to_repr, 1),
|
||||
to_bool => BuiltinFunction::create("to_bool", BaseObj::to_bool, 1),
|
||||
to_int => BuiltinFunction::create("to_int", BaseObj::not_implemented_un, 1),
|
||||
to_float => BuiltinFunction::create("to_float", BaseObj::not_implemented_un, 1),
|
||||
len => BuiltinFunction::create("len", BaseObj::not_implemented_un, 1),
|
||||
|
||||
// Constructor
|
||||
// TODO Ty::do_call, Ty::init - implement these methods
|
||||
__call__ => BuiltinFunction::create("__call__", BaseObj::not_implemented_un, 1),
|
||||
__init__ => BuiltinFunction::create("__init__", BaseObj::not_implemented_un, 1),
|
||||
|
||||
// Operators
|
||||
__add__ => BuiltinFunction::create("__add__", BaseObj::not_implemented_bin, 2),
|
||||
__sub__ => BuiltinFunction::create("__sub__", BaseObj::not_implemented_bin, 2),
|
||||
__mul__ => BuiltinFunction::create("__mul__", BaseObj::not_implemented_bin, 2),
|
||||
__div__ => BuiltinFunction::create("__div__", BaseObj::not_implemented_bin, 2),
|
||||
__and__ => BuiltinFunction::create("__and__", BaseObj::and, 2),
|
||||
__or__ => BuiltinFunction::create("__or__", BaseObj::or, 2),
|
||||
__ne__ => BuiltinFunction::create("__ne__", BaseObj::ne, 2),
|
||||
__eq__ => BuiltinFunction::create("__eq__", BaseObj::eq, 2),
|
||||
__gt__ => BuiltinFunction::create("__gt__", BaseObj::not_implemented_bin, 2),
|
||||
__ge__ => BuiltinFunction::create("__ge__", BaseObj::not_implemented_bin, 2),
|
||||
__lt__ => BuiltinFunction::create("__lt__", BaseObj::not_implemented_bin, 2),
|
||||
__le__ => BuiltinFunction::create("__le__", BaseObj::not_implemented_bin, 2),
|
||||
__pos__ => BuiltinFunction::create("__pos__", BaseObj::not_implemented_un, 1),
|
||||
__neg__ => BuiltinFunction::create("__neg__", BaseObj::not_implemented_un, 1),
|
||||
__not__ => BuiltinFunction::create("__not__", BaseObj::not, 1),
|
||||
},
|
||||
Obj {
|
||||
//__call__ => BuiltinFunction::create("__call__",
|
||||
},
|
||||
Str {
|
||||
// Conversion methods
|
||||
to_str => BuiltinFunction::create("to_str", Str::to_str, 1),
|
||||
to_int => BuiltinFunction::create("to_int", Str::to_int, 1),
|
||||
to_float => BuiltinFunction::create("to_float", Str::to_float, 1),
|
||||
len => BuiltinFunction::create("len", Str::len, 1),
|
||||
|
||||
// Constructor
|
||||
__call__ => BuiltinFunction::create("__call__", Str::do_call, 2),
|
||||
__init__ => BuiltinFunction::create("__init__", Str::init, 2),
|
||||
|
||||
// Operators
|
||||
__add__ => BuiltinFunction::create("__add__", Str::add, 2),
|
||||
__mul__ => BuiltinFunction::create("__mul__", Str::mul, 2),
|
||||
// .lower, .upper, .slice, etc
|
||||
},
|
||||
Int {
|
||||
// Conversion methods
|
||||
to_int => BuiltinFunction::create("to_int", Int::to_int, 1),
|
||||
to_float => BuiltinFunction::create("to_float", Int::to_float, 1),
|
||||
|
||||
// Constructor
|
||||
__call__ => BuiltinFunction::create("__call__", Int::do_call, 2),
|
||||
__init__ => BuiltinFunction::create("__init__", Int::init, 2),
|
||||
|
||||
// Operators
|
||||
__add__ => BuiltinFunction::create("__add__", Int::add, 2),
|
||||
__sub__ => BuiltinFunction::create("__sub__", Int::sub, 2),
|
||||
__mul__ => BuiltinFunction::create("__mul__", Int::mul, 2),
|
||||
__div__ => BuiltinFunction::create("__div__", Int::div, 2),
|
||||
//__eq__ => BuiltinFunction::create("__eq__", Int::eq, 2),
|
||||
__gt__ => BuiltinFunction::create("__gt__", Int::gt, 2),
|
||||
__ge__ => BuiltinFunction::create("__ge__", Int::ge, 2),
|
||||
__lt__ => BuiltinFunction::create("__lt__", Int::lt, 2),
|
||||
__le__ => BuiltinFunction::create("__le__", Int::le, 2),
|
||||
__pos__ => BuiltinFunction::create("__pos__", Int::pos, 1),
|
||||
__neg__ => BuiltinFunction::create("__neg__", Int::neg, 1),
|
||||
},
|
||||
Float {
|
||||
// Conversion methods
|
||||
to_int => BuiltinFunction::create("to_int", Float::to_int, 1),
|
||||
to_float => BuiltinFunction::create("to_float", Float::to_float, 1),
|
||||
|
||||
// Constructor
|
||||
__call__ => BuiltinFunction::create("__call__", Float::do_call, 2),
|
||||
__init__ => BuiltinFunction::create("__init__", Float::init, 2),
|
||||
|
||||
// Operators
|
||||
__add__ => BuiltinFunction::create("__add__", Float::add, 2),
|
||||
__sub__ => BuiltinFunction::create("__sub__", Float::sub, 2),
|
||||
__mul__ => BuiltinFunction::create("__mul__", Float::mul, 2),
|
||||
__div__ => BuiltinFunction::create("__div__", Float::div, 2),
|
||||
__gt__ => BuiltinFunction::create("__gt__", Float::gt, 2),
|
||||
__ge__ => BuiltinFunction::create("__ge__", Float::ge, 2),
|
||||
__lt__ => BuiltinFunction::create("__lt__", Float::lt, 2),
|
||||
__le__ => BuiltinFunction::create("__le__", Float::le, 2),
|
||||
__pos__ => BuiltinFunction::create("__pos__", Float::pos, 1),
|
||||
__neg__ => BuiltinFunction::create("__neg__", Float::neg, 1),
|
||||
},
|
||||
Bool {
|
||||
// Conversion methods
|
||||
to_int => BuiltinFunction::create("to_int", Bool::to_int, 1),
|
||||
to_float => BuiltinFunction::create("to_float", Bool::to_float, 1),
|
||||
|
||||
// Constructor
|
||||
__call__ => BuiltinFunction::create("__call__", Bool::do_call, 2),
|
||||
__init__ => BuiltinFunction::create("__init__", Bool::init, 2),
|
||||
|
||||
// Operators
|
||||
},
|
||||
Nil {
|
||||
// Conversion methods
|
||||
|
||||
// Constructor
|
||||
__call__ => BuiltinFunction::create("__call__", Nil::do_call, 1),
|
||||
__init__ => BuiltinFunction::create("__init__", Nil::init, 1),
|
||||
|
||||
// Operators
|
||||
},
|
||||
BuiltinFunction { },
|
||||
UserFunction { },
|
||||
Method { },
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user