1
0
Fork 0
forked from fun/fun

Added to_string() and to_number() functions as built-ins.

This commit is contained in:
Johannes Findeisen 2025-09-15 03:45:26 +02:00
commit 559400a866
11 changed files with 113 additions and 3 deletions

View file

@ -82,6 +82,8 @@ static const char *opcode_name(OpCode op) {
case OP_ARR_INSERT: return "ARR_INSERT";
case OP_ARR_REMOVE: return "ARR_REMOVE";
case OP_SLICE: return "SLICE";
case OP_TO_NUMBER: return "TO_NUMBER";
case OP_TO_STRING: return "TO_STRING";
default: return "???";
}
}

View file

@ -57,7 +57,11 @@ typedef enum {
OP_ARR_SET, // pops value, index, array; pushes value
OP_ARR_INSERT, // pops value, index, array; pushes new length
OP_ARR_REMOVE, // pops index, array; pushes removed element
OP_SLICE // pops end, start, array; pushes new array
OP_SLICE, // pops end, start, array; pushes new array
// conversions
OP_TO_NUMBER, // pops any; pushes int (parse strings)
OP_TO_STRING // pops any; pushes string
} OpCode;
typedef struct {

View file

@ -519,6 +519,22 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
free(name);
return 1;
}
if (strcmp(name, "to_number") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "to_number expects 1 argument"); free(name); return 0; }
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after to_number arg"); free(name); return 0; }
bytecode_add_instruction(bc, OP_TO_NUMBER, 0);
free(name);
return 1;
}
if (strcmp(name, "to_string") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "to_string expects 1 argument"); free(name); return 0; }
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after to_string arg"); free(name); return 0; }
bytecode_add_instruction(bc, OP_TO_STRING, 0);
free(name);
return 1;
}
/* push function value first */
if (local_idx >= 0) {

View file

@ -682,6 +682,47 @@ void vm_run(VM *vm, Bytecode *entry) {
break;
}
case OP_TO_NUMBER: {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
/* pass-through */
Value out = make_int(v.i);
free_value(v);
push_value(vm, out);
} else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : "";
/* trim spaces */
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
char *endp = NULL;
long long parsed = strtoll(p, &endp, 10);
/* skip trailing spaces */
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++;
if (endp && *endp != '\0') {
/* non-numeric suffix -> 0 */
push_value(vm, make_int(0));
} else {
push_value(vm, make_int((int64_t)parsed));
}
free_value(v);
} else {
/* nil, array, function -> 0 */
free_value(v);
push_value(vm, make_int(0));
}
break;
}
case OP_TO_STRING: {
Value v = pop_value(vm);
char *s = value_to_string_alloc(&v);
Value out = make_string(s ? s : "");
if (s) free(s);
free_value(v);
push_value(vm, out);
break;
}
case OP_LOAD_GLOBAL: {
int idx = inst.operand;
if (idx < 0 || idx >= VM_MAX_GLOBALS) {

View file

@ -16,7 +16,8 @@ static const char *opcode_names[] = {
"JUMP_IF_FALSE","CALL","RETURN","PRINT","HALT",
"MOD","AND","OR","NOT","DUP","SWAP",
"MAKE_ARRAY","INDEX_GET","INDEX_SET",
"LEN","ARR_PUSH","ARR_POP","ARR_SET","ARR_INSERT","ARR_REMOVE","SLICE"
"LEN","ARR_PUSH","ARR_POP","ARR_SET","ARR_INSERT","ARR_REMOVE","SLICE",
"TO_NUMBER","TO_STRING"
};
typedef struct {
@ -57,7 +58,7 @@ void vm_dump_globals(VM *vm);
void vm_run(VM *vm, Bytecode *entry);
static inline int opcode_is_valid(int op) {
return op >= OP_NOP && op <= OP_SLICE; // all current opcodes
return op >= OP_NOP && op <= OP_TO_STRING; // all current opcodes
}
#endif