Added to_string() and to_number() functions as built-ins.
This commit is contained in:
parent
d67c541824
commit
559400a866
11 changed files with 113 additions and 3 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
// Arrays basics
|
// Arrays basics
|
||||||
arr = [1, 2, 3]
|
arr = [1, 2, 3]
|
||||||
print(arr) // -> [1, 2, 3]
|
print(arr) // -> [1, 2, 3]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
// Advanced Arrays Demo
|
// Advanced Arrays Demo
|
||||||
|
|
||||||
// Start with a basic array
|
// Start with a basic array
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
// for-in over an array literal
|
// for-in over an array literal
|
||||||
for x in [1, 2, 3]
|
for x in [1, 2, 3]
|
||||||
print(x) // prints: 1, then 2, then 3
|
print(x) // prints: 1, then 2, then 3
|
||||||
|
|
|
||||||
37
examples/builtins_conversions.fun
Normal file
37
examples/builtins_conversions.fun
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
// Conversions and length built-ins demo
|
||||||
|
|
||||||
|
// len on array and string
|
||||||
|
print(len([1, 2, 3])) // -> 3
|
||||||
|
print(len("hello")) // -> 5
|
||||||
|
|
||||||
|
// to_number with various string inputs
|
||||||
|
print(to_number("42")) // -> 42
|
||||||
|
print(to_number(" -7 ")) // -> -7
|
||||||
|
print(to_number("12a")) // non-numeric suffix -> 0
|
||||||
|
print(to_number("")) // empty -> 0
|
||||||
|
print(to_number(" ")) // spaces -> 0
|
||||||
|
|
||||||
|
// to_string on numbers and arrays
|
||||||
|
print(to_string(42)) // -> "42" (printed as 42)
|
||||||
|
|
||||||
|
arr = [1, 2]
|
||||||
|
s = to_string(arr) // -> "[array n=2]"
|
||||||
|
print(s)
|
||||||
|
|
||||||
|
// combine to_string with concatenation
|
||||||
|
print("val=" + to_string(99)) // -> "val=99"
|
||||||
|
|
||||||
|
/* Expected output:
|
||||||
|
3
|
||||||
|
5
|
||||||
|
42
|
||||||
|
-7
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
42
|
||||||
|
[array n=2]
|
||||||
|
val=99
|
||||||
|
*/
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
// Nested loops: break and continue behavior
|
// Nested loops: break and continue behavior
|
||||||
|
|
||||||
// 1) Nested for range: inner continue on j==2, inner break on j==4, outer break on i==3
|
// 1) Nested for range: inner continue on j==2, inner break on j==4, outer break on i==3
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env fun
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
// Strings test: concatenation with variables and literals, functions returning strings
|
// Strings test: concatenation with variables and literals, functions returning strings
|
||||||
|
|
||||||
print("=== strings test start ===")
|
print("=== strings test start ===")
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@ static const char *opcode_name(OpCode op) {
|
||||||
case OP_ARR_INSERT: return "ARR_INSERT";
|
case OP_ARR_INSERT: return "ARR_INSERT";
|
||||||
case OP_ARR_REMOVE: return "ARR_REMOVE";
|
case OP_ARR_REMOVE: return "ARR_REMOVE";
|
||||||
case OP_SLICE: return "SLICE";
|
case OP_SLICE: return "SLICE";
|
||||||
|
case OP_TO_NUMBER: return "TO_NUMBER";
|
||||||
|
case OP_TO_STRING: return "TO_STRING";
|
||||||
default: return "???";
|
default: return "???";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,11 @@ typedef enum {
|
||||||
OP_ARR_SET, // pops value, index, array; pushes value
|
OP_ARR_SET, // pops value, index, array; pushes value
|
||||||
OP_ARR_INSERT, // pops value, index, array; pushes new length
|
OP_ARR_INSERT, // pops value, index, array; pushes new length
|
||||||
OP_ARR_REMOVE, // pops index, array; pushes removed element
|
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;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
16
src/parser.c
16
src/parser.c
|
|
@ -519,6 +519,22 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
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 */
|
/* push function value first */
|
||||||
if (local_idx >= 0) {
|
if (local_idx >= 0) {
|
||||||
|
|
|
||||||
41
src/vm.c
41
src/vm.c
|
|
@ -682,6 +682,47 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
break;
|
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: {
|
case OP_LOAD_GLOBAL: {
|
||||||
int idx = inst.operand;
|
int idx = inst.operand;
|
||||||
if (idx < 0 || idx >= VM_MAX_GLOBALS) {
|
if (idx < 0 || idx >= VM_MAX_GLOBALS) {
|
||||||
|
|
|
||||||
5
src/vm.h
5
src/vm.h
|
|
@ -16,7 +16,8 @@ static const char *opcode_names[] = {
|
||||||
"JUMP_IF_FALSE","CALL","RETURN","PRINT","HALT",
|
"JUMP_IF_FALSE","CALL","RETURN","PRINT","HALT",
|
||||||
"MOD","AND","OR","NOT","DUP","SWAP",
|
"MOD","AND","OR","NOT","DUP","SWAP",
|
||||||
"MAKE_ARRAY","INDEX_GET","INDEX_SET",
|
"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 {
|
typedef struct {
|
||||||
|
|
@ -57,7 +58,7 @@ void vm_dump_globals(VM *vm);
|
||||||
void vm_run(VM *vm, Bytecode *entry);
|
void vm_run(VM *vm, Bytecode *entry);
|
||||||
|
|
||||||
static inline int opcode_is_valid(int op) {
|
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
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue