Added some crypto 100% implemented in Fun. (0.11.0)
This commit is contained in:
parent
5d3cfd9f65
commit
12a5646d56
25 changed files with 1926 additions and 12 deletions
|
|
@ -112,7 +112,17 @@ typedef enum {
|
|||
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
|
||||
|
||||
// OS
|
||||
OP_ENV // pops name string; pushes value string (or "")
|
||||
OP_ENV, // pops name string; pushes value string (or "")
|
||||
|
||||
// Bitwise (32-bit) and shifts/rotates
|
||||
OP_BAND, // pops b, a; pushes (uint32_t)(a & b)
|
||||
OP_BOR, // pops b, a; pushes (uint32_t)(a | b)
|
||||
OP_BXOR, // pops b, a; pushes (uint32_t)(a ^ b)
|
||||
OP_BNOT, // pops a; pushes (uint32_t)(~a)
|
||||
OP_SHL, // pops s, a; pushes (uint32_t)(a << (s&31))
|
||||
OP_SHR, // pops s, a; pushes (uint32_t)(a >> (s&31)) logical
|
||||
OP_ROTL, // pops s, a; pushes rotl32(a, s)
|
||||
OP_ROTR // pops s, a; pushes rotr32(a, s)
|
||||
} OpCode;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
109
src/parser.c
109
src/parser.c
|
|
@ -1073,6 +1073,72 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* bitwise ops (32-bit) */
|
||||
if (strcmp(name, "band") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "band expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "band expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_BAND, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "bor") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "bor expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "bor expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_BOR, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "bxor") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "bxor expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "bxor expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_BXOR, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "bnot") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "bnot expects 1 arg"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "bnot expects 1 arg"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_BNOT, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "shl") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "shl expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "shl expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_SHL, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "shr") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "shr expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "shr expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_SHR, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "rol") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "rol expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "rol expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_ROTL, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "ror") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "ror expects 2 args"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "ror expects 2 args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_ROTR, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* push function value first */
|
||||
if (local_idx >= 0) {
|
||||
bytecode_add_instruction(bc, OP_LOAD_LOCAL, local_idx);
|
||||
|
|
@ -3308,7 +3374,48 @@ Bytecode *parse_file_to_bytecode(const char *path) {
|
|||
calc_line_col(compile_src, compile_len, g_err_pos, &line, &col);
|
||||
g_err_line = line;
|
||||
g_err_col = col;
|
||||
fprintf(stderr, "Parse error %s:%d:%d: %s\n", path ? path : "<input>", line, col, g_err_msg);
|
||||
|
||||
/* Try to locate include context marker preceding error */
|
||||
const char *marker = "// __include_begin__: ";
|
||||
size_t mlen = strlen(marker);
|
||||
int inner_line = -1;
|
||||
char inc_path[512]; inc_path[0] = '\0';
|
||||
/* scan backward to find last marker line */
|
||||
size_t scan = g_err_pos;
|
||||
while (scan > 0) {
|
||||
/* find start of current line */
|
||||
size_t ls = scan;
|
||||
while (ls > 0 && compile_src[ls - 1] != '\n') ls--;
|
||||
/* check if this line starts with marker */
|
||||
if (ls + mlen <= compile_len && strncmp(compile_src + ls, marker, mlen) == 0) {
|
||||
/* extract path until end-of-line */
|
||||
size_t p = ls + mlen;
|
||||
size_t pe = p;
|
||||
while (pe < compile_len && compile_src[pe] != '\n' && (pe - p) < sizeof(inc_path) - 1) pe++;
|
||||
memcpy(inc_path, compile_src + p, pe - p);
|
||||
inc_path[pe - p] = '\0';
|
||||
/* compute inner line as number of newlines from (pe+1) to error position */
|
||||
int count = 1;
|
||||
size_t q = (pe < compile_len && compile_src[pe] == '\n') ? (pe + 1) : pe;
|
||||
while (q < g_err_pos) {
|
||||
if (compile_src[q] == '\n') count++;
|
||||
q++;
|
||||
}
|
||||
inner_line = count;
|
||||
break;
|
||||
}
|
||||
/* move to previous line */
|
||||
if (ls == 0) break;
|
||||
scan = ls - 1;
|
||||
}
|
||||
|
||||
if (inner_line > 0 && inc_path[0] != '\0') {
|
||||
fprintf(stderr, "Parse error %s:%d:%d: %s (in %s:%d)\n",
|
||||
path ? path : "<input>", line, col, g_err_msg, inc_path, inner_line);
|
||||
} else {
|
||||
fprintf(stderr, "Parse error %s:%d:%d: %s\n", path ? path : "<input>", line, col, g_err_msg);
|
||||
}
|
||||
|
||||
if (bc) bytecode_free(bc);
|
||||
if (prep) free(prep);
|
||||
free(src);
|
||||
|
|
|
|||
|
|
@ -325,6 +325,10 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
|||
char *exp = preprocess_includes_internal(inc, depth + 1);
|
||||
free(inc);
|
||||
if (exp) {
|
||||
/* mark file origin for better error messages */
|
||||
sb_append(&out, "// __include_begin__: ");
|
||||
sb_append(&out, resolved);
|
||||
sb_append(&out, "\n");
|
||||
sb_append(&out, exp);
|
||||
/* ensure included chunk ends with newline to preserve line structure */
|
||||
if (out.len == 0 || out.buf[out.len - 1] != '\n') sb_append_ch(&out, '\n');
|
||||
|
|
|
|||
28
src/vm.c
28
src/vm.c
|
|
@ -25,11 +25,21 @@ static VM *g_active_vm = NULL;
|
|||
static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
||||
int written = vfprintf(stream, fmt, ap);
|
||||
if (stream == stderr && g_active_vm && g_active_vm->current_line > 0) {
|
||||
/* If original message didn't end with newline, add a space first */
|
||||
if (written > 0) {
|
||||
/* best-effort: do not attempt to inspect fmt string fully; just append line info */
|
||||
/* Append more context: line number, opcode name, and ip */
|
||||
const char *opname = "unknown";
|
||||
int ip = -1;
|
||||
if (g_active_vm->fp >= 0) {
|
||||
Frame *f = &g_active_vm->frames[g_active_vm->fp];
|
||||
ip = f->ip - 1;
|
||||
if (f->fn && ip >= 0 && ip < f->fn->instr_count) {
|
||||
int op = f->fn->instructions[ip].op;
|
||||
if (op >= 0 && op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0]))) {
|
||||
opname = opcode_names[op];
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stream == stderr ? stderr : stream, " (line %d)\n", g_active_vm->current_line);
|
||||
fprintf(stream == stderr ? stderr : stream, " (line %d, op %s @ip %d)\n",
|
||||
g_active_vm->current_line, opname, ip);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
|
@ -249,6 +259,16 @@ void vm_run(VM *vm, Bytecode *entry) {
|
|||
#include "vm/arrays/slice.c"
|
||||
#include "vm/arrays/zip.c"
|
||||
|
||||
/* Bitwise and shifts/rotates */
|
||||
#include "vm/bitwise/band.c"
|
||||
#include "vm/bitwise/bor.c"
|
||||
#include "vm/bitwise/bxor.c"
|
||||
#include "vm/bitwise/bnot.c"
|
||||
#include "vm/bitwise/shl.c"
|
||||
#include "vm/bitwise/shr.c"
|
||||
#include "vm/bitwise/rol.c"
|
||||
#include "vm/bitwise/ror.c"
|
||||
|
||||
#include "vm/core/call.c"
|
||||
#include "vm/core/dup.c"
|
||||
#include "vm/core/halt.c"
|
||||
|
|
|
|||
5
src/vm.h
5
src/vm.h
|
|
@ -33,7 +33,8 @@ static const char *opcode_names[] = {
|
|||
"ENUMERATE","ZIP",
|
||||
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
|
||||
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
|
||||
"READ_FILE","WRITE_FILE","ENV"
|
||||
"READ_FILE","WRITE_FILE","ENV",
|
||||
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR"
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -76,7 +77,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_ENV; // all current opcodes
|
||||
return op >= OP_NOP && op <= OP_ROTR; // all current opcodes
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ case OP_INDEX_GET: {
|
|||
free_value(idx);
|
||||
push_value(vm, out);
|
||||
} else {
|
||||
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map\n");
|
||||
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map (got container=%s, index=%s)\n",
|
||||
value_type_name(container.type), value_type_name(idx.type));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
27
src/vm/bitwise/band.c
Normal file
27
src/vm/bitwise/band.c
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_BAND: bitwise AND (uint32 32-bit)
|
||||
* pops: b, a
|
||||
* pushes: (uint32_t)(a & b)
|
||||
*/
|
||||
case OP_BAND: {
|
||||
Value vb = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
|
||||
uint32_t r = a & b;
|
||||
free_value(vb);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
24
src/vm/bitwise/bnot.c
Normal file
24
src/vm/bitwise/bnot.c
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_BNOT: bitwise NOT (uint32 32-bit)
|
||||
* pops: a
|
||||
* pushes: (uint32_t)(~a)
|
||||
*/
|
||||
case OP_BNOT: {
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t r = ~a;
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
27
src/vm/bitwise/bor.c
Normal file
27
src/vm/bitwise/bor.c
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_BOR: bitwise OR (uint32 32-bit)
|
||||
* pops: b, a
|
||||
* pushes: (uint32_t)(a | b)
|
||||
*/
|
||||
case OP_BOR: {
|
||||
Value vb = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
|
||||
uint32_t r = a | b;
|
||||
free_value(vb);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
27
src/vm/bitwise/bxor.c
Normal file
27
src/vm/bitwise/bxor.c
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_BXOR: bitwise XOR (uint32 32-bit)
|
||||
* pops: b, a
|
||||
* pushes: (uint32_t)(a ^ b)
|
||||
*/
|
||||
case OP_BXOR: {
|
||||
Value vb = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
|
||||
uint32_t r = a ^ b;
|
||||
free_value(vb);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
28
src/vm/bitwise/rol.c
Normal file
28
src/vm/bitwise/rol.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_ROTL: rotate left (uint32)
|
||||
* pops: s, a
|
||||
* pushes: rotl32(a, s)
|
||||
*/
|
||||
case OP_ROTL: {
|
||||
Value vs = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||
s &= 31u;
|
||||
uint32_t r = (s == 0u) ? a : ((a << s) | (a >> (32u - s)));
|
||||
free_value(vs);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
28
src/vm/bitwise/ror.c
Normal file
28
src/vm/bitwise/ror.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_ROTR: rotate right (uint32)
|
||||
* pops: s, a
|
||||
* pushes: rotr32(a, s)
|
||||
*/
|
||||
case OP_ROTR: {
|
||||
Value vs = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||
s &= 31u;
|
||||
uint32_t r = (s == 0u) ? a : ((a >> s) | (a << (32u - s)));
|
||||
free_value(vs);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
28
src/vm/bitwise/shl.c
Normal file
28
src/vm/bitwise/shl.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_SHL: logical left shift (uint32)
|
||||
* pops: s, a
|
||||
* pushes: (uint32_t)(a << (s&31))
|
||||
*/
|
||||
case OP_SHL: {
|
||||
Value vs = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||
s &= 31u;
|
||||
uint32_t r = (s == 0u) ? a : (a << s);
|
||||
free_value(vs);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
28
src/vm/bitwise/shr.c
Normal file
28
src/vm/bitwise/shr.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* 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 ISC license.
|
||||
* https://opensource.org/license/isc-license-txt
|
||||
*
|
||||
* Added: 2025-09-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* OP_SHR: logical right shift (uint32)
|
||||
* pops: s, a
|
||||
* pushes: (uint32_t)(a >> (s&31)) using logical shift
|
||||
*/
|
||||
case OP_SHR: {
|
||||
Value vs = pop_value(vm);
|
||||
Value va = pop_value(vm);
|
||||
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||
s &= 31u;
|
||||
uint32_t r = (s == 0u) ? a : (a >> s);
|
||||
free_value(vs);
|
||||
free_value(va);
|
||||
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||
break;
|
||||
}
|
||||
|
|
@ -40,8 +40,8 @@ case OP_LEN: {
|
|||
len = array_length(&a);
|
||||
if (len < 0) len = 0;
|
||||
} else {
|
||||
fprintf(stderr, "Runtime type error: LEN expects array or string\n");
|
||||
exit(1);
|
||||
/* Be lenient: for non-array/non-string, treat length as 0 */
|
||||
push_value(vm, make_int(0));
|
||||
}
|
||||
free_value(a);
|
||||
push_value(vm, make_int(len));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue