718 lines
21 KiB
C
718 lines
21 KiB
C
#include <fx/bool.h>
|
|
#include <fx/comparable.h>
|
|
#include <fx/convertible.h>
|
|
#include <fx/float.h>
|
|
#include <fx/int.h>
|
|
#include <fx/operable.h>
|
|
#include <fx/reflection/type.h>
|
|
#include <fx/string.h>
|
|
#include <fx/value-type.h>
|
|
#include <fx/value.h>
|
|
#include <fx/wstr.h>
|
|
|
|
void fx_value_init(fx_value *v, fx_type_id type)
|
|
{
|
|
}
|
|
|
|
void fx_value_copy(fx_value *dst, const fx_value *src)
|
|
{
|
|
memcpy(dst, src, sizeof *dst);
|
|
|
|
if (dst->v_type && !fx_type_is_value_type(dst->v_type)) {
|
|
fx_object_ref(dst->v_object);
|
|
}
|
|
}
|
|
|
|
fx_value fx_value_copy_return(const fx_value src)
|
|
{
|
|
fx_value result;
|
|
fx_value_copy(&result, &src);
|
|
return result;
|
|
}
|
|
|
|
fx_value fx_value_ref_copy_return(const fx_value *src)
|
|
{
|
|
if (!src) {
|
|
return FX_VALUE_EMPTY;
|
|
}
|
|
|
|
fx_value result;
|
|
fx_value_copy(&result, src);
|
|
return result;
|
|
}
|
|
|
|
void fx_value_copy_array(fx_value *dst, const fx_value *src, size_t count)
|
|
{
|
|
for (size_t i = 0; i < count; i++) {
|
|
fx_value_copy(dst + i, src + i);
|
|
}
|
|
}
|
|
|
|
void fx_value_unset(fx_value *v)
|
|
{
|
|
if (!v->v_type) {
|
|
return;
|
|
}
|
|
|
|
if (!fx_type_is_value_type(v->v_type)) {
|
|
fx_object_unref(v->v_object);
|
|
}
|
|
|
|
memset(v, 0x0, sizeof *v);
|
|
}
|
|
|
|
void fx_value_unset_array(fx_value *v, size_t count)
|
|
{
|
|
for (size_t i = 0; i < count; i++) {
|
|
fx_value_unset(v + i);
|
|
}
|
|
}
|
|
|
|
fx_status fx_value_to_string(
|
|
const fx_value *v,
|
|
fx_stream *out,
|
|
const char *format)
|
|
{
|
|
if (!v->v_type) {
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
void *c = fx_class_get(v->v_type);
|
|
if (!c) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_object_class *object = fx_class_get_interface(c, FX_TYPE_OBJECT);
|
|
if (object && object->to_string) {
|
|
return object->to_string(v, out, format);
|
|
}
|
|
|
|
const fx_type *ty = fx_type_get_by_id(v->v_type);
|
|
if (ty) {
|
|
fx_stream_write_fmt(out, NULL, "%s", fx_type_get_name(ty));
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_status fx_value_hash(const fx_value *v, uint64_t *out_hash)
|
|
{
|
|
void *c = fx_class_get(v->v_type);
|
|
if (!c) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_object_class *object = fx_class_get_interface(c, FX_TYPE_OBJECT);
|
|
if (!object || !object->hash) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return object->hash(v, out_hash);
|
|
}
|
|
|
|
int fx_value_compare(const fx_value *left, const fx_value *right)
|
|
{
|
|
if (!left->v_type && !right->v_type) {
|
|
return 0;
|
|
}
|
|
|
|
if (!left->v_type || !right->v_type) {
|
|
return -1;
|
|
}
|
|
|
|
void *c = fx_class_get(left->v_type);
|
|
if (!c) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_comparable_class *comparable = fx_class_get_interface(
|
|
c,
|
|
FX_TYPE_COMPARABLE);
|
|
if (!comparable || !comparable->c_compare) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return comparable->c_compare(left, right);
|
|
}
|
|
|
|
bool fx_value_is_set(const fx_value *v)
|
|
{
|
|
return v->v_type != NULL;
|
|
}
|
|
|
|
bool fx_value_is_type(const fx_value *v, fx_type_id type)
|
|
{
|
|
if (fx_type_id_compare(v->v_type, type) == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (!fx_type_is_value_type(v->v_type)) {
|
|
return fx_object_is_type(v->v_object, type);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static fx_status value_change_string(fx_value *in, fx_value *out)
|
|
{
|
|
if (!in->v_type) {
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
void *c = fx_class_get(in->v_type);
|
|
if (!c) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_object_class *object = fx_class_get_interface(c, FX_TYPE_OBJECT);
|
|
if (!object || !object->to_string) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_stringstream *strm = fx_stringstream_create();
|
|
if (!strm) {
|
|
return FX_ERR_NO_MEMORY;
|
|
}
|
|
|
|
fx_status status = object->to_string(in, strm, NULL);
|
|
if (!FX_OK(status)) {
|
|
fx_stringstream_unref(strm);
|
|
return status;
|
|
}
|
|
|
|
fx_string *result = fx_string_create_from_cstr(
|
|
fx_stringstream_ptr(strm));
|
|
fx_stringstream_unref(strm);
|
|
|
|
if (!result) {
|
|
return FX_ERR_NO_MEMORY;
|
|
}
|
|
|
|
*out = FX_VALUE_OBJECT(result);
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
fx_status fx_value_change_type(
|
|
fx_value *in,
|
|
fx_value *out,
|
|
fx_type_id convert_to)
|
|
{
|
|
if (fx_value_is_type(in, convert_to)) {
|
|
fx_value_copy(out, in);
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
if (fx_type_id_compare(convert_to, FX_TYPE_STRING) == 0) {
|
|
return value_change_string(in, out);
|
|
}
|
|
|
|
void *c = fx_class_get(in->v_type);
|
|
if (!c) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_convertible_class *convertible = fx_class_get_interface(
|
|
c,
|
|
FX_TYPE_CONVERTIBLE);
|
|
if (!convertible) {
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!fx_type_is_value_type(convert_to)) {
|
|
if (convertible->c_to_object) {
|
|
return convertible->c_to_object(in, out, convert_to);
|
|
}
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
unsigned int value_type = __fx_type_get_value_type(convert_to);
|
|
fx_status status = FX_SUCCESS;
|
|
|
|
switch (value_type) {
|
|
case __FX_VALUE_TYPE_BOOL:
|
|
status = convertible->c_to_bool
|
|
? convertible->c_to_bool(in, &out->v_bool)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_I16:
|
|
status = convertible->c_to_i16
|
|
? convertible->c_to_i16(in, &out->v_i16)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_U16:
|
|
status = convertible->c_to_u16
|
|
? convertible->c_to_u16(in, &out->v_u16)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_I32:
|
|
status = convertible->c_to_i32
|
|
? convertible->c_to_i32(in, &out->v_i32)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_U32:
|
|
status = convertible->c_to_u32
|
|
? convertible->c_to_u32(in, &out->v_u32)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_I64:
|
|
status = convertible->c_to_i64
|
|
? convertible->c_to_i64(in, &out->v_i64)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_U64:
|
|
status = convertible->c_to_u64
|
|
? convertible->c_to_u64(in, &out->v_u64)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_IPTR:
|
|
status = convertible->c_to_iptr
|
|
? convertible->c_to_iptr(in, &out->v_iptr)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_UPTR:
|
|
status = convertible->c_to_uptr
|
|
? convertible->c_to_uptr(in, &out->v_uptr)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_SBYTE:
|
|
status = convertible->c_to_sbyte
|
|
? convertible->c_to_sbyte(in, &out->v_sbyte)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_BYTE:
|
|
status = convertible->c_to_byte
|
|
? convertible->c_to_byte(in, &out->v_byte)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_SHORT:
|
|
status = convertible->c_to_short
|
|
? convertible->c_to_short(in, &out->v_short)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_USHORT:
|
|
status = convertible->c_to_ushort
|
|
? convertible->c_to_ushort(in, &out->v_ushort)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_INT:
|
|
status = convertible->c_to_int
|
|
? convertible->c_to_int(in, &out->v_int)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_UINT:
|
|
status = convertible->c_to_uint
|
|
? convertible->c_to_uint(in, &out->v_uint)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_LONG:
|
|
status = convertible->c_to_long
|
|
? convertible->c_to_long(in, &out->v_long)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_ULONG:
|
|
status = convertible->c_to_ulong
|
|
? convertible->c_to_ulong(in, &out->v_ulong)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_LONGLONG:
|
|
status = convertible->c_to_longlong
|
|
? convertible->c_to_longlong(
|
|
in,
|
|
&out->v_longlong)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_ULONGLONG:
|
|
status = convertible->c_to_ulonglong
|
|
? convertible->c_to_ulonglong(
|
|
in,
|
|
&out->v_ulonglong)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_SIZE:
|
|
status = convertible->c_to_size
|
|
? convertible->c_to_size(in, &out->v_size)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_FLOAT:
|
|
status = convertible->c_to_float
|
|
? convertible->c_to_float(in, &out->v_float)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_DOUBLE:
|
|
status = convertible->c_to_double
|
|
? convertible->c_to_double(in, &out->v_double)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
case __FX_VALUE_TYPE_CSTR:
|
|
status = convertible->c_to_cstr
|
|
? convertible->c_to_cstr(in, &out->v_cstr)
|
|
: FX_ERR_NOT_SUPPORTED;
|
|
break;
|
|
default:
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (FX_OK(status)) {
|
|
out->v_type = convert_to;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#define VALUE_GETTER(c_type_name, fx_type_name, type_id) \
|
|
fx_status fx_value_get_##fx_type_name( \
|
|
const fx_value *v, \
|
|
c_type_name *out) \
|
|
{ \
|
|
if (fx_type_id_compare(v->v_type, type_id) == 0) { \
|
|
*out = v->v_##fx_type_name; \
|
|
return FX_SUCCESS; \
|
|
} \
|
|
\
|
|
void *c = fx_class_get(v->v_type); \
|
|
if (!c) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
fx_convertible_class *convertible = fx_class_get_interface( \
|
|
c, \
|
|
FX_TYPE_CONVERTIBLE); \
|
|
if (!convertible) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
c_type_name result; \
|
|
fx_status status = convertible->c_to_##fx_type_name( \
|
|
v, \
|
|
&result); \
|
|
if (!FX_OK(status)) { \
|
|
return status; \
|
|
} \
|
|
\
|
|
*out = result; \
|
|
return FX_SUCCESS; \
|
|
}
|
|
|
|
#define VALUE_SETTER(c_type_name, fx_type_name, type_id) \
|
|
fx_status fx_value_set_##fx_type_name(fx_value *v, c_type_name value) \
|
|
{ \
|
|
fx_value_unset(v); \
|
|
v->v_type = type_id; \
|
|
v->v_##fx_type_name = value; \
|
|
return FX_SUCCESS; \
|
|
}
|
|
|
|
VALUE_GETTER(bool, bool, FX_TYPE_BOOL)
|
|
VALUE_GETTER(i16, i16, FX_TYPE_I16)
|
|
VALUE_GETTER(u16, u16, FX_TYPE_U16)
|
|
VALUE_GETTER(i32, i32, FX_TYPE_I32)
|
|
VALUE_GETTER(u32, u32, FX_TYPE_U32)
|
|
VALUE_GETTER(i64, i64, FX_TYPE_I64)
|
|
VALUE_GETTER(u64, u64, FX_TYPE_U64)
|
|
VALUE_GETTER(iptr, iptr, FX_TYPE_IPTR)
|
|
VALUE_GETTER(uptr, uptr, FX_TYPE_UPTR)
|
|
VALUE_GETTER(sbyte, sbyte, FX_TYPE_SBYTE)
|
|
VALUE_GETTER(byte, byte, FX_TYPE_BYTE)
|
|
VALUE_GETTER(short, short, FX_TYPE_SHORT)
|
|
VALUE_GETTER(unsigned short, ushort, FX_TYPE_USHORT)
|
|
VALUE_GETTER(int, int, FX_TYPE_INT)
|
|
VALUE_GETTER(unsigned int, uint, FX_TYPE_UINT)
|
|
VALUE_GETTER(long, long, FX_TYPE_LONG)
|
|
VALUE_GETTER(unsigned long, ulong, FX_TYPE_ULONG)
|
|
VALUE_GETTER(long long, longlong, FX_TYPE_LONGLONG)
|
|
VALUE_GETTER(unsigned long long, ulonglong, FX_TYPE_ULONGLONG)
|
|
VALUE_GETTER(size_t, size, FX_TYPE_SIZE)
|
|
VALUE_GETTER(float, float, FX_TYPE_FLOAT)
|
|
VALUE_GETTER(double, double, FX_TYPE_DOUBLE)
|
|
VALUE_GETTER(fx_wchar, wchar, FX_TYPE_WCHAR)
|
|
VALUE_GETTER(const fx_wchar *, wstr, FX_TYPE_WSTR)
|
|
|
|
VALUE_SETTER(bool, bool, FX_TYPE_BOOL)
|
|
VALUE_SETTER(i16, i16, FX_TYPE_I16)
|
|
VALUE_SETTER(u16, u16, FX_TYPE_U16)
|
|
VALUE_SETTER(i32, i32, FX_TYPE_I32)
|
|
VALUE_SETTER(u32, u32, FX_TYPE_U32)
|
|
VALUE_SETTER(i64, i64, FX_TYPE_I64)
|
|
VALUE_SETTER(u64, u64, FX_TYPE_U64)
|
|
VALUE_SETTER(iptr, iptr, FX_TYPE_IPTR)
|
|
VALUE_SETTER(uptr, uptr, FX_TYPE_UPTR)
|
|
VALUE_SETTER(sbyte, sbyte, FX_TYPE_SBYTE)
|
|
VALUE_SETTER(byte, byte, FX_TYPE_BYTE)
|
|
VALUE_SETTER(short, short, FX_TYPE_SHORT)
|
|
VALUE_SETTER(unsigned short, ushort, FX_TYPE_USHORT)
|
|
VALUE_SETTER(int, int, FX_TYPE_INT)
|
|
VALUE_SETTER(unsigned int, uint, FX_TYPE_UINT)
|
|
VALUE_SETTER(long, long, FX_TYPE_LONG)
|
|
VALUE_SETTER(unsigned long, ulong, FX_TYPE_ULONG)
|
|
VALUE_SETTER(long long, longlong, FX_TYPE_LONGLONG)
|
|
VALUE_SETTER(unsigned long long, ulonglong, FX_TYPE_ULONGLONG)
|
|
VALUE_SETTER(size_t, size, FX_TYPE_SIZE)
|
|
VALUE_SETTER(float, float, FX_TYPE_FLOAT)
|
|
VALUE_SETTER(double, double, FX_TYPE_DOUBLE)
|
|
VALUE_SETTER(fx_wchar, wchar, FX_TYPE_WCHAR)
|
|
|
|
fx_status fx_value_get_cstr(const fx_value *v, const char **out)
|
|
{
|
|
if (fx_type_id_compare(v->v_type, FX_TYPE_CSTR) == 0) {
|
|
*out = v->v_cstr;
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
if (fx_type_id_compare(v->v_type, FX_TYPE_STRING) == 0) {
|
|
*out = fx_string_get_cstr(v->v_object);
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
*out = NULL;
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_status fx_value_get_pointer(const fx_value *v, void **out)
|
|
{
|
|
if (fx_type_id_compare(v->v_type, FX_TYPE_POINTER) == 0) {
|
|
*out = v->v_pointer;
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
*out = NULL;
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
fx_status fx_value_get_object(const fx_value *v, fx_object **out)
|
|
{
|
|
if (!v || !v->v_type) {
|
|
*out = NULL;
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
if (!fx_type_is_value_type(v->v_type)) {
|
|
*out = v->v_object;
|
|
return FX_SUCCESS;
|
|
}
|
|
|
|
*out = NULL;
|
|
return FX_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
static bool is_integral_type(unsigned int type)
|
|
{
|
|
switch (type) {
|
|
case __FX_VALUE_TYPE_BOOL:
|
|
case __FX_VALUE_TYPE_I16:
|
|
case __FX_VALUE_TYPE_U16:
|
|
case __FX_VALUE_TYPE_I32:
|
|
case __FX_VALUE_TYPE_U32:
|
|
case __FX_VALUE_TYPE_I64:
|
|
case __FX_VALUE_TYPE_U64:
|
|
case __FX_VALUE_TYPE_IPTR:
|
|
case __FX_VALUE_TYPE_UPTR:
|
|
|
|
case __FX_VALUE_TYPE_SBYTE:
|
|
case __FX_VALUE_TYPE_BYTE:
|
|
case __FX_VALUE_TYPE_SHORT:
|
|
case __FX_VALUE_TYPE_USHORT:
|
|
case __FX_VALUE_TYPE_INT:
|
|
case __FX_VALUE_TYPE_UINT:
|
|
case __FX_VALUE_TYPE_LONG:
|
|
case __FX_VALUE_TYPE_ULONG:
|
|
case __FX_VALUE_TYPE_LONGLONG:
|
|
case __FX_VALUE_TYPE_ULONGLONG:
|
|
case __FX_VALUE_TYPE_SIZE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_signed_type(unsigned int type)
|
|
{
|
|
switch (type) {
|
|
case __FX_VALUE_TYPE_I16:
|
|
case __FX_VALUE_TYPE_I32:
|
|
case __FX_VALUE_TYPE_I64:
|
|
case __FX_VALUE_TYPE_IPTR:
|
|
|
|
case __FX_VALUE_TYPE_SBYTE:
|
|
case __FX_VALUE_TYPE_SHORT:
|
|
case __FX_VALUE_TYPE_INT:
|
|
case __FX_VALUE_TYPE_LONG:
|
|
case __FX_VALUE_TYPE_LONGLONG:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_float_type(unsigned int type)
|
|
{
|
|
switch (type) {
|
|
case __FX_VALUE_TYPE_FLOAT:
|
|
case __FX_VALUE_TYPE_DOUBLE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_negative(unsigned int type, const fx_value *v)
|
|
{
|
|
switch (type) {
|
|
case __FX_VALUE_TYPE_I16:
|
|
return v->v_i16 < 0;
|
|
case __FX_VALUE_TYPE_I32:
|
|
return v->v_i32 < 0;
|
|
case __FX_VALUE_TYPE_I64:
|
|
return v->v_i64 < 0;
|
|
case __FX_VALUE_TYPE_IPTR:
|
|
return v->v_iptr < 0;
|
|
case __FX_VALUE_TYPE_SBYTE:
|
|
return v->v_sbyte < 0;
|
|
case __FX_VALUE_TYPE_SHORT:
|
|
return v->v_short < 0;
|
|
case __FX_VALUE_TYPE_INT:
|
|
return v->v_int < 0;
|
|
case __FX_VALUE_TYPE_LONG:
|
|
return v->v_long < 0;
|
|
case __FX_VALUE_TYPE_LONGLONG:
|
|
return v->v_longlong < 0;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FX_API fx_type_id fx_value_get_common_type(const fx_value *a, const fx_value *b)
|
|
{
|
|
unsigned int a_type = __fx_type_get_value_type(a->v_type);
|
|
unsigned int b_type = __fx_type_get_value_type(b->v_type);
|
|
|
|
if (fx_type_id_compare(a->v_type, FX_TYPE_STRING) == 0
|
|
&& b_type == __FX_VALUE_TYPE_CSTR) {
|
|
return FX_TYPE_STRING;
|
|
}
|
|
|
|
if (fx_type_id_compare(b->v_type, FX_TYPE_STRING) == 0
|
|
&& a_type == __FX_VALUE_TYPE_CSTR) {
|
|
return FX_TYPE_STRING;
|
|
}
|
|
|
|
if (!fx_type_is_value_type(a->v_type)
|
|
|| !fx_type_is_value_type(b->v_type)) {
|
|
if (fx_type_id_compare(a->v_type, b->v_type) == 0) {
|
|
return a->v_type;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (a_type == b_type) {
|
|
return a->v_type;
|
|
}
|
|
|
|
if (is_float_type(a_type) && is_float_type(b_type)) {
|
|
return a_type == b_type ? a->v_type : FX_TYPE_DOUBLE;
|
|
}
|
|
|
|
if (!is_integral_type(a_type) || !is_integral_type(b_type)) {
|
|
if (a_type == __FX_VALUE_TYPE_CSTR) {
|
|
return FX_TYPE_STRING;
|
|
}
|
|
|
|
return a->v_type;
|
|
}
|
|
|
|
size_t a_size = __fx_value_type_get_size(a_type);
|
|
size_t b_size = __fx_value_type_get_size(b_type);
|
|
|
|
bool a_signed = is_signed_type(a_type);
|
|
bool b_signed = is_signed_type(b_type);
|
|
|
|
if ((!a_signed && !b_signed) || (a_signed && b_signed)) {
|
|
return a_size > b_size ? a->v_type : b->v_type;
|
|
}
|
|
|
|
bool a_negative = is_negative(a_type, a);
|
|
bool b_negative = is_negative(b_type, b);
|
|
|
|
if ((!a_negative && !b_negative) || (a_negative && b_negative)) {
|
|
return a_size > b_size ? a->v_type : b->v_type;
|
|
}
|
|
|
|
/* TODO if value is too large for i64, return double instead */
|
|
return FX_TYPE_I64;
|
|
}
|
|
|
|
#define BINARY_OP(name) \
|
|
fx_status fx_value_##name( \
|
|
const fx_value *left, \
|
|
const fx_value *right, \
|
|
fx_value *out) \
|
|
{ \
|
|
if (fx_type_id_compare(left->v_type, right->v_type) != 0) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
void *c = fx_class_get(left->v_type); \
|
|
if (!c) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
fx_operable_class *operable = fx_class_get_interface( \
|
|
c, \
|
|
FX_TYPE_OPERABLE); \
|
|
if (!operable || !operable->op_##name) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
return operable->op_##name(left, right, out); \
|
|
}
|
|
#define PREFIX_OP(name) \
|
|
fx_status fx_value_##name(fx_value *value) \
|
|
{ \
|
|
void *c = fx_class_get(value->v_type); \
|
|
if (!c) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
fx_operable_class *operable = fx_class_get_interface( \
|
|
c, \
|
|
FX_TYPE_OPERABLE); \
|
|
if (!operable || !operable->op_##name) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
return operable->op_##name(value); \
|
|
}
|
|
#define IN_PLACE_OP(name) \
|
|
fx_status fx_value_##name(fx_value *left, const fx_value *right) \
|
|
{ \
|
|
if (fx_type_id_compare(left->v_type, right->v_type) != 0) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
void *c = fx_class_get(left->v_type); \
|
|
if (!c) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
fx_operable_class *operable = fx_class_get_interface( \
|
|
c, \
|
|
FX_TYPE_OPERABLE); \
|
|
if (!operable || !operable->op_##name) { \
|
|
return FX_ERR_NOT_SUPPORTED; \
|
|
} \
|
|
\
|
|
return operable->op_##name(left, right); \
|
|
}
|
|
|
|
BINARY_OP(add)
|
|
BINARY_OP(subtract)
|
|
BINARY_OP(multiply)
|
|
BINARY_OP(divide)
|
|
PREFIX_OP(increment)
|
|
PREFIX_OP(decrement)
|
|
IN_PLACE_OP(add_in_place)
|
|
IN_PLACE_OP(subtract_in_place)
|
|
IN_PLACE_OP(multiply_in_place)
|
|
IN_PLACE_OP(divide_in_place)
|