Signed Integers.
This commit is contained in:
parent
6d3456b62c
commit
3f5fabae92
5 changed files with 87 additions and 11 deletions
29
examples/signed_ints.fun
Normal file
29
examples/signed_ints.fun
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
// Signed integer type examples with two's complement wrapping
|
||||
|
||||
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))
|
||||
print("s32 = " + to_string(s32) + " :: " + typeof(s32))
|
||||
print("s64 = " + to_string(s64) + " :: " + typeof(s64))
|
||||
|
||||
// Demonstrate wrapping on assignment beyond range
|
||||
s8 = 130 // 130 -> wraps to -126 in int8
|
||||
s16 = 70000 // wraps into int16 range
|
||||
s32 = 4294967296 // wraps into int32 range
|
||||
s64 = 18446744073709551616 // wraps into int64 range
|
||||
|
||||
print("wrap s8 -> " + to_string(s8))
|
||||
print("wrap s16 -> " + to_string(s16))
|
||||
print("wrap s32 -> " + to_string(s32))
|
||||
print("wrap s64 -> " + to_string(s64))
|
||||
|
||||
// Arithmetic with signed values then wrapping
|
||||
s8 = -120
|
||||
s8 = s8 - 20 // -140 -> wraps into int8 range
|
||||
print("s8 after -20 wrap -> " + to_string(s8))
|
||||
|
|
@ -76,6 +76,7 @@ typedef enum {
|
|||
OP_TO_STRING, // pops any; pushes string
|
||||
OP_TYPEOF, // pops any; pushes string name of type
|
||||
OP_UCLAMP, // pops number; pushes number masked to N bits (operand = bits)
|
||||
OP_SCLAMP, // pops number; pushes number clamped to signed N-bit range (operand = bits)
|
||||
|
||||
// string ops
|
||||
OP_SPLIT, // pops sep, string; pushes array of strings
|
||||
|
|
|
|||
37
src/parser.c
37
src/parser.c
|
|
@ -1454,9 +1454,13 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
return;
|
||||
}
|
||||
|
||||
/* typed declarations: number|string|boolean|nil|uint8|uint16|uint32|uint64 <ident> (= expr)? */
|
||||
/* typed declarations:
|
||||
number|string|boolean|nil|uint8|uint16|uint32|uint64|int8|int16|int32|int64 <ident> (= expr)?
|
||||
Note: 'number' maps to unsigned 64-bit here.
|
||||
*/
|
||||
if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0 || strcmp(name, "nil") == 0
|
||||
|| strcmp(name, "uint8") == 0 || strcmp(name, "uint16") == 0 || strcmp(name, "uint32") == 0 || strcmp(name, "uint64") == 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) {
|
||||
int is_number = (strcmp(name, "number") == 0);
|
||||
int is_string = (strcmp(name, "string") == 0);
|
||||
int is_boolean = (strcmp(name, "boolean") == 0);
|
||||
|
|
@ -1465,7 +1469,15 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
int is_u16 = (strcmp(name, "uint16") == 0);
|
||||
int is_u32 = (strcmp(name, "uint32") == 0);
|
||||
int is_u64 = (strcmp(name, "uint64") == 0) || is_number; /* number maps to uint64 */
|
||||
int decl_bits = is_u8 ? 8 : is_u16 ? 16 : is_u32 ? 32 : is_u64 ? 64 : 0; /* 0 for non-integer types */
|
||||
int is_s8 = (strcmp(name, "int8") == 0);
|
||||
int is_s16 = (strcmp(name, "int16") == 0);
|
||||
int is_s32 = (strcmp(name, "int32") == 0);
|
||||
int is_s64 = (strcmp(name, "int64") == 0);
|
||||
int decl_bits = is_u8 ? 8 : is_u16 ? 16 : is_u32 ? 32 : is_u64 ? 64
|
||||
: is_s8 ? 8 : is_s16 ? 16 : is_s32 ? 32 : is_s64 ? 64 : 0;
|
||||
int decl_signed = (is_s8 || is_s16 || is_s32 || is_s64) ? 1 : 0;
|
||||
/* store decl bits with sign encoded: negative means signed */
|
||||
if (decl_signed) decl_bits = -decl_bits;
|
||||
free(name);
|
||||
|
||||
/* read variable name */
|
||||
|
|
@ -1484,7 +1496,7 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
if (existing >= 0) lidx = existing;
|
||||
else lidx = local_add(varname);
|
||||
if (lidx >= 0) {
|
||||
g_locals->types[lidx] = decl_bits; /* record declared bits (0 for non-integer types) */
|
||||
g_locals->types[lidx] = decl_bits; /* negative = signed, positive = unsigned width, 0 = non-integer */
|
||||
}
|
||||
} else {
|
||||
gi = sym_index(varname);
|
||||
|
|
@ -1502,8 +1514,9 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
return;
|
||||
}
|
||||
/* clamp if integer type was declared */
|
||||
if (decl_bits > 0) {
|
||||
bytecode_add_instruction(bc, OP_UCLAMP, decl_bits);
|
||||
int abs_bits = decl_bits < 0 ? -decl_bits : decl_bits;
|
||||
if (abs_bits > 0) {
|
||||
bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits);
|
||||
}
|
||||
if (lidx >= 0) {
|
||||
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
|
||||
|
|
@ -1517,14 +1530,15 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
ci = bytecode_add_constant(bc, make_string(""));
|
||||
} else if (is_nil) {
|
||||
ci = bytecode_add_constant(bc, make_nil());
|
||||
} else if (is_number || is_boolean || decl_bits > 0) {
|
||||
} else if (is_number || is_boolean || (decl_bits != 0)) {
|
||||
/* integers/booleans default to 0 */
|
||||
ci = bytecode_add_constant(bc, make_int(0));
|
||||
}
|
||||
if (ci >= 0) {
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||
if (decl_bits > 0) {
|
||||
bytecode_add_instruction(bc, OP_UCLAMP, decl_bits);
|
||||
int abs_bits2 = decl_bits < 0 ? -decl_bits : decl_bits;
|
||||
if (abs_bits2 > 0) {
|
||||
bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits2);
|
||||
}
|
||||
if (lidx >= 0) {
|
||||
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
|
||||
|
|
@ -1699,8 +1713,9 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
} else if (gi >= 0) {
|
||||
decl_bits = G.types[gi];
|
||||
}
|
||||
if (decl_bits > 0) {
|
||||
bytecode_add_instruction(bc, OP_UCLAMP, decl_bits);
|
||||
int abs_bits = decl_bits < 0 ? -decl_bits : decl_bits;
|
||||
if (abs_bits > 0) {
|
||||
bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits);
|
||||
}
|
||||
if (lidx >= 0) {
|
||||
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
|
||||
|
|
|
|||
1
src/vm.c
1
src/vm.c
|
|
@ -302,6 +302,7 @@ void vm_run(VM *vm, Bytecode *entry) {
|
|||
#include "vm/to_string.c"
|
||||
#include "vm/typeof.c"
|
||||
#include "vm/uclamp.c"
|
||||
#include "vm/sclamp.c"
|
||||
|
||||
default:
|
||||
if (!opcode_is_valid(inst.op)) {
|
||||
|
|
|
|||
30
src/vm/sclamp.c
Normal file
30
src/vm/sclamp.c
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
case OP_SCLAMP: {
|
||||
/* Clamp/wrap to signed N-bit range: [-2^(N-1), 2^(N-1)-1] */
|
||||
Value v = pop_value(vm);
|
||||
int bits = inst.operand;
|
||||
uint64_t u = 0;
|
||||
|
||||
if (v.type == VAL_INT) {
|
||||
u = (uint64_t)v.i;
|
||||
} else {
|
||||
u = 0;
|
||||
}
|
||||
|
||||
if (bits <= 0) {
|
||||
/* treat as no-op for invalid widths */
|
||||
} else if (bits >= 64) {
|
||||
/* 64-bit signed: nothing to mask, keep as is */
|
||||
} else {
|
||||
uint64_t mask = (1ULL << bits) - 1ULL;
|
||||
u &= mask;
|
||||
uint64_t sign_bit = 1ULL << (bits - 1);
|
||||
if (u & sign_bit) {
|
||||
/* negative value in two's complement */
|
||||
u -= (1ULL << bits);
|
||||
}
|
||||
}
|
||||
|
||||
push_value(vm, make_int((int64_t)u));
|
||||
free_value(v);
|
||||
break;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue