#include "cvm_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#ifdef _WIN32
# include <windows.h>
#else
# include <unistd.h>
#endif
static int arg_num(Runtime *rt, Value v, double *out, const char *fn) {
if (v.type == VAL_INT) { *out = (double)v.as.i; return 1; }
if (v.type == VAL_NUM) { *out = v.as.num; return 1; }
snprintf(rt->errbuf, sizeof(rt->errbuf), "函数 '%s' 需要数字参数", fn);
rt->has_error = 1;
return 0;
}
static Value b_print(Runtime *rt, int argc, Value *a) {
for (int i = 0; i < argc; ++i) {
char *s = val_to_str(&rt->arena, a[i]);
if (i) fputc(' ', stdout);
fputs(s, stdout);
}
fflush(stdout);
return val_nil();
}
static Value b_println(Runtime *rt, int argc, Value *a) {
b_print(rt, argc, a);
fputc('\n', stdout);
return val_nil();
}
static Value b_sqrt(Runtime *rt, int argc, Value *a) {
double x; if (argc != 1 || !arg_num(rt, a[0], &x, "sqrt")) return val_nil();
return val_num(sqrt(x));
}
static Value b_pow(Runtime *rt, int argc, Value *a) {
double x, y; if (argc != 2 || !arg_num(rt, a[0], &x, "pow") || !arg_num(rt, a[1], &y, "pow")) return val_nil();
return val_num(pow(x, y));
}
static Value b_abs(Runtime *rt, int argc, Value *a) {
double x; if (argc != 1 || !arg_num(rt, a[0], &x, "abs")) return val_nil();
return val_num(fabs(x));
}
static Value b_floor(Runtime *rt, int argc, Value *a) {
double x; if (argc != 1 || !arg_num(rt, a[0], &x, "floor")) return val_nil();
return val_num(floor(x));
}
static Value b_ceil(Runtime *rt, int argc, Value *a) {
double x; if (argc != 1 || !arg_num(rt, a[0], &x, "ceil")) return val_nil();
return val_num(ceil(x));
}
static Value b_round(Runtime *rt, int argc, Value *a) {
double x; if (argc != 1 || !arg_num(rt, a[0], &x, "round")) return val_nil();
return val_num((double)((long long)(x + (x >= 0 ? 0.5 : -0.5))));
}
static Value b_rand(Runtime *rt, int argc, Value *a) {
(void)argc; (void)a;
return val_num((double)rand() / (double)RAND_MAX);
}
static Value b_time(Runtime *rt, int argc, Value *a) {
(void)argc; (void)a;
return val_num((double)time(NULL));
}
static Value b_clock_ms(Runtime *rt, int argc, Value *a) {
(void)argc; (void)a;
return val_num((double)clock() * 1000.0 / (double)CLOCKS_PER_SEC);
}
static Value b_len(Runtime *rt, int argc, Value *a) {
if (argc != 1) { snprintf(rt->errbuf, sizeof(rt->errbuf), "len 需要 1 个参数"); rt->has_error = 1; return val_nil(); }
if (a[0].type == VAL_STR) return val_num((double)strlen(a[0].as.str));
if (a[0].type == VAL_ARRAY) return val_num((double)arr_len((const Arr *)a[0].as.arr));
snprintf(rt->errbuf, sizeof(rt->errbuf), "len 需要字符串或数组"); rt->has_error = 1; return val_nil();
}
static Value b_push(Runtime *rt, int argc, Value *a) {
if (argc < 2 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "push(arr, v, ...) 需要数组作为首个参数"); rt->has_error = 1; return val_nil(); }
for (int i = 1; i < argc; ++i) arr_append(&rt->arena, (Arr *)a[0].as.arr, a[i]);
return a[0];
}
static Value b_arr_get(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_get(arr, key) 用法错误"); rt->has_error = 1; return val_nil(); }
ArrEntry *e = arr_lookup(rt, (Arr *)a[0].as.arr, a[1]);
return e ? e->value : val_nil();
}
static Value b_arr_set(Runtime *rt, int argc, Value *a) {
if (argc != 3 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_set(arr, key, val) 用法错误"); rt->has_error = 1; return val_nil(); }
arr_set_from_key(rt, (Arr *)a[0].as.arr, a[1], a[2]);
return a[0];
}
static Value b_arr_has(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_has(arr, key) 用法错误"); rt->has_error = 1; return val_nil(); }
return val_bool(arr_lookup(rt, (Arr *)a[0].as.arr, a[1]) != NULL);
}
static Value b_arr_del(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_del(arr, key) 用法错误"); rt->has_error = 1; return val_nil(); }
return val_bool(arr_del((Arr *)a[0].as.arr, a[1]));
}
static Value b_arr_keys(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_keys(arr) 需要数组"); rt->has_error = 1; return val_nil(); }
return val_array(arr_keys(&rt->arena, (const Arr *)a[0].as.arr));
}
static Value b_arr_values(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_values(arr) 需要数组"); rt->has_error = 1; return val_nil(); }
return val_array(arr_values(&rt->arena, (const Arr *)a[0].as.arr));
}
static Value b_arr_merge(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_ARRAY || a[1].type != VAL_ARRAY) { snprintf(rt->errbuf, sizeof(rt->errbuf), "arr_merge(a, b) 需要两个数组"); rt->has_error = 1; return val_nil(); }
Arr *dst = (Arr *)arena_alloc(&rt->arena, sizeof(Arr));
arr_init(&rt->arena, dst);
for (int i = 0; i < arr_len((const Arr *)a[0].as.arr); ++i) {
ArrEntry *e = arr_at((const Arr *)a[0].as.arr, i);
arr_set(&rt->arena, dst, e->is_str, e->skey, e->ikey, e->value);
}
for (int i = 0; i < arr_len((const Arr *)a[1].as.arr); ++i) {
ArrEntry *e = arr_at((const Arr *)a[1].as.arr, i);
arr_set(&rt->arena, dst, e->is_str, e->skey, e->ikey, e->value);
}
return val_array(dst);
}
static Value b_type(Runtime *rt, int argc, Value *a) {
(void)argc;
return val_str(arena_strdup(&rt->arena, val_type_name(a[0])));
}
static Value b_str(Runtime *rt, int argc, Value *a) {
(void)argc;
return val_str(val_to_str(&rt->arena, a[0]));
}
static Value b_num(Runtime *rt, int argc, Value *a) {
if (argc != 1) { snprintf(rt->errbuf, sizeof(rt->errbuf), "num 需要 1 个参数"); rt->has_error = 1; return val_nil(); }
if (a[0].type == VAL_NUM) return a[0];
if (a[0].type == VAL_INT) return val_num((double)a[0].as.i);
if (a[0].type == VAL_STR) return val_num(strtod(a[0].as.str, NULL));
snprintf(rt->errbuf, sizeof(rt->errbuf), "num 无法转换该类型"); rt->has_error = 1; return val_nil();
}
static Value b_input(Runtime *rt, int argc, Value *a) {
if (argc > 0) { char *p = val_to_str(&rt->arena, a[0]); fputs(p, stdout); fflush(stdout); }
char buf[4096];
if (!fgets(buf, sizeof(buf), stdin)) return val_str(arena_strdup(&rt->arena, ""));
size_t n = strlen(buf);
while (n > 0 && (buf[n - 1] == '\n' || buf[n - 1] == '\r')) buf[--n] = '\0';
return val_str(arena_strdup(&rt->arena, buf));
}
static Value b_import(Runtime *rt, int argc, Value *a) {
if (argc < 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "import 需要库路径字符串"); rt->has_error = 1; return val_nil(); }
if (!rt->ffi_enabled) {
snprintf(rt->errbuf, sizeof(rt->errbuf), "FFI 已禁用 (运维未配置 cvmruntime.ffi)");
rt->has_error = 1;
return val_nil();
}
if (rt->ffi_allow.count > 0 && !map_get(&rt->ffi_allow, a[0].as.str)) {
char norm[300];
#ifdef _WIN32
snprintf(norm, sizeof(norm), "%s.dll", a[0].as.str);
#else
snprintf(norm, sizeof(norm), "%s.so", a[0].as.str);
#endif
if (!map_get(&rt->ffi_allow, norm)) {
snprintf(rt->errbuf, sizeof(rt->errbuf),
"FFI: 库不在白名单: %s (运维需在 cvmruntime.ffi 中授权)", a[0].as.str);
rt->has_error = 1;
return val_nil();
}
}
if (ffi_load(rt, a[0].as.str) != 0) return val_nil();
return val_nil();
}
static Value b_ffi_enable(Runtime *rt, int argc, Value *a) {
(void)argc; (void)a;
snprintf(rt->errbuf, sizeof(rt->errbuf),
"FFI 必须由运维通过 cvmruntime.ffi 配置授权,脚本不可自启");
rt->has_error = 1;
return val_nil();
}
static Value b_ffi_decl(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) {
snprintf(rt->errbuf, sizeof(rt->errbuf), "ffi_decl(name, sig) 需要两个字符串参数");
rt->has_error = 1;
return val_nil();
}
if (!rt->ffi_enabled) {
snprintf(rt->errbuf, sizeof(rt->errbuf),
"FFI 未启用 (运维未配置 cvmruntime.ffi)");
rt->has_error = 1;
return val_nil();
}
if (rt->ffi_allow_sym.count > 0 && !map_get(&rt->ffi_allow_sym, a[0].as.str)) {
snprintf(rt->errbuf, sizeof(rt->errbuf),
"FFI 符号未授权 (不在 cvmruntime.ffi 白名单): %s", a[0].as.str);
rt->has_error = 1;
return val_nil();
}
map_set(&rt->ffi_sigs, a[0].as.str, (void *)a[1].as.str);
return val_nil();
}
static Value b_str_upper(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_upper 需要字符串"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str; size_t slen = strlen(s);
char *o = (char *)arena_alloc(&rt->arena, slen * 4 + 1);
size_t w = 0; const char *p = s;
while (*p) {
int clen = utf8_seq_len((unsigned char)*p);
int actual = 1;
for (int k = 1; k < clen; k++) {
if (p[k] == '\0' || ((unsigned char)p[k] & 0xC0) != 0x80) break;
actual = k + 1;
}
unsigned char *u = (unsigned char *)p;
int64_t cp;
switch (actual) {
case 1: cp = u[0]; break;
case 2: cp = ((u[0] & 0x1F) << 6) | (u[1] & 0x3F); break;
case 3: cp = ((u[0] & 0x0F) << 12) | ((u[1] & 0x3F) << 6) | (u[2] & 0x3F); break;
case 4: cp = ((u[0] & 0x07) << 18) | ((u[1] & 0x3F) << 12) | ((u[2] & 0x3F) << 6) | (u[3] & 0x3F); break;
default: cp = u[0]; actual = 1; break;
}
int64_t folded = unicode_casefold(cp, 1);
char enc[4]; int enclen = utf8_encode(folded, enc);
memcpy(o + w, enc, (size_t)enclen); w += (size_t)enclen;
p += actual;
}
o[w] = '\0';
return val_str(o);
}
static Value b_str_lower(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_lower 需要字符串"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str; size_t slen = strlen(s);
char *o = (char *)arena_alloc(&rt->arena, slen * 4 + 1);
size_t w = 0; const char *p = s;
while (*p) {
int clen = utf8_seq_len((unsigned char)*p);
int actual = 1;
for (int k = 1; k < clen; k++) {
if (p[k] == '\0' || ((unsigned char)p[k] & 0xC0) != 0x80) break;
actual = k + 1;
}
unsigned char *u = (unsigned char *)p;
int64_t cp;
switch (actual) {
case 1: cp = u[0]; break;
case 2: cp = ((u[0] & 0x1F) << 6) | (u[1] & 0x3F); break;
case 3: cp = ((u[0] & 0x0F) << 12) | ((u[1] & 0x3F) << 6) | (u[2] & 0x3F); break;
case 4: cp = ((u[0] & 0x07) << 18) | ((u[1] & 0x3F) << 12) | ((u[2] & 0x3F) << 6) | (u[3] & 0x3F); break;
default: cp = u[0]; actual = 1; break;
}
int64_t folded = unicode_casefold(cp, 0);
char enc[4]; int enclen = utf8_encode(folded, enc);
memcpy(o + w, enc, (size_t)enclen); w += (size_t)enclen;
p += actual;
}
o[w] = '\0';
return val_str(o);
}
static Value b_str_substr(Runtime *rt, int argc, Value *a) {
if (argc != 3 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_substr(s,start,len) 用法错误"); rt->has_error = 1; return val_nil(); }
char *s = a[0].as.str; long n = (long)strlen(s);
long start = (long)(a[1].type == VAL_INT ? (double)a[1].as.i : a[1].as.num);
long len = (long)(a[2].type == VAL_INT ? (double)a[2].as.i : a[2].as.num);
if (start < 0) start = 0;
if (start > n) start = n;
if (start + len > n) len = n - start;
if (len < 0) len = 0;
char *o = (char *)arena_alloc(&rt->arena, (size_t)len + 1);
memcpy(o, s + start, (size_t)len); o[len] = '\0';
return val_str(o);
}
static Value b_str_contains(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_contains(s,sub) 用法错误"); rt->has_error = 1; return val_nil(); }
return val_bool(strstr(a[0].as.str, a[1].as.str) != NULL);
}
static long str_idx_arg(Runtime *rt, Value v, const char *fn) {
if (v.type == VAL_INT) return (long)v.as.i;
if (v.type == VAL_NUM) return (long)v.as.num;
snprintf(rt->errbuf, sizeof(rt->errbuf), "函数 '%s' 需要整数下标", fn);
rt->has_error = 1;
return -1;
}
static Value b_str_index(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_index(s,sub) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *m = strstr(a[0].as.str, a[1].as.str);
if (!m) return val_num(-1);
return val_num((double)(m - a[0].as.str));
}
static Value b_str_rindex(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_rindex(s,sub) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str, *sub = a[1].as.str;
if (sub[0] == '\0') return val_num((double)strlen(s));
const char *last = NULL, *p = s;
while (*p) {
const char *m = strstr(p, sub);
if (!m) break;
last = m; p = m + 1;
}
if (!last) return val_num(-1);
return val_num((double)(last - s));
}
static Value b_str_replace(Runtime *rt, int argc, Value *a) {
if (argc != 3 || a[0].type != VAL_STR || a[1].type != VAL_STR || a[2].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_replace(s,old,new) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str, *old = a[1].as.str, *nw = a[2].as.str;
size_t oldlen = strlen(old);
if (oldlen == 0) return val_str(arena_strdup(&rt->arena, s));
size_t nwlen = strlen(nw);
long count = 0; const char *p = s;
while (*p) { const char *m = strstr(p, old); if (!m) break; count++; p = m + oldlen; }
size_t slen = strlen(s);
size_t total = slen + (size_t)count * (nwlen > oldlen ? (nwlen - oldlen) : 0);
char *o = (char *)arena_alloc(&rt->arena, total + 1);
char *w = o; p = s;
while (*p) {
const char *m = strstr(p, old);
if (!m) { size_t rest = strlen(p); memcpy(w, p, rest); w += rest; break; }
size_t pre = (size_t)(m - p);
memcpy(w, p, pre); w += pre;
memcpy(w, nw, nwlen); w += nwlen;
p = m + oldlen;
}
*w = '\0';
return val_str(o);
}
static Value b_str_split(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_split(s,sep) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str, *sep = a[1].as.str;
size_t seplen = strlen(sep);
if (seplen == 0) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_split: 分隔符不能为空"); rt->has_error = 1; return val_nil(); }
Arr *arr = (Arr *)arena_alloc(&rt->arena, sizeof(Arr));
arr_init(&rt->arena, arr);
if (s[0] == '\0') { arr_append(&rt->arena, arr, val_str(arena_strdup(&rt->arena, ""))); return val_array(arr); }
while (1) {
const char *next = strstr(s, sep);
if (!next) { arr_append(&rt->arena, arr, val_str(arena_strdup(&rt->arena, s))); break; }
size_t fieldlen = (size_t)(next - s);
char *field = (char *)arena_alloc(&rt->arena, fieldlen + 1);
memcpy(field, s, fieldlen); field[fieldlen] = '\0';
arr_append(&rt->arena, arr, val_str(field));
s = next + seplen;
}
return val_array(arr);
}
static Value b_str_join(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_ARRAY || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_join(arr,sep) 用法错误"); rt->has_error = 1; return val_nil(); }
Arr *arr = (Arr *)a[0].as.arr;
const char *sep = a[1].as.str;
size_t seplen = strlen(sep);
size_t total = 0; int n = arr_len(arr);
for (int i = 0; i < n; ++i) {
ArrEntry *e = arr_at(arr, i);
char *ss = e ? val_to_str(&rt->arena, e->value) : (char *)"";
total += strlen(ss);
if (i) total += seplen;
}
char *buf = (char *)arena_alloc(&rt->arena, total + 1);
char *w = buf;
for (int i = 0; i < n; ++i) {
ArrEntry *e = arr_at(arr, i);
char *ss = e ? val_to_str(&rt->arena, e->value) : (char *)"";
if (i) { memcpy(w, sep, seplen); w += seplen; }
size_t l = strlen(ss); memcpy(w, ss, l); w += l;
}
*w = '\0';
return val_str(buf);
}
static Value b_str_trim(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_trim(s) 需要字符串"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str;
while (*s && isspace((unsigned char)*s)) s++;
const char *end = s + strlen(s);
while (end > s && isspace((unsigned char)end[-1])) end--;
size_t n = (size_t)(end - s);
char *o = (char *)arena_alloc(&rt->arena, n + 1);
memcpy(o, s, n); o[n] = '\0';
return val_str(o);
}
static Value b_str_ltrim(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_ltrim(s) 需要字符串"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str;
while (*s && isspace((unsigned char)*s)) s++;
return val_str(arena_strdup(&rt->arena, s));
}
static Value b_str_rtrim(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_rtrim(s) 需要字符串"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str;
const char *end = s + strlen(s);
while (end > s && isspace((unsigned char)end[-1])) end--;
size_t n = (size_t)(end - s);
char *o = (char *)arena_alloc(&rt->arena, n + 1);
memcpy(o, s, n); o[n] = '\0';
return val_str(o);
}
static Value b_str_starts(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_starts(s,prefix) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str, *pre = a[1].as.str;
size_t plen = strlen(pre);
if (plen == 0) return val_bool(1);
return val_bool(strlen(s) >= plen && strncmp(s, pre, plen) == 0);
}
static Value b_str_ends(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_ends(s,suffix) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str, *suf = a[1].as.str;
size_t slen = strlen(s), suf_len = strlen(suf);
if (suf_len == 0) return val_bool(1);
if (suf_len > slen) return val_bool(0);
return val_bool(strcmp(s + slen - suf_len, suf) == 0);
}
static Value b_str_reverse(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_reverse(s) 需要字符串"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str;
size_t n = strlen(s);
char *o = (char *)arena_alloc(&rt->arena, n + 1);
for (size_t i = 0; i < n; ++i) o[i] = s[n - 1 - i];
o[n] = '\0';
return val_str(o);
}
static Value b_str_repeat(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_repeat(s,n) 用法错误"); rt->has_error = 1; return val_nil(); }
double dn;
if (a[1].type == VAL_INT) dn = (double)a[1].as.i;
else if (a[1].type == VAL_NUM) dn = a[1].as.num;
else { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_repeat 需要整数次数"); rt->has_error = 1; return val_nil(); }
if (dn < 0) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_repeat 次数不能为负"); rt->has_error = 1; return val_nil(); }
long n = (long)dn;
const char *s = a[0].as.str;
size_t slen = strlen(s);
size_t total = slen * (size_t)n;
char *o = (char *)arena_alloc(&rt->arena, total + 1);
char *w = o;
for (long i = 0; i < n; ++i) { memcpy(w, s, slen); w += slen; }
*w = '\0';
return val_str(o);
}
static Value b_str_count(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_count(s,sub) 用法错误"); rt->has_error = 1; return val_nil(); }
const char *s = a[0].as.str, *sub = a[1].as.str;
size_t sublen = strlen(sub);
if (sublen == 0) return val_num(0);
long count = 0; const char *p = s;
while (*p) { const char *m = strstr(p, sub); if (!m) break; count++; p = m + sublen; }
return val_num((double)count);
}
static Value b_str_len(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_len(s) 需要字符串"); rt->has_error = 1; return val_nil(); }
return val_num((double)utf8_count_codepoints(a[0].as.str));
}
static Value b_str_char_at(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_char_at(s,i) 用法错误"); rt->has_error = 1; return val_nil(); }
long idx = str_idx_arg(rt, a[1], "str_char_at");
if (rt->has_error) return val_nil();
int clen;
const char *p = utf8_char_at_ptr(a[0].as.str, idx, &clen);
if (!p) { snprintf(rt->errbuf, sizeof(rt->errbuf), "字符串索引越界"); rt->has_error = 1; return val_nil(); }
char *o = (char *)arena_alloc(&rt->arena, (size_t)clen + 1);
memcpy(o, p, (size_t)clen); o[clen] = '\0';
return val_str(o);
}
static Value b_str_ord(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "str_ord(s,i) 用法错误"); rt->has_error = 1; return val_nil(); }
long idx = str_idx_arg(rt, a[1], "str_ord");
if (rt->has_error) return val_nil();
int64_t cp = utf8_codepoint_at(a[0].as.str, idx);
if (cp < 0) { snprintf(rt->errbuf, sizeof(rt->errbuf), "字符串索引越界"); rt->has_error = 1; return val_nil(); }
return val_int(cp);
}
static Value b_file_read(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "file_read 需要路径字符串"); rt->has_error = 1; return val_nil(); }
FILE *f = fopen(a[0].as.str, "rb");
if (!f) return val_nil();
fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET);
char *buf = (char *)arena_alloc(&rt->arena, (size_t)sz + 1);
size_t rd = fread(buf, 1, (size_t)sz, f); buf[rd] = '\0';
fclose(f);
return val_str(buf);
}
static Value b_file_write(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "file_write(path,content) 用法错误"); rt->has_error = 1; return val_nil(); }
FILE *f = fopen(a[0].as.str, "wb");
if (!f) { snprintf(rt->errbuf, sizeof(rt->errbuf), "file_write: 无法打开 '%s'", a[0].as.str); rt->has_error = 1; return val_nil(); }
fwrite(a[1].as.str, 1, strlen(a[1].as.str), f);
fclose(f);
return val_nil();
}
static Value b_file_exists(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "file_exists 需要路径字符串"); rt->has_error = 1; return val_nil(); }
FILE *f = fopen(a[0].as.str, "rb");
if (!f) return val_bool(0);
fclose(f);
return val_bool(1);
}
#ifdef _WIN32
# include <windows.h>
static void host_sleep_ms(int ms) { Sleep((DWORD)ms); }
#else
# include <unistd.h>
# include <time.h>
static void host_sleep_ms(int ms) { struct timespec ts = {ms/1000, (ms%1000)*1000000L}; nanosleep(&ts, NULL); }
#endif
static Value b_delay(Runtime *rt, int argc, Value *a) {
if (argc != 1) { snprintf(rt->errbuf, sizeof(rt->errbuf), "delay(ms) 需要一个参数"); rt->has_error = 1; return val_nil(); }
int ms = (int)(a[0].type == VAL_INT ? (double)a[0].as.i : a[0].as.num);
if (ms < 0) ms = 0;
host_sleep_ms(ms);
return val_nil();
}
static Value b_set_timeout(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "set_timeout(name, ms) 用法错误"); rt->has_error = 1; return val_nil(); }
int ms = (int)(a[1].type == VAL_INT ? (double)a[1].as.i : a[1].as.num);
if (ms < 0) ms = 0;
rt_timer_add(rt, a[0].as.str, ms, 0);
return val_nil();
}
static Value b_set_interval(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "set_interval(name, ms) 用法错误"); rt->has_error = 1; return val_nil(); }
int ms = (int)(a[1].type == VAL_INT ? (double)a[1].as.i : a[1].as.num);
if (ms <= 0) { snprintf(rt->errbuf, sizeof(rt->errbuf), "set_interval 间隔必须 > 0"); rt->has_error = 1; return val_nil(); }
rt_timer_add(rt, a[0].as.str, ms, ms);
return val_nil();
}
static Value b_event_loop(Runtime *rt, int argc, Value *a) {
(void)argc; (void)a;
while (1) {
int active = 0, soonest = 999999;
if (!rt->timer_epoch) { host_sleep_ms(100); continue; }
int64_t now = rt_now_ms(rt) - rt->timer_epoch;
for (int i = 0; i < rt->timer_count; ++i) {
if (!rt->timers[i].active) continue;
active = 1;
int64_t wait = rt->timers[i].deadline_ms - now;
if (wait < 0) wait = 0;
if (wait < soonest) soonest = (wait > 2147483647LL) ? 2147483647 : (int)wait;
if (rt->timers[i].deadline_ms <= now) {
char call_str[320];
snprintf(call_str, sizeof(call_str), "%s();", rt->timers[i].name);
rt_run(rt, call_str, "timer");
rt->has_error = 0;
if (rt->timers[i].repeat_ms > 0)
rt->timers[i].deadline_ms = now + rt->timers[i].repeat_ms;
else
rt->timers[i].active = 0;
now = rt_now_ms(rt) - rt->timer_epoch;
goto restart_loop;
}
}
if (!active) break;
if (soonest > 0) host_sleep_ms(soonest);
restart_loop:;
}
return val_nil();
}
static Value b_gc_collect(Runtime *rt, int argc, Value *a) {
(void)argc; (void)a;
gc_collect(rt);
return val_nil();
}
static Value b_math_sin(Runtime *rt, int argc, Value *a) {
if(argc!=1){rt->has_error=1;return val_nil();}
double v=a[0].type==VAL_INT?(double)a[0].as.i:a[0].as.num;
return val_num(sin(v));
}
static Value b_math_cos(Runtime *rt, int argc, Value *a) {
if(argc!=1){rt->has_error=1;return val_nil();}
double v=a[0].type==VAL_INT?(double)a[0].as.i:a[0].as.num;
return val_num(cos(v));
}
static Value b_math_tan(Runtime *rt, int argc, Value *a) {
if(argc!=1){rt->has_error=1;return val_nil();}
double v=a[0].type==VAL_INT?(double)a[0].as.i:a[0].as.num;
return val_num(tan(v));
}
static Value b_math_log(Runtime *rt, int argc, Value *a) {
if(argc!=1){rt->has_error=1;return val_nil();}
double v=a[0].type==VAL_INT?(double)a[0].as.i:a[0].as.num;
return val_num(log(v));
}
static Value b_math_exp(Runtime *rt, int argc, Value *a) {
if(argc!=1){rt->has_error=1;return val_nil();}
double v=a[0].type==VAL_INT?(double)a[0].as.i:a[0].as.num;
return val_num(exp(v));
}
static Value b_math_pi(Runtime *rt, int argc, Value *a) {
(void)argc;(void)a;
return val_num(3.14159265358979323846);
}
static int hex_val(char c){if(c>='0'&&c<='9')return c-'0';if(c>='A'&&c<='F')return c-'A'+10;if(c>='a'&&c<='f')return c-'a'+10;return 0;}
static Value b_url_encode(Runtime *rt, int argc, Value *a) {
if(argc!=1||a[0].type!=VAL_STR){rt->has_error=1;return val_nil();}
const char *s=a[0].as.str; size_t n=strlen(s);
char *o=(char*)arena_alloc(&rt->arena,n*3+1); size_t w=0;
for(size_t i=0;i<n;i++){
unsigned char c=(unsigned char)s[i];
if((c>='A'&&c<='Z')||(c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='-'||c=='_'||c=='.'||c=='~'){o[w++]=c;}
else{w+=sprintf(o+w,"%%%02X",c);}
}
o[w]='\0'; return val_str(o);
}
static Value b_url_decode(Runtime *rt, int argc, Value *a) {
if(argc!=1||a[0].type!=VAL_STR){rt->has_error=1;return val_nil();}
const char *s=a[0].as.str; size_t n=strlen(s);
char *o=(char*)arena_alloc(&rt->arena,n+1); size_t w=0;
for(size_t i=0;i<n;i++){
if(s[i]=='%'&&i+2<n){o[w++]=(char)(hex_val(s[i+1])*16+hex_val(s[i+2]));i+=2;}
else if(s[i]=='+'){o[w++]=' ';}
else o[w++]=s[i];
}
o[w]='\0'; return val_str(o);
}
static Value b_html_escape(Runtime *rt, int argc, Value *a) {
if(argc!=1||a[0].type!=VAL_STR){rt->has_error=1;return val_nil();}
const char *s=a[0].as.str; size_t n=strlen(s);
char *o=(char*)arena_alloc(&rt->arena,n*6+1); size_t w=0;
for(size_t i=0;i<n;i++){
switch(s[i]){
case '&': memcpy(o+w,"&",5);w+=5;break;
case '<': memcpy(o+w,"<",4);w+=4;break;
case '>': memcpy(o+w,">",4);w+=4;break;
case '"': memcpy(o+w,""",6);w+=6;break;
default: o[w++]=s[i];
}
}
o[w]='\0'; return val_str(o);
}
static Value b_str_format(Runtime *rt, int argc, Value *a) {
if(argc<1||a[0].type!=VAL_STR){rt->has_error=1;return val_nil();}
const char *tmpl=a[0].as.str; size_t tlen=strlen(tmpl);
char *o=(char*)arena_alloc(&rt->arena,4096); size_t w=0;
size_t i=0;
while(i<tlen){
if(tmpl[i]=='{'&&i+2<tlen&&tmpl[i+1]>='0'&&tmpl[i+1]<='9'){
size_t j=i+1; while(j<tlen&&tmpl[j]!='}')j++;
if(j<tlen){
int idx=tmpl[i+1]-'0';
if(idx<argc-1){
char *vs=val_to_str(&rt->arena,a[idx+1]);
size_t vl=strlen(vs); memcpy(o+w,vs,vl); w+=vl;
}
i=j+1; continue;
}
}
o[w++]=tmpl[i++];
}
o[w]='\0'; return val_str(o);
}
static Value b_os_getenv(Runtime *rt, int argc, Value *a) {
if(argc!=1||a[0].type!=VAL_STR){rt->has_error=1;return val_nil();}
const char *v=getenv(a[0].as.str);
return v?val_str(arena_strdup(&rt->arena,v)):val_nil();
}
static Value b_file_listdir(Runtime *rt, int argc, Value *a) {
if(argc!=1||a[0].type!=VAL_STR){rt->has_error=1;return val_nil();}
Arr *arr=(Arr*)arena_alloc(&rt->arena,sizeof(Arr)); arr_init(&rt->arena,arr);
#ifdef _WIN32
char pat[512]; snprintf(pat,sizeof(pat),"%s\\*",a[0].as.str);
WIN32_FIND_DATAA fd; HANDLE h=FindFirstFileA(pat,&fd);
if(h!=INVALID_HANDLE_VALUE){
do{arr_append(&rt->arena,arr,val_str(arena_strdup(&rt->arena,fd.cFileName)));}while(FindNextFileA(h,&fd));
FindClose(h);
}
#endif
return val_array(arr);
}
static ProtoState *arg_proto(Runtime *rt, Value v) {
if (v.type != VAL_PTR || !v.as.ptr) {
snprintf(rt->errbuf, sizeof(rt->errbuf), "协议状态机操作需要状态机句柄");
rt->has_error = 1;
return NULL;
}
return (ProtoState *)v.as.ptr;
}
static Value b_proto_new(Runtime *rt, int argc, Value *a) {
if (argc < 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_new(name) 需要字符串名称"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = proto_new(a[0].as.str);
if (!ps) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_new: 创建状态机失败"); rt->has_error = 1; return val_nil(); }
return val_ptr_obj(ps);
}
static Value b_proto_state(Runtime *rt, int argc, Value *a) {
if (argc < 2) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_state(sm, id, label?, level?) 参数不足"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = arg_proto(rt, a[0]); if (!ps) return val_nil();
int id = (int)(a[1].type == VAL_INT ? a[1].as.i : (int64_t)a[1].as.num);
const char *label = (argc >= 3 && a[2].type == VAL_STR) ? a[2].as.str : "";
int level = (argc >= 4) ? (int)(a[3].type == VAL_INT ? a[3].as.i : (int64_t)a[3].as.num) : 0;
if (proto_state_add(ps, id, label, level) != 0) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_state: 添加状态失败"); rt->has_error = 1; return val_nil(); }
return val_ptr_obj(ps);
}
static Value b_proto_transition(Runtime *rt, int argc, Value *a) {
if (argc < 4) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_transition(sm, from, event, to, action?) 参数不足"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = arg_proto(rt, a[0]); if (!ps) return val_nil();
int from = (int)(a[1].type == VAL_INT ? a[1].as.i : (int64_t)a[1].as.num);
if (a[2].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_transition: event 需要字符串"); rt->has_error = 1; return val_nil(); }
int to = (int)(a[3].type == VAL_INT ? a[3].as.i : (int64_t)a[3].as.num);
const char *action = (argc >= 5 && a[4].type == VAL_STR) ? a[4].as.str : "";
if (proto_transition_add(ps, from, a[2].as.str, to, action) != 0) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_transition: 添加转换失败"); rt->has_error = 1; return val_nil(); }
return val_ptr_obj(ps);
}
static Value b_proto_event(Runtime *rt, int argc, Value *a) {
if (argc != 2 || a[1].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_event(sm, event) 需要事件字符串"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = arg_proto(rt, a[0]); if (!ps) return val_nil();
int r = proto_event(ps, a[1].as.str);
return val_int(r);
}
static Value b_proto_current(Runtime *rt, int argc, Value *a) {
if (argc != 1) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_current(sm) 需要一个参数"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = arg_proto(rt, a[0]); if (!ps) return val_nil();
return val_str(arena_strdup(&rt->arena, proto_current_label(ps)));
}
static Value b_proto_current_id(Runtime *rt, int argc, Value *a) {
if (argc != 1) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_current_id(sm) 需要一个参数"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = arg_proto(rt, a[0]); if (!ps) return val_nil();
return val_int(proto_current_id(ps));
}
static Value b_proto_level(Runtime *rt, int argc, Value *a) {
if (argc != 1) { snprintf(rt->errbuf, sizeof(rt->errbuf), "proto_level(sm) 需要一个参数"); rt->has_error = 1; return val_nil(); }
ProtoState *ps = arg_proto(rt, a[0]); if (!ps) return val_nil();
return val_int(proto_current_level(ps));
}
static Value b_proto_level_name(Runtime *rt, int argc, Value *a) {
int level = (argc >= 1) ? (int)(a[0].type == VAL_INT ? a[0].as.i : (int64_t)a[0].as.num) : 0;
return val_str(arena_strdup(&rt->arena, proto_level_name(level)));
}
static Value b_json_stringify(Runtime *rt, int argc, Value *a) {
if(argc!=1){rt->has_error=1;return val_nil();}
Value v=a[0];
if(v.type==VAL_NIL) return val_str(arena_strdup(&rt->arena,"null"));
if(v.type==VAL_BOOL) return val_str(arena_strdup(&rt->arena,v.as.i?"true":"false"));
if(v.type==VAL_NUM) {char b[64];snprintf(b,sizeof(b),"%.12g",v.as.num);return val_str(arena_strdup(&rt->arena,b));}
if(v.type==VAL_INT) {char b[64];snprintf(b,sizeof(b),"%lld",(long long)v.as.i);return val_str(arena_strdup(&rt->arena,b));}
if(v.type==VAL_STR) {
const char *s=v.as.str; size_t n=strlen(s);
char *o=(char*)arena_alloc(&rt->arena,n*2+3); size_t w=0; o[w++]='"';
for(size_t i=0;i<n;i++){
char c=s[i];
if(c=='"'||c=='\\'){o[w++]='\\';o[w++]=c;}
else if(c=='\n'){o[w++]='\\';o[w++]='n';}
else if(c=='\r'){o[w++]='\\';o[w++]='r';}
else if(c=='\t'){o[w++]='\\';o[w++]='t';}
else o[w++]=c;
}
o[w++]='"'; o[w]='\0'; return val_str(o);
}
if(v.type==VAL_ARRAY){
Arr *arr=(Arr*)v.as.arr; int n=arr_len(arr);
char *o=(char*)arena_alloc(&rt->arena,65536); size_t w=0; o[w++]='[';
for(int i=0;i<n;i++){ArrEntry *e=arr_at(arr,i);if(i>0){o[w++]=',';}
Value sv=b_json_stringify(rt,1,&e->value);
size_t sl=strlen(sv.as.str); memcpy(o+w,sv.as.str,sl); w+=sl;
}
o[w++]=']'; o[w]='\0'; return val_str(o);
}
return val_str(arena_strdup(&rt->arena,"null"));
}
static Value b_require(Runtime *rt, int argc, Value *a) {
if (argc != 1 || a[0].type != VAL_STR) { snprintf(rt->errbuf, sizeof(rt->errbuf), "require 需要字符串模块名"); rt->has_error = 1; return val_nil(); }
Map *exp = load_module(rt, a[0].as.str);
if (!exp) return val_nil();
return val_module((void *)exp);
}
void host_register_all(Runtime *rt) {
srand((unsigned)time(NULL));
rt_register_builtin(rt, "print", b_print);
rt_register_builtin(rt, "println", b_println);
rt_register_builtin(rt, "sqrt", b_sqrt);
rt_register_builtin(rt, "pow", b_pow);
rt_register_builtin(rt, "abs", b_abs);
rt_register_builtin(rt, "floor", b_floor);
rt_register_builtin(rt, "ceil", b_ceil);
rt_register_builtin(rt, "round", b_round);
rt_register_builtin(rt, "rand", b_rand);
rt_register_builtin(rt, "time", b_time);
rt_register_builtin(rt, "clock_ms", b_clock_ms);
rt_register_builtin(rt, "len", b_len);
rt_register_builtin(rt, "type", b_type);
rt_register_builtin(rt, "str", b_str);
rt_register_builtin(rt, "num", b_num);
rt_register_builtin(rt, "input", b_input);
rt_register_builtin(rt, "import", b_import);
rt_register_builtin(rt, "ffi_enable", b_ffi_enable);
rt_register_builtin(rt, "ffi_decl", b_ffi_decl);
rt_register_builtin(rt, "require", b_require);
rt_register_builtin(rt, "push", b_push);
rt_register_builtin(rt, "arr_get", b_arr_get);
rt_register_builtin(rt, "arr_set", b_arr_set);
rt_register_builtin(rt, "arr_has", b_arr_has);
rt_register_builtin(rt, "arr_del", b_arr_del);
rt_register_builtin(rt, "arr_keys", b_arr_keys);
rt_register_builtin(rt, "arr_values", b_arr_values);
rt_register_builtin(rt, "arr_merge", b_arr_merge);
rt_register_builtin(rt, "str_upper", b_str_upper);
rt_register_builtin(rt, "str_lower", b_str_lower);
rt_register_builtin(rt, "str_substr", b_str_substr);
rt_register_builtin(rt, "str_contains",b_str_contains);
rt_register_builtin(rt, "str_index", b_str_index);
rt_register_builtin(rt, "str_rindex", b_str_rindex);
rt_register_builtin(rt, "str_replace", b_str_replace);
rt_register_builtin(rt, "str_split", b_str_split);
rt_register_builtin(rt, "str_join", b_str_join);
rt_register_builtin(rt, "str_trim", b_str_trim);
rt_register_builtin(rt, "str_ltrim", b_str_ltrim);
rt_register_builtin(rt, "str_rtrim", b_str_rtrim);
rt_register_builtin(rt, "str_starts", b_str_starts);
rt_register_builtin(rt, "str_ends", b_str_ends);
rt_register_builtin(rt, "str_reverse", b_str_reverse);
rt_register_builtin(rt, "str_repeat", b_str_repeat);
rt_register_builtin(rt, "str_count", b_str_count);
rt_register_builtin(rt, "str_len", b_str_len);
rt_register_builtin(rt, "str_char_at", b_str_char_at);
rt_register_builtin(rt, "str_ord", b_str_ord);
rt_register_builtin(rt, "file_read", b_file_read);
rt_register_builtin(rt, "file_write", b_file_write);
rt_register_builtin(rt, "file_exists", b_file_exists);
rt_register_builtin(rt, "delay", b_delay);
rt_register_builtin(rt, "set_timeout", b_set_timeout);
rt_register_builtin(rt, "set_interval", b_set_interval);
rt_register_builtin(rt, "event_loop", b_event_loop);
rt_register_builtin(rt, "gc_collect", b_gc_collect);
rt_register_builtin(rt, "math_sin", b_math_sin);
rt_register_builtin(rt, "math_cos", b_math_cos);
rt_register_builtin(rt, "math_tan", b_math_tan);
rt_register_builtin(rt, "math_log", b_math_log);
rt_register_builtin(rt, "math_exp", b_math_exp);
rt_register_builtin(rt, "math_pi", b_math_pi);
rt_register_builtin(rt, "url_encode", b_url_encode);
rt_register_builtin(rt, "url_decode", b_url_decode);
rt_register_builtin(rt, "html_escape", b_html_escape);
rt_register_builtin(rt, "str_format", b_str_format);
rt_register_builtin(rt, "os_getenv", b_os_getenv);
rt_register_builtin(rt, "file_listdir", b_file_listdir);
rt_register_builtin(rt, "json_stringify", b_json_stringify);
rt_register_builtin(rt, "proto_new", b_proto_new);
rt_register_builtin(rt, "proto_state", b_proto_state);
rt_register_builtin(rt, "proto_transition", b_proto_transition);
rt_register_builtin(rt, "proto_event", b_proto_event);
rt_register_builtin(rt, "proto_current", b_proto_current);
rt_register_builtin(rt, "proto_current_id", b_proto_current_id);
rt_register_builtin(rt, "proto_level", b_proto_level);
rt_register_builtin(rt, "proto_level_name", b_proto_level_name);
}