Broken site based impl
This commit is contained in:
+299
@@ -0,0 +1,299 @@
|
||||
// any_t: a value of any kind, and JS's operators on it
|
||||
#pragma once
|
||||
|
||||
#include "number.h"
|
||||
|
||||
// A value of any kind. An object in it is the pointer to its cell, so it keeps
|
||||
// its identity. A field can also hold absent
|
||||
|
||||
typedef enum {
|
||||
JSRT_KIND_ABSENT,
|
||||
JSRT_KIND_UNDEFINED,
|
||||
JSRT_KIND_NULL,
|
||||
JSRT_KIND_BOOL,
|
||||
JSRT_KIND_INT,
|
||||
JSRT_KIND_FLOAT,
|
||||
JSRT_KIND_STRING,
|
||||
JSRT_KIND_OBJECT,
|
||||
} jsrt_kind;
|
||||
|
||||
typedef struct {
|
||||
jsrt_kind kind;
|
||||
union {
|
||||
bool b;
|
||||
int32_t i;
|
||||
double f;
|
||||
string_t s;
|
||||
void *o;
|
||||
} as;
|
||||
} any_t;
|
||||
|
||||
// Box a value. A pointer that is a sentinel (NULL, JSRT_UNDEFINED,
|
||||
// JSRT_ABSENT) boxes as null, undefined or absent
|
||||
any_t __any_undefined(void);
|
||||
any_t __any_null(void);
|
||||
any_t __any_absent(void);
|
||||
any_t __any_from_bool(bool b);
|
||||
any_t __any_from_int(int32_t n);
|
||||
any_t __any_from_float(double x);
|
||||
any_t __any_from_string(string_t s);
|
||||
any_t __any_from_object(void *o);
|
||||
|
||||
// Unbox a value. A value of another kind throws. A string or an object unboxes
|
||||
// null, undefined and absent to their sentinels
|
||||
bool __any_as_bool(any_t a);
|
||||
int32_t __any_as_int(any_t a);
|
||||
double __any_as_float(any_t a); // an int too
|
||||
string_t __any_as_string(any_t a);
|
||||
void *__any_as_object(any_t a);
|
||||
|
||||
// The kind of a value
|
||||
bool __any_is_absent(any_t a);
|
||||
bool __any_is_undefined(any_t a);
|
||||
bool __any_is_null(any_t a);
|
||||
bool __any_is_nullish(any_t a); // undefined or null
|
||||
bool __any_is_boolean(any_t a);
|
||||
bool __any_is_number(any_t a);
|
||||
bool __any_is_string(any_t a);
|
||||
bool __any_is_object(any_t a); // a fn too
|
||||
bool __any_is_function(any_t a);
|
||||
|
||||
// JS's operators, with JS's conversions
|
||||
any_t __any_add(any_t a, any_t b);
|
||||
any_t __any_sub(any_t a, any_t b);
|
||||
any_t __any_mul(any_t a, any_t b);
|
||||
any_t __any_div(any_t a, any_t b);
|
||||
any_t __any_mod(any_t a, any_t b);
|
||||
any_t __any_pow(any_t a, any_t b);
|
||||
any_t __any_neg(any_t a);
|
||||
any_t __any_plus(any_t a); // ToNumber
|
||||
any_t __any_inc(any_t a);
|
||||
any_t __any_dec(any_t a);
|
||||
|
||||
bool __any_lt(any_t a, any_t b);
|
||||
bool __any_lte(any_t a, any_t b);
|
||||
bool __any_gt(any_t a, any_t b);
|
||||
bool __any_gte(any_t a, any_t b);
|
||||
bool __any_eq(any_t a, any_t b);
|
||||
bool __any_neq(any_t a, any_t b);
|
||||
bool __any_strict_eq(any_t a, any_t b);
|
||||
bool __any_strict_neq(any_t a, any_t b);
|
||||
|
||||
// JS's abstract operations
|
||||
bool __any_truthy(any_t a); // ToBoolean
|
||||
int32_t __any_to_int32(any_t a); // ToInt32
|
||||
string_t __any_to_string(any_t a); // ToString
|
||||
string_t __any_typeof(any_t a);
|
||||
|
||||
#ifdef JSRT_IMPLEMENTATION
|
||||
#include <math.h>
|
||||
|
||||
// ── any: boxing ──
|
||||
|
||||
any_t __any_undefined(void) { return (any_t){ .kind = JSRT_KIND_UNDEFINED }; }
|
||||
any_t __any_null(void) { return (any_t){ .kind = JSRT_KIND_NULL }; }
|
||||
any_t __any_absent(void) { return (any_t){ .kind = JSRT_KIND_ABSENT }; }
|
||||
any_t __any_from_bool(bool b) { return (any_t){ .kind = JSRT_KIND_BOOL, .as.b = b }; }
|
||||
any_t __any_from_int(int32_t n) { return (any_t){ .kind = JSRT_KIND_INT, .as.i = n }; }
|
||||
any_t __any_from_float(double x) { return (any_t){ .kind = JSRT_KIND_FLOAT, .as.f = x }; }
|
||||
|
||||
// A pointer that can be a sentinel, as the kind it stands for, else as `kind`
|
||||
static any_t jsrt_from_ptr(void *p, jsrt_kind kind) {
|
||||
if (p == NULL) return __any_null();
|
||||
if (p == JSRT_UNDEFINED) return __any_undefined();
|
||||
if (p == JSRT_ABSENT) return __any_absent();
|
||||
return (any_t){ .kind = kind, .as.o = p };
|
||||
}
|
||||
|
||||
any_t __any_from_string(string_t s) { return jsrt_from_ptr(s, JSRT_KIND_STRING); }
|
||||
any_t __any_from_object(void *o) { return jsrt_from_ptr(o, JSRT_KIND_OBJECT); }
|
||||
|
||||
bool __any_as_bool(any_t a) {
|
||||
if (a.kind != JSRT_KIND_BOOL) jsrt_type_error();
|
||||
return a.as.b;
|
||||
}
|
||||
|
||||
double __any_as_float(any_t a) {
|
||||
if (a.kind == JSRT_KIND_INT) return a.as.i;
|
||||
if (a.kind != JSRT_KIND_FLOAT) jsrt_type_error();
|
||||
return a.as.f;
|
||||
}
|
||||
|
||||
int32_t __any_as_int(any_t a) {
|
||||
return a.kind == JSRT_KIND_INT ? a.as.i : (int32_t)__any_as_float(a);
|
||||
}
|
||||
|
||||
// A pointer of kind `kind`, or the sentinel of a nullish or absent value
|
||||
static void *jsrt_as_ptr(any_t a, jsrt_kind kind) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_NULL: return NULL;
|
||||
case JSRT_KIND_UNDEFINED: return JSRT_UNDEFINED;
|
||||
case JSRT_KIND_ABSENT: return JSRT_ABSENT;
|
||||
default:
|
||||
if (a.kind != kind) jsrt_type_error();
|
||||
return a.as.o;
|
||||
}
|
||||
}
|
||||
|
||||
string_t __any_as_string(any_t a) { return jsrt_as_ptr(a, JSRT_KIND_STRING); }
|
||||
void *__any_as_object(any_t a) { return jsrt_as_ptr(a, JSRT_KIND_OBJECT); }
|
||||
|
||||
// ── any: kinds ──
|
||||
|
||||
bool __any_is_absent(any_t a) { return a.kind == JSRT_KIND_ABSENT; }
|
||||
bool __any_is_undefined(any_t a) { return a.kind == JSRT_KIND_UNDEFINED || a.kind == JSRT_KIND_ABSENT; }
|
||||
bool __any_is_null(any_t a) { return a.kind == JSRT_KIND_NULL; }
|
||||
bool __any_is_nullish(any_t a) { return __any_is_undefined(a) || __any_is_null(a); }
|
||||
bool __any_is_boolean(any_t a) { return a.kind == JSRT_KIND_BOOL; }
|
||||
bool __any_is_number(any_t a) { return a.kind == JSRT_KIND_INT || a.kind == JSRT_KIND_FLOAT; }
|
||||
bool __any_is_string(any_t a) { return a.kind == JSRT_KIND_STRING; }
|
||||
bool __any_is_object(any_t a) { return a.kind == JSRT_KIND_OBJECT; }
|
||||
|
||||
bool __any_is_function(any_t a) {
|
||||
return a.kind == JSRT_KIND_OBJECT && jsrt_fn_sites[((jsrt_header *)a.as.o)->site];
|
||||
}
|
||||
|
||||
// ── any: abstract operations ──
|
||||
|
||||
// ToNumber. An object would call its `valueOf` or `toString` first, which the
|
||||
// runtime cannot do, so it gives NaN
|
||||
static double jsrt_to_number(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_NULL: return 0;
|
||||
case JSRT_KIND_BOOL: return a.as.b;
|
||||
case JSRT_KIND_INT: return a.as.i;
|
||||
case JSRT_KIND_FLOAT: return a.as.f;
|
||||
case JSRT_KIND_STRING: return __native_string_to_number(a.as.s);
|
||||
default: return NAN;
|
||||
}
|
||||
}
|
||||
|
||||
bool __any_truthy(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_BOOL: return a.as.b;
|
||||
case JSRT_KIND_INT: return a.as.i != 0;
|
||||
case JSRT_KIND_FLOAT: return a.as.f != 0 && !isnan(a.as.f);
|
||||
case JSRT_KIND_STRING: return a.as.s->length != 0;
|
||||
case JSRT_KIND_OBJECT: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __any_to_int32(any_t a) {
|
||||
double x = jsrt_to_number(a);
|
||||
|
||||
if (isnan(x) || isinf(x)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
x = fmod(trunc(x), 4294967296.0);
|
||||
return (int32_t)(uint32_t)(int64_t)(x < 0 ? x + 4294967296.0 : x);
|
||||
}
|
||||
|
||||
// ToString. An object would call its `toString`, which the runtime cannot do
|
||||
string_t __any_to_string(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_UNDEFINED: case JSRT_KIND_ABSENT: return jsrt_string_lit("undefined");
|
||||
case JSRT_KIND_NULL: return jsrt_string_lit("null");
|
||||
case JSRT_KIND_BOOL: return jsrt_string_lit(a.as.b ? "true" : "false");
|
||||
case JSRT_KIND_INT: return __native_number_to_string(a.as.i);
|
||||
case JSRT_KIND_FLOAT: return __native_number_to_string(a.as.f);
|
||||
case JSRT_KIND_STRING: return a.as.s;
|
||||
default: return jsrt_string_lit(__any_is_function(a) ? "function" : "[object Object]");
|
||||
}
|
||||
}
|
||||
|
||||
string_t __any_typeof(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_UNDEFINED: case JSRT_KIND_ABSENT: return jsrt_string_lit("undefined");
|
||||
case JSRT_KIND_BOOL: return jsrt_string_lit("boolean");
|
||||
case JSRT_KIND_INT: case JSRT_KIND_FLOAT: return jsrt_string_lit("number");
|
||||
case JSRT_KIND_STRING: return jsrt_string_lit("string");
|
||||
case JSRT_KIND_OBJECT: return jsrt_string_lit(__any_is_function(a) ? "function" : "object");
|
||||
default: return jsrt_string_lit("object");
|
||||
}
|
||||
}
|
||||
|
||||
// ── any: operators ──
|
||||
|
||||
any_t __any_add(any_t a, any_t b) {
|
||||
// An object becomes a string (see `__any_to_string`), so it concatenates
|
||||
bool strings = a.kind == JSRT_KIND_STRING || b.kind == JSRT_KIND_STRING
|
||||
|| a.kind == JSRT_KIND_OBJECT || b.kind == JSRT_KIND_OBJECT;
|
||||
|
||||
if (strings) {
|
||||
return __any_from_string(__native_string_concat(__any_to_string(a), __any_to_string(b)));
|
||||
}
|
||||
|
||||
return __any_from_float(jsrt_to_number(a) + jsrt_to_number(b));
|
||||
}
|
||||
|
||||
any_t __any_sub(any_t a, any_t b) { return __any_from_float(jsrt_to_number(a) - jsrt_to_number(b)); }
|
||||
any_t __any_mul(any_t a, any_t b) { return __any_from_float(jsrt_to_number(a) * jsrt_to_number(b)); }
|
||||
any_t __any_div(any_t a, any_t b) { return __any_from_float(jsrt_to_number(a) / jsrt_to_number(b)); }
|
||||
any_t __any_mod(any_t a, any_t b) { return __any_from_float(fmod(jsrt_to_number(a), jsrt_to_number(b))); }
|
||||
any_t __any_pow(any_t a, any_t b) { return __any_from_float(pow(jsrt_to_number(a), jsrt_to_number(b))); }
|
||||
any_t __any_neg(any_t a) { return __any_from_float(-jsrt_to_number(a)); }
|
||||
any_t __any_plus(any_t a) { return __any_from_float(jsrt_to_number(a)); }
|
||||
any_t __any_inc(any_t a) { return __any_from_float(jsrt_to_number(a) + 1); }
|
||||
any_t __any_dec(any_t a) { return __any_from_float(jsrt_to_number(a) - 1); }
|
||||
|
||||
// ── any: comparisons ──
|
||||
|
||||
// Two strings compare by code units. Anything else compares as numbers, where
|
||||
// NaN makes every comparison false
|
||||
bool __any_lt(any_t a, any_t b) {
|
||||
bool strings = a.kind == JSRT_KIND_STRING && b.kind == JSRT_KIND_STRING;
|
||||
return strings ? jsrt_string_cmp(a.as.s, b.as.s) < 0 : jsrt_to_number(a) < jsrt_to_number(b);
|
||||
}
|
||||
|
||||
bool __any_lte(any_t a, any_t b) {
|
||||
bool strings = a.kind == JSRT_KIND_STRING && b.kind == JSRT_KIND_STRING;
|
||||
return strings ? jsrt_string_cmp(a.as.s, b.as.s) <= 0 : jsrt_to_number(a) <= jsrt_to_number(b);
|
||||
}
|
||||
|
||||
bool __any_gt(any_t a, any_t b) { return __any_lt(b, a); }
|
||||
bool __any_gte(any_t a, any_t b) { return __any_lte(b, a); }
|
||||
|
||||
bool __any_strict_eq(any_t a, any_t b) {
|
||||
if (__any_is_number(a) && __any_is_number(b)) {
|
||||
return jsrt_to_number(a) == jsrt_to_number(b);
|
||||
}
|
||||
|
||||
if (__any_is_undefined(a) || __any_is_undefined(b)) {
|
||||
return __any_is_undefined(a) && __any_is_undefined(b);
|
||||
}
|
||||
|
||||
if (a.kind != b.kind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_NULL: return true;
|
||||
case JSRT_KIND_BOOL: return a.as.b == b.as.b;
|
||||
case JSRT_KIND_STRING: return jsrt_string_eq(a.as.s, b.as.s);
|
||||
default: return a.as.o == b.as.o;
|
||||
}
|
||||
}
|
||||
|
||||
bool __any_strict_neq(any_t a, any_t b) { return !__any_strict_eq(a, b); }
|
||||
|
||||
// Undefined and null equal each other and nothing else. Values of one kind,
|
||||
// and objects, compare as `===` does. Other primitives compare as numbers
|
||||
bool __any_eq(any_t a, any_t b) {
|
||||
if (__any_is_nullish(a) || __any_is_nullish(b)) {
|
||||
return __any_is_nullish(a) && __any_is_nullish(b);
|
||||
}
|
||||
|
||||
bool same = a.kind == b.kind || (__any_is_number(a) && __any_is_number(b));
|
||||
|
||||
if (same || a.kind == JSRT_KIND_OBJECT || b.kind == JSRT_KIND_OBJECT) {
|
||||
return __any_strict_eq(a, b);
|
||||
}
|
||||
|
||||
return jsrt_to_number(a) == jsrt_to_number(b);
|
||||
}
|
||||
|
||||
bool __any_neq(any_t a, any_t b) { return !__any_eq(a, b); }
|
||||
|
||||
#endif // JSRT_IMPLEMENTATION
|
||||
Reference in New Issue
Block a user