Added the Float type and many fixes. (0.24.0)
This commit is contained in:
parent
95eba1eb12
commit
41fa946f1c
19 changed files with 432 additions and 69 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(fun VERSION 0.23.0 LANGUAGES C)
|
||||
project(fun VERSION 0.24.0 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-04
|
||||
*/
|
||||
|
||||
boolean b = false
|
||||
print("b => " + to_string(b))
|
||||
/* Try typeof via built-in function if available */
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-04
|
||||
*/
|
||||
|
||||
// Boolean datatype usage examples (first-class booleans)
|
||||
|
|
|
|||
78
examples/floats.fun
Executable file
78
examples/floats.fun
Executable file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-04
|
||||
*/
|
||||
|
||||
// Float type demonstration Literals: decimal and scientific notation
|
||||
float a = 1.23
|
||||
float b = 4.0
|
||||
float c = 1e2
|
||||
float d = 5E-1
|
||||
|
||||
// Arithmetic with promotion
|
||||
float s1 = a + b // 1.23 + 4.0 = 5.23
|
||||
float s2 = c - 50 // 100.0 - 50 -> 50.0
|
||||
float p = 2 * d // 2 * 0.5 -> 1.0
|
||||
number q = 7 / 2 // int division -> 3
|
||||
float r = 7 / 2.0 // float division -> 3.5
|
||||
|
||||
print("a=" + to_string(a))
|
||||
print("a: " + typeof(a))
|
||||
print("b=" + to_string(b))
|
||||
print(typeof(b))
|
||||
print("c=" + to_string(c))
|
||||
print(typeof(c))
|
||||
print("d=" + to_string(d))
|
||||
print(typeof(d))
|
||||
print("s1=" + to_string(s1))
|
||||
print(typeof(s1))
|
||||
print("s2=" + to_string(s2))
|
||||
print(typeof(s2))
|
||||
print("p=" + to_string(p))
|
||||
print(typeof(p))
|
||||
print("q=" + to_string(q))
|
||||
print(typeof(q))
|
||||
print("r=" + to_string(r))
|
||||
print(typeof(r))
|
||||
|
||||
// to_number behavior
|
||||
string s = "42.0"
|
||||
number t = to_number(s) // preserves int when exact -> 42
|
||||
print("to_number(\"42.0\")=" + to_string(t))
|
||||
print(typeof(t))
|
||||
float u = to_number("3.14")
|
||||
print("to_number(\"3.14\")=" + to_string(u))
|
||||
print(typeof(u))
|
||||
|
||||
/* Expected output:
|
||||
a=1.23
|
||||
Float
|
||||
b=4
|
||||
Float
|
||||
c=100
|
||||
Float
|
||||
d=0.5
|
||||
Float
|
||||
s1=5.2300000000000004
|
||||
Float
|
||||
s2=50
|
||||
Float
|
||||
p=1
|
||||
Float
|
||||
q=3
|
||||
Number
|
||||
r=3.5
|
||||
Float
|
||||
to_number("42.0")=42
|
||||
Number
|
||||
to_number("3.14")=3.1400000000000001
|
||||
Float
|
||||
*/
|
||||
|
|
@ -11,10 +11,10 @@
|
|||
|
||||
// Signed integer type examples with two's complement wrapping
|
||||
|
||||
sint8 s8 = -1 // should be -1 within sint8 range
|
||||
sint16 s16 = -32768 // min sint16
|
||||
sint32 s32 = -2147483648
|
||||
sint64 s64 = -9223372036854775808
|
||||
int8 s8 = -1 // should be -1 within int8 range
|
||||
int16 s16 = -32768 // min int16
|
||||
int32 s32 = -2147483648
|
||||
int64 s64 = -9223372036854775808
|
||||
|
||||
print("s8 = " + to_string(s8) + " :: " + typeof(s8))
|
||||
print("s16 = " + to_string(s16) + " :: " + typeof(s16))
|
||||
|
|
@ -36,3 +36,15 @@ print("wrap s64 -> " + to_string(s64))
|
|||
s8 = -120
|
||||
s8 = s8 - 20 // -140 -> wraps into int8 range
|
||||
print("s8 after -20 wrap -> " + to_string(s8))
|
||||
|
||||
/* Expected output:
|
||||
s8 = -1 :: Number
|
||||
s16 = -32768 :: Number
|
||||
s32 = -2147483648 :: Number
|
||||
s64 = -9223372036854775808 :: Number
|
||||
wrap s8 -> -126
|
||||
wrap s16 -> 4464
|
||||
wrap s32 -> 0
|
||||
wrap s64 -> 0
|
||||
s8 after -20 wrap -> 116
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -40,3 +40,16 @@ print("u8 after +10 wrap -> " + to_string(u8))
|
|||
// 'number' maps to 64-bit unsigned behavior
|
||||
number n = -1 // will clamp/wrap to 64-bit (implementation-defined display)
|
||||
print("number n = " + to_string(n) + " :: " + typeof(n))
|
||||
|
||||
/* Exprected result:
|
||||
u8 = 255 :: Number
|
||||
u16 = 65535 :: Number
|
||||
u32 = 4294967295 :: Number
|
||||
u64 = -1 :: Number
|
||||
wrap u8 -> 0
|
||||
wrap u16 -> 0
|
||||
wrap u32 -> 0
|
||||
wrap u64 -> 0
|
||||
u8 after +10 wrap -> 4
|
||||
number n = -1 :: Number
|
||||
*/
|
||||
|
|
|
|||
81
src/parser.c
81
src/parser.c
|
|
@ -72,6 +72,7 @@ static int g_temp_counter = 0;
|
|||
#define TYPE_META_BOOLEAN 10002
|
||||
#define TYPE_META_NIL 10003
|
||||
#define TYPE_META_CLASS 10004
|
||||
#define TYPE_META_FLOAT 10005
|
||||
|
||||
static void parser_fail(size_t pos, const char *fmt, ...) {
|
||||
g_has_error = 1;
|
||||
|
|
@ -387,9 +388,45 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* number */
|
||||
/* number (prefer float first to consume cases like 1.23 or 1e2) */
|
||||
int ok = 0;
|
||||
size_t save = *pos;
|
||||
/* float literal */
|
||||
double fval = parse_float_literal_value(src, len, pos, &ok);
|
||||
if (ok) {
|
||||
int ci = bytecode_add_constant(bc, make_float(fval));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||
/* postfix indexing or slice (not typical for floats but keep consistency) */
|
||||
for (;;) {
|
||||
skip_spaces(src, len, pos);
|
||||
if (*pos < len && src[*pos] == '[') {
|
||||
(*pos)++;
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected start expression"); return 0; }
|
||||
skip_spaces(src, len, pos);
|
||||
if (*pos < len && src[*pos] == ':') {
|
||||
(*pos)++;
|
||||
skip_spaces(src, len, pos);
|
||||
size_t savep2 = *pos;
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
*pos = savep2;
|
||||
int ci2 = bytecode_add_constant(bc, make_int(-1));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci2);
|
||||
}
|
||||
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after slice"); return 0; }
|
||||
bytecode_add_instruction(bc, OP_SLICE, 0);
|
||||
continue;
|
||||
} else {
|
||||
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); return 0; }
|
||||
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
*pos = save;
|
||||
/* integer literal */
|
||||
int64_t ival = parse_int_literal_value(src, len, pos, &ok);
|
||||
if (ok) {
|
||||
int ci = bytecode_add_constant(bc, make_int(ival));
|
||||
|
|
@ -426,7 +463,6 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
*pos = save;
|
||||
|
||||
/* identifier or keyword */
|
||||
char *name = NULL;
|
||||
|
|
@ -2131,7 +2167,7 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
Note: 'number' maps to signed 64-bit here. 'byte' is an alias of unsigned 8-bit. 'Class' restricts to class instances.
|
||||
*/
|
||||
if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0 || strcmp(name, "nil") == 0
|
||||
|| strcmp(name, "Class") == 0
|
||||
|| strcmp(name, "Class") == 0 || strcmp(name, "float") == 0
|
||||
|| strcmp(name, "byte") == 0
|
||||
|| strcmp(name, "uint8") == 0 || strcmp(name, "uint16") == 0 || strcmp(name, "uint32") == 0 || strcmp(name, "uint64") == 0
|
||||
|| strcmp(name, "int8") == 0 || strcmp(name, "int16") == 0 || strcmp(name, "int32") == 0 || strcmp(name, "int64") == 0) {
|
||||
|
|
@ -2140,6 +2176,7 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
int is_boolean = (strcmp(name, "boolean") == 0);
|
||||
int is_nil = (strcmp(name, "nil") == 0);
|
||||
int is_class_tkn = (strcmp(name, "Class") == 0);
|
||||
int is_float_tkn = (strcmp(name, "float") == 0);
|
||||
int is_byte = (strcmp(name, "byte") == 0);
|
||||
int is_u8 = (strcmp(name, "uint8") == 0) || is_byte;
|
||||
int is_u16 = (strcmp(name, "uint16") == 0);
|
||||
|
|
@ -2155,7 +2192,7 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
/* store decl bits with sign encoded: negative means signed (number is signed 64-bit) */
|
||||
if (decl_signed) decl_bits = -decl_bits;
|
||||
|
||||
/* declared type metadata: integers use decl_bits; string/boolean/nil/Class use special markers */
|
||||
/* declared type metadata: integers use decl_bits; string/boolean/nil/Class/float use special markers */
|
||||
int decl_meta = decl_bits;
|
||||
if (is_string) {
|
||||
decl_meta = TYPE_META_STRING;
|
||||
|
|
@ -2165,6 +2202,8 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
decl_meta = TYPE_META_NIL;
|
||||
} else if (is_class_tkn) {
|
||||
decl_meta = TYPE_META_CLASS;
|
||||
} else if (is_float_tkn) {
|
||||
decl_meta = TYPE_META_FLOAT;
|
||||
}
|
||||
|
||||
free(name);
|
||||
|
|
@ -2255,6 +2294,23 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
}
|
||||
/* Continue after checks */
|
||||
bytecode_set_operand(bc, j_ok, bc->instr_count);
|
||||
} else if (decl_meta == TYPE_META_FLOAT) {
|
||||
/* expect Float */
|
||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||
bytecode_add_instruction(bc, OP_TYPEOF, 0);
|
||||
int ciF = bytecode_add_constant(bc, make_string("Float"));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ciF);
|
||||
bytecode_add_instruction(bc, OP_EQ, 0);
|
||||
int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||
int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
bytecode_set_operand(bc, j_to_error, bc->instr_count);
|
||||
{
|
||||
int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Float"));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_HALT, 0);
|
||||
}
|
||||
bytecode_set_operand(bc, j_skip_err, bc->instr_count);
|
||||
} else if (decl_meta == TYPE_META_BOOLEAN) {
|
||||
/* accept Boolean literal or Number; if Number, clamp to 0/1 */
|
||||
/* check if value is Boolean */
|
||||
|
|
@ -2581,6 +2637,23 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
}
|
||||
/* Continue after checks */
|
||||
bytecode_set_operand(bc, j_ok, bc->instr_count);
|
||||
} else if (meta == TYPE_META_FLOAT) {
|
||||
/* expect Float */
|
||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||
bytecode_add_instruction(bc, OP_TYPEOF, 0);
|
||||
int ciF = bytecode_add_constant(bc, make_string("Float"));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ciF);
|
||||
bytecode_add_instruction(bc, OP_EQ, 0);
|
||||
int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||
int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
bytecode_set_operand(bc, j_to_error, bc->instr_count);
|
||||
{
|
||||
int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Float"));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_HALT, 0);
|
||||
}
|
||||
bytecode_set_operand(bc, j_skip_err, bc->instr_count);
|
||||
} else if (meta == TYPE_META_BOOLEAN) {
|
||||
/* expect Number then clamp to 1 bit (unsigned) */
|
||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||
|
|
|
|||
|
|
@ -697,3 +697,61 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
|||
static char *preprocess_includes(const char *src) {
|
||||
return preprocess_includes_internal(src, 0);
|
||||
}
|
||||
|
||||
|
||||
/* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */
|
||||
static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
|
||||
size_t p = *pos;
|
||||
skip_spaces(src, len, &p);
|
||||
size_t start = p;
|
||||
int saw_digit = 0;
|
||||
int saw_dot = 0;
|
||||
int saw_exp = 0;
|
||||
|
||||
/* optional sign */
|
||||
if (p < len && (src[p] == '+' || src[p] == '-')) p++;
|
||||
|
||||
/* integer part */
|
||||
while (p < len && isdigit((unsigned char)src[p])) { p++; saw_digit = 1; }
|
||||
|
||||
/* fractional part */
|
||||
if (p < len && src[p] == '.') {
|
||||
saw_dot = 1;
|
||||
p++;
|
||||
while (p < len && isdigit((unsigned char)src[p])) { p++; saw_digit = 1; }
|
||||
}
|
||||
|
||||
/* exponent part */
|
||||
if (p < len && (src[p] == 'e' || src[p] == 'E')) {
|
||||
saw_exp = 1;
|
||||
size_t epos = p + 1;
|
||||
if (epos < len && (src[epos] == '+' || src[epos] == '-')) epos++;
|
||||
size_t digits_start = epos;
|
||||
while (epos < len && isdigit((unsigned char)src[epos])) { epos++; }
|
||||
if (epos == digits_start) {
|
||||
/* no digits after exponent -> not a float */
|
||||
*ok = 0; return 0.0;
|
||||
}
|
||||
p = epos;
|
||||
}
|
||||
|
||||
if (!saw_digit || (!saw_dot && !saw_exp)) {
|
||||
*ok = 0; return 0.0;
|
||||
}
|
||||
|
||||
/* Create temporary buffer to parse with strtod safely */
|
||||
size_t n = p - start;
|
||||
char *tmp = (char*)malloc(n + 1);
|
||||
if (!tmp) { *ok = 0; return 0.0; }
|
||||
memcpy(tmp, src + start, n);
|
||||
tmp[n] = '\0';
|
||||
|
||||
char *endp = NULL;
|
||||
double dv = strtod(tmp, &endp);
|
||||
if (!endp || *endp != '\0') { free(tmp); *ok = 0; return 0.0; }
|
||||
|
||||
*pos = p;
|
||||
*ok = 1;
|
||||
free(tmp);
|
||||
return dv;
|
||||
}
|
||||
|
|
|
|||
28
src/value.c
28
src/value.c
|
|
@ -37,6 +37,13 @@ Value make_int(int64_t v) {
|
|||
return val;
|
||||
}
|
||||
|
||||
Value make_float(double v) {
|
||||
Value val;
|
||||
val.type = VAL_FLOAT;
|
||||
val.d = v;
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_bool(int v) {
|
||||
Value val;
|
||||
val.type = VAL_BOOL;
|
||||
|
|
@ -229,6 +236,9 @@ Value copy_value(const Value *v) {
|
|||
case VAL_INT:
|
||||
out.i = v->i;
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
out.d = v->d;
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
out.i = v->i ? 1 : 0;
|
||||
break;
|
||||
|
|
@ -262,6 +272,8 @@ Value deep_copy_value(const Value *v) {
|
|||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return make_int(v->i);
|
||||
case VAL_FLOAT:
|
||||
return make_float(v->d);
|
||||
case VAL_BOOL:
|
||||
return make_bool(v->i);
|
||||
case VAL_STRING:
|
||||
|
|
@ -334,6 +346,9 @@ void print_value(const Value *v) {
|
|||
case VAL_INT:
|
||||
printf("%" PRId64, v->i);
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
printf("%.17g", v->d);
|
||||
break;
|
||||
case VAL_STRING:
|
||||
printf("%s", v->s ? v->s : "");
|
||||
break;
|
||||
|
|
@ -379,6 +394,8 @@ int value_is_truthy(const Value *v) {
|
|||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return v->i != 0;
|
||||
case VAL_FLOAT:
|
||||
return v->d != 0.0;
|
||||
case VAL_BOOL:
|
||||
return v->i != 0;
|
||||
case VAL_STRING:
|
||||
|
|
@ -405,6 +422,11 @@ char *value_to_string_alloc(const Value *v) {
|
|||
snprintf(tmp, sizeof(tmp), "%" PRId64, v->i);
|
||||
return strdup(tmp);
|
||||
}
|
||||
case VAL_FLOAT: {
|
||||
char tmp[64];
|
||||
snprintf(tmp, sizeof(tmp), "%.17g", v->d);
|
||||
return strdup(tmp);
|
||||
}
|
||||
case VAL_STRING:
|
||||
return strdup(v->s ? v->s : "");
|
||||
case VAL_BOOL:
|
||||
|
|
@ -426,6 +448,12 @@ char *value_to_string_alloc(const Value *v) {
|
|||
}
|
||||
|
||||
int value_equals(const Value *a, const Value *b) {
|
||||
// Numeric cross-type equality: int vs float compares numerically
|
||||
if ((a->type == VAL_INT || a->type == VAL_FLOAT) && (b->type == VAL_INT || b->type == VAL_FLOAT)) {
|
||||
double da = (a->type == VAL_INT) ? (double)a->i : a->d;
|
||||
double db = (b->type == VAL_INT) ? (double)b->i : b->d;
|
||||
return da == db;
|
||||
}
|
||||
if (a->type != b->type) return 0;
|
||||
switch (a->type) {
|
||||
case VAL_INT: return a->i == b->i;
|
||||
|
|
|
|||
|
|
@ -51,13 +51,15 @@ typedef enum {
|
|||
VAL_FUNCTION,
|
||||
VAL_ARRAY,
|
||||
VAL_MAP,
|
||||
VAL_NIL
|
||||
VAL_NIL,
|
||||
VAL_FLOAT
|
||||
} ValueType;
|
||||
|
||||
typedef struct {
|
||||
ValueType type;
|
||||
union {
|
||||
int64_t i;
|
||||
double d;
|
||||
char *s;
|
||||
struct Bytecode *fn;
|
||||
struct Array *arr;
|
||||
|
|
@ -71,6 +73,7 @@ Value make_bool(int v);
|
|||
Value make_string(const char *s);
|
||||
Value make_function(struct Bytecode *fn);
|
||||
Value make_nil(void);
|
||||
Value make_float(double v);
|
||||
|
||||
/* arrays */
|
||||
Value make_array_from_values(const Value *vals, int count); /* deep-copies vals */
|
||||
|
|
|
|||
4
src/vm.c
4
src/vm.c
|
|
@ -119,6 +119,10 @@ static const char* value_type_name(ValueType t) {
|
|||
switch (t) {
|
||||
case VAL_FUNCTION: return "function";
|
||||
case VAL_INT: return "int";
|
||||
case VAL_FLOAT: return "float";
|
||||
case VAL_BOOL: return "boolean";
|
||||
case VAL_ARRAY: return "array";
|
||||
case VAL_MAP: return "map";
|
||||
case VAL_NIL: return "nil";
|
||||
case VAL_STRING: return "string";
|
||||
default: return "unknown";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue