1
0
Fork 0
forked from fun/fun

Added the Float type and many fixes. (0.24.0)

This commit is contained in:
Johannes Findeisen 2025-10-04 20:35:59 +02:00
commit 41fa946f1c
19 changed files with 432 additions and 69 deletions

View file

@ -38,11 +38,20 @@
case OP_ADD: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type == VAL_INT && b.type == VAL_INT) {
Value res = make_int(a.i + b.i);
free_value(a);
free_value(b);
push_value(vm, res);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da + db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i + b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
} else if (a.type == VAL_STRING && b.type == VAL_STRING) {
const char *sa = a.s ? a.s : "";
const char *sb = b.s ? b.s : "";
@ -68,7 +77,7 @@ case OP_ADD: {
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: ADD expects both ints, both strings, or both arrays, got %s and %s\n",
fprintf(stderr, "Runtime type error: ADD expects both numbers, both strings, or both arrays, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}

View file

@ -35,18 +35,32 @@
case OP_DIV: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: DIV expects ints, got %s and %s\n",
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
if (db == 0.0) {
fprintf(stderr, "Runtime error: division by zero\n");
exit(1);
}
Value res = make_float(da / db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
if (b.i == 0) {
fprintf(stderr, "Runtime error: division by zero\n");
exit(1);
}
Value res = make_int(a.i / b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
} else {
fprintf(stderr, "Runtime type error: DIV expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
if (b.i == 0) {
fprintf(stderr, "Runtime error: division by zero\n");
exit(1);
}
Value res = make_int(a.i / b.i);
free_value(a);
free_value(b);
push_value(vm, res);
break;
}

View file

@ -34,14 +34,24 @@
case OP_MUL: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: MUL expects ints, got %s and %s\n",
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da * db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i * b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
} else {
fprintf(stderr, "Runtime type error: MUL expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
Value res = make_int(a.i * b.i);
free_value(a);
free_value(b);
push_value(vm, res);
break;
}

View file

@ -34,14 +34,24 @@
case OP_SUB: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: SUB expects ints, got %s and %s\n",
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da - db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i - b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
} else {
fprintf(stderr, "Runtime type error: SUB expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
Value res = make_int(a.i - b.i);
free_value(a);
free_value(b);
push_value(vm, res);
break;
}

View file

@ -8,27 +8,35 @@
*/
case OP_SCLAMP: {
/* Saturating clamp to signed N-bit range: [-2^(N-1) .. 2^(N-1)-1] */
/* Two's complement wrap to signed N-bit range:
- Mask to N bits
- If sign bit is set, sign-extend to 64-bit
This yields values in [-2^(N-1) .. 2^(N-1)-1]. */
Value v = pop_value(vm);
int bits = inst.operand;
int64_t vi = (v.type == VAL_INT) ? v.i : 0;
int64_t smin, smax;
int64_t out = 0;
if (bits <= 0) {
smin = 0; smax = 0;
} else if (bits >= 63) {
/* cover full int64_t domain for 63+ bits */
smin = INT64_MIN;
smax = INT64_MAX;
out = 0;
} else {
smin = -(1LL << (bits - 1));
smax = (1LL << (bits - 1)) - 1LL;
uint64_t mask = (bits >= 64) ? UINT64_MAX : ((1ULL << bits) - 1ULL);
uint64_t wrapped = ((uint64_t)vi) & mask;
if (bits >= 64) {
/* 64-bit: already full width; interpret as signed */
out = (int64_t)wrapped;
} else {
uint64_t sign_bit = 1ULL << (bits - 1);
if (wrapped & sign_bit) {
/* sign-extend */
out = (int64_t)(wrapped | (~mask));
} else {
out = (int64_t)wrapped;
}
}
}
if (vi < smin) vi = smin;
else if (vi > smax) vi = smax;
push_value(vm, make_int(vi));
push_value(vm, make_int(out));
free_value(v);
break;
}

View file

@ -34,22 +34,56 @@
case OP_TO_NUMBER: {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
Value out = make_int(v.i);
push_value(vm, make_int(v.i));
free_value(v);
} else if (v.type == VAL_FLOAT) {
double d = v.d;
if (d >= (double)INT64_MIN && d <= (double)INT64_MAX) {
int64_t ii = (int64_t)d;
if ((double)ii == d) {
push_value(vm, make_int(ii));
} else {
push_value(vm, make_float(d));
}
} else {
push_value(vm, make_float(d));
}
free_value(v);
push_value(vm, out);
} else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : "";
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
char *endp = NULL;
long long parsed = strtoll(p, &endp, 10);
/* Try float first to support decimals and scientific notation */
double dval = strtod(p, &endp);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++;
if (endp && *endp != '\0') {
push_value(vm, make_int(0));
if (!endp || *endp != '\0') {
/* Fallback to integer-only parse */
endp = NULL;
long long parsed = strtoll(p, &endp, 10);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++;
if (endp && *endp == '\0') {
push_value(vm, make_int((int64_t)parsed));
} else {
push_value(vm, make_int(0));
}
} else {
push_value(vm, make_int((int64_t)parsed));
/* Preserve int when exact; else float */
if (dval >= (double)INT64_MIN && dval <= (double)INT64_MAX) {
int64_t ii = (int64_t)dval;
if ((double)ii == dval) {
push_value(vm, make_int(ii));
} else {
push_value(vm, make_float(dval));
}
} else {
push_value(vm, make_float(dval));
}
}
free_value(v);
} else if (v.type == VAL_BOOL) {
push_value(vm, make_int(v.i ? 1 : 0));
free_value(v);
} else {
free_value(v);
push_value(vm, make_int(0));

View file

@ -12,6 +12,7 @@ case OP_TYPEOF: {
const char *tname = "Unknown";
switch (v.type) {
case VAL_INT: tname = "Number"; break;
case VAL_FLOAT: tname = "Float"; break;
case VAL_BOOL: tname = "Boolean"; break;
case VAL_STRING: tname = "String"; break;
case VAL_FUNCTION: tname = "Function"; break;

View file

@ -8,27 +8,22 @@
*/
case OP_UCLAMP: {
/* Saturating clamp to unsigned N-bit range: [0 .. 2^N - 1] */
/* Unsigned wrap to N bits: mask lower N bits (operand = bits) */
Value v = pop_value(vm);
int bits = inst.operand;
int64_t vi = (v.type == VAL_INT) ? v.i : 0;
uint64_t umax;
uint64_t mask;
if (bits <= 0) {
/* treat as clamp to 0..0 */
umax = 0;
mask = 0ULL;
} else if (bits >= 64) {
umax = UINT64_MAX;
mask = UINT64_MAX;
} else {
umax = (1ULL << bits) - 1ULL;
mask = (1ULL << bits) - 1ULL;
}
uint64_t u;
if (vi < 0) u = 0;
else if ((uint64_t)vi > umax) u = umax;
else u = (uint64_t)vi;
push_value(vm, make_int((int64_t)u));
uint64_t wrapped = ((uint64_t)vi) & mask;
push_value(vm, make_int((int64_t)wrapped));
free_value(v);
break;
}