diff --git a/Makefile b/Makefile index 73ba82e..4f1f9b8 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ LIBCS_LDFLAGS = -shared LIBCS_OBJ = \ cubescript.o \ + cs_vm.o \ lib_str.o \ lib_math.o \ lib_list.o @@ -27,7 +28,8 @@ $(LIBCS_LIB): $(LIBCS_OBJ) clean: rm -f $(LIBCS_LIB) $(LIBCS_OBJ) -cubescript.o: cubescript.hh +cubescript.o: cubescript.hh cs_vm.hh +cs_vm.o: cubescript.hh cs_vm.hh lib_str.o: cubescript.hh lib_math.o: cubescript.hh lib_list.o: cubescript.hh diff --git a/cs_vm.cc b/cs_vm.cc new file mode 100644 index 0000000..7cfba07 --- /dev/null +++ b/cs_vm.cc @@ -0,0 +1,1104 @@ +#include "cubescript.hh" +#include "cs_vm.hh" + +#include + +namespace cscript { + +ostd::Uint32 const *forcecode(CsState &cs, TaggedValue &v); +void forcecond(CsState &cs, TaggedValue &v); + +static ostd::Uint32 emptyblock[VAL_ANY][2] = { + { CODE_START + 0x100, CODE_EXIT | RET_NULL }, + { CODE_START + 0x100, CODE_EXIT | RET_INT }, + { CODE_START + 0x100, CODE_EXIT | RET_FLOAT }, + { CODE_START + 0x100, CODE_EXIT | RET_STR } +}; + +static TaggedValue no_ret = null_value; + +static inline void force_arg(TaggedValue &v, int type) { + switch (type) { + case RET_STR: + if (v.get_type() != VAL_STR) v.force_str(); + break; + case RET_INT: + if (v.get_type() != VAL_INT) v.force_int(); + break; + case RET_FLOAT: + if (v.get_type() != VAL_FLOAT) v.force_float(); + break; + } +} + +static inline void free_args(TaggedValue *args, int &oldnum, int newnum) { + for (int i = newnum; i < oldnum; i++) args[i].cleanup(); + oldnum = newnum; +} + +static ostd::Uint32 const *skipcode(ostd::Uint32 const *code, TaggedValue *result = nullptr) { + int depth = 0; + for (;;) { + ostd::Uint32 op = *code++; + switch (op & 0xFF) { + case CODE_MACRO: + case CODE_VAL|RET_STR: { + ostd::Uint32 len = op >> 8; + code += len / sizeof(ostd::Uint32) + 1; + continue; + } + case CODE_BLOCK: + case CODE_JUMP: + case CODE_JUMP_TRUE: + case CODE_JUMP_FALSE: + case CODE_JUMP_RESULT_TRUE: + case CODE_JUMP_RESULT_FALSE: { + ostd::Uint32 len = op >> 8; + code += len; + continue; + } + case CODE_ENTER: + case CODE_ENTER_RESULT: + ++depth; + continue; + case CODE_EXIT|RET_NULL: + case CODE_EXIT|RET_STR: + case CODE_EXIT|RET_INT: + case CODE_EXIT|RET_FLOAT: + if (depth <= 0) { + if (result) { + force_arg(*result, op & CODE_RET_MASK); + } + return code; + } + --depth; + continue; + } + } +} + +void TaggedValue::copy_arg(TaggedValue &r) const { + r.cleanup(); + switch (get_type()) { + case VAL_INT: + case VAL_FLOAT: + case VAL_IDENT: + r = *this; + break; + case VAL_STR: + case VAL_CSTR: + case VAL_MACRO: + r.set_str(ostd::ConstCharRange(s, len)); + break; + case VAL_CODE: { + ostd::Uint32 const *bcode = reinterpret_cast(code); + ostd::Uint32 const *end = skipcode(bcode); + ostd::Uint32 *dst = new ostd::Uint32[end - bcode + 1]; + *dst++ = CODE_START; + memcpy(dst, bcode, (end - bcode) * sizeof(ostd::Uint32)); + r.set_code(reinterpret_cast(dst)); + break; + } + default: + r.set_null(); + break; + } +} + +static inline void callcommand(CsState &cs, Ident *id, TaggedValue *args, TaggedValue &res, int numargs, bool lookup = false) { + int i = -1, fakeargs = 0; + bool rep = false; + for (char const *fmt = id->cargs; *fmt; fmt++) switch (*fmt) { + case 'i': + if (++i >= numargs) { + if (rep) break; + args[i].set_int(0); + fakeargs++; + } else args[i].force_int(); + break; + case 'b': + if (++i >= numargs) { + if (rep) break; + args[i].set_int(INT_MIN); + fakeargs++; + } else args[i].force_int(); + break; + case 'f': + if (++i >= numargs) { + if (rep) break; + args[i].set_float(0.0f); + fakeargs++; + } else args[i].force_float(); + break; + case 'F': + if (++i >= numargs) { + if (rep) break; + args[i].set_float(args[i - 1].get_float()); + fakeargs++; + } else args[i].force_float(); + break; + case 'S': + if (++i >= numargs) { + if (rep) break; + args[i].set_str(""); + fakeargs++; + } else args[i].force_str(); + break; + case 's': + if (++i >= numargs) { + if (rep) break; + args[i].set_cstr(""); + fakeargs++; + } else args[i].force_str(); + break; + case 'T': + case 't': + if (++i >= numargs) { + if (rep) break; + args[i].set_null(); + fakeargs++; + } + break; + case 'E': + if (++i >= numargs) { + if (rep) break; + args[i].set_null(); + fakeargs++; + } else forcecond(cs, args[i]); + break; + case 'e': + if (++i >= numargs) { + if (rep) break; + args[i].set_code(reinterpret_cast(emptyblock[VAL_NULL] + 1)); + fakeargs++; + } else forcecode(cs, args[i]); + break; + case 'r': + if (++i >= numargs) { + if (rep) break; + args[i].set_ident(cs.dummy); + fakeargs++; + } else cs.force_ident(args[i]); + break; + case '$': + if (++i < numargs) args[i].cleanup(); + args[i].set_ident(id); + break; + case 'N': + if (++i < numargs) args[i].cleanup(); + args[i].set_int(lookup ? -1 : i - fakeargs); + break; + case 'C': { + i = ostd::max(i + 1, numargs); + auto buf = ostd::appender(); + cscript::util::tvals_concat(buf, ostd::iter(args, i), " "); + TaggedValue tv; + tv.set_mstr(buf.get().iter()); + id->cb_cftv(TvalRange(&tv, 1), res); + goto cleanup; + } + case 'V': + i = ostd::max(i + 1, numargs); + id->cb_cftv(ostd::iter(args, i), res); + goto cleanup; + case '1': + case '2': + case '3': + case '4': + if (i + 1 < numargs) { + fmt -= *fmt - '0' + 1; + rep = true; + } + break; + } + ++i; + id->cb_cftv(TvalRange(args, i), res); +cleanup: + for (ostd::Size k = 0; k < ostd::Size(i); ++k) args[k].cleanup(); + for (; i < numargs; i++) args[i].cleanup(); +} + +static constexpr int MaxRunDepth = 255; +static thread_local int rundepth = 0; + +static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, TaggedValue &result) { + result.set_null(); + if (rundepth >= MaxRunDepth) { + cs_debug_code(cs, "exceeded recursion limit"); + return skipcode(code, (&result == &no_ret) ? nullptr : &result); + } + ++rundepth; + int numargs = 0; + TaggedValue args[MaxArguments + MaxResults]; + for (;;) { + ostd::Uint32 op = *code++; + switch (op & 0xFF) { + case CODE_START: + case CODE_OFFSET: + continue; + +#define RETOP(op, val) \ + case op: \ + result.cleanup(); \ + val; \ + continue; + + RETOP(CODE_NULL | RET_NULL, result.set_null()) + RETOP(CODE_NULL | RET_STR, result.set_str("")) + RETOP(CODE_NULL | RET_INT, result.set_int(0)) + RETOP(CODE_NULL | RET_FLOAT, result.set_float(0.0f)) + + RETOP(CODE_FALSE | RET_STR, result.set_str("0")) + case CODE_FALSE|RET_NULL: + RETOP(CODE_FALSE | RET_INT, result.set_int(0)) + RETOP(CODE_FALSE | RET_FLOAT, result.set_float(0.0f)) + + RETOP(CODE_TRUE | RET_STR, result.set_str("1")) + case CODE_TRUE|RET_NULL: + RETOP(CODE_TRUE | RET_INT, result.set_int(1)) + RETOP(CODE_TRUE | RET_FLOAT, result.set_float(1.0f)) + +#define RETPOP(op, val) \ + RETOP(op, { --numargs; val; args[numargs].cleanup(); }) + + RETPOP(CODE_NOT | RET_STR, result.set_str(args[numargs].get_bool() ? "0" : "1")) + case CODE_NOT|RET_NULL: + RETPOP(CODE_NOT | RET_INT, result.set_int(args[numargs].get_bool() ? 0 : 1)) + RETPOP(CODE_NOT | RET_FLOAT, result.set_float(args[numargs].get_bool() ? 0.0f : 1.0f)) + + case CODE_POP: + args[--numargs].cleanup(); + continue; + case CODE_ENTER: + code = runcode(cs, code, args[numargs++]); + continue; + case CODE_ENTER_RESULT: + result.cleanup(); + code = runcode(cs, code, result); + continue; + case CODE_EXIT|RET_STR: + case CODE_EXIT|RET_INT: + case CODE_EXIT|RET_FLOAT: + force_arg(result, op & CODE_RET_MASK); + /* fallthrough */ + case CODE_EXIT|RET_NULL: + goto exit; + case CODE_RESULT_ARG|RET_STR: + case CODE_RESULT_ARG|RET_INT: + case CODE_RESULT_ARG|RET_FLOAT: + force_arg(result, op & CODE_RET_MASK); + /* fallthrough */ + case CODE_RESULT_ARG|RET_NULL: + args[numargs++] = result; + result.set_null(); + continue; + case CODE_PRINT: + cs.print_var(cs.identmap[op >> 8]); + continue; + + case CODE_LOCAL: { + result.cleanup(); + int numlocals = op >> 8, offset = numargs - numlocals; + IdentStack locals[MaxArguments]; + for (int i = 0; i < numlocals; ++i) args[offset + i].id->push_alias(locals[i]); + code = runcode(cs, code, result); + for (int i = offset; i < numargs; i++) args[i].id->pop_alias(); + goto exit; + } + + case CODE_DOARGS|RET_NULL: + case CODE_DOARGS|RET_STR: + case CODE_DOARGS|RET_INT: + case CODE_DOARGS|RET_FLOAT: + if (cs.stack != &cs.noalias) { + cs_do_args(cs, [&]() { + result.cleanup(); + cs.run_ret(args[--numargs].code, result); + args[numargs].cleanup(); + force_arg(result, op & CODE_RET_MASK); + }); + continue; + } + /* fallthrough */ + case CODE_DO|RET_NULL: + case CODE_DO|RET_STR: + case CODE_DO|RET_INT: + case CODE_DO|RET_FLOAT: + result.cleanup(); + cs.run_ret(args[--numargs].code, result); + args[numargs].cleanup(); + force_arg(result, op & CODE_RET_MASK); + continue; + + case CODE_JUMP: { + ostd::Uint32 len = op >> 8; + code += len; + continue; + } + case CODE_JUMP_TRUE: { + ostd::Uint32 len = op >> 8; + if (args[--numargs].get_bool()) code += len; + args[numargs].cleanup(); + continue; + } + case CODE_JUMP_FALSE: { + ostd::Uint32 len = op >> 8; + if (!args[--numargs].get_bool()) code += len; + args[numargs].cleanup(); + continue; + } + case CODE_JUMP_RESULT_TRUE: { + ostd::Uint32 len = op >> 8; + result.cleanup(); + --numargs; + if (args[numargs].get_type() == VAL_CODE) { + cs.run_ret(args[numargs].code, result); + args[numargs].cleanup(); + } else result = args[numargs]; + if (result.get_bool()) code += len; + continue; + } + case CODE_JUMP_RESULT_FALSE: { + ostd::Uint32 len = op >> 8; + result.cleanup(); + --numargs; + if (args[numargs].get_type() == VAL_CODE) { + cs.run_ret(args[numargs].code, result); + args[numargs].cleanup(); + } else result = args[numargs]; + if (!result.get_bool()) code += len; + continue; + } + + case CODE_MACRO: { + ostd::Uint32 len = op >> 8; + cs_set_macro(args[numargs++], reinterpret_cast(code), len); + code += len / sizeof(ostd::Uint32) + 1; + continue; + } + + case CODE_VAL|RET_STR: { + ostd::Uint32 len = op >> 8; + args[numargs++].set_str(ostd::ConstCharRange(reinterpret_cast(code), len)); + code += len / sizeof(ostd::Uint32) + 1; + continue; + } + case CODE_VALI|RET_STR: { + char s[4] = { char((op >> 8) & 0xFF), char((op >> 16) & 0xFF), char((op >> 24) & 0xFF), '\0' }; + args[numargs++].set_str(s); + continue; + } + case CODE_VAL|RET_NULL: + case CODE_VALI|RET_NULL: + args[numargs++].set_null(); + continue; + case CODE_VAL|RET_INT: + args[numargs++].set_int(int(*code++)); + continue; + case CODE_VALI|RET_INT: + args[numargs++].set_int(int(op) >> 8); + continue; + case CODE_VAL|RET_FLOAT: + args[numargs++].set_float(*reinterpret_cast(code++)); + continue; + case CODE_VALI|RET_FLOAT: + args[numargs++].set_float(float(int(op) >> 8)); + continue; + + case CODE_DUP|RET_NULL: + args[numargs - 1].get_val(args[numargs]); + numargs++; + continue; + case CODE_DUP|RET_INT: + args[numargs].set_int(args[numargs - 1].get_int()); + numargs++; + continue; + case CODE_DUP|RET_FLOAT: + args[numargs].set_float(args[numargs - 1].get_float()); + numargs++; + continue; + case CODE_DUP|RET_STR: + args[numargs].set_str(ostd::move(args[numargs - 1].get_str())); + numargs++; + continue; + + case CODE_FORCE|RET_STR: + args[numargs - 1].force_str(); + continue; + case CODE_FORCE|RET_INT: + args[numargs - 1].force_int(); + continue; + case CODE_FORCE|RET_FLOAT: + args[numargs - 1].force_float(); + continue; + + case CODE_RESULT|RET_NULL: + result.cleanup(); + result = args[--numargs]; + continue; + case CODE_RESULT|RET_STR: + case CODE_RESULT|RET_INT: + case CODE_RESULT|RET_FLOAT: + result.cleanup(); + result = args[--numargs]; + force_arg(result, op & CODE_RET_MASK); + continue; + + case CODE_EMPTY|RET_NULL: + args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_NULL] + 1)); + break; + case CODE_EMPTY|RET_STR: + args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_STR] + 1)); + break; + case CODE_EMPTY|RET_INT: + args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_INT] + 1)); + break; + case CODE_EMPTY|RET_FLOAT: + args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_FLOAT] + 1)); + break; + case CODE_BLOCK: { + ostd::Uint32 len = op >> 8; + args[numargs++].set_code(reinterpret_cast(code + 1)); + code += len; + continue; + } + case CODE_COMPILE: { + TaggedValue &arg = args[numargs - 1]; + GenState gs(cs); + switch (arg.get_type()) { + case VAL_INT: + gs.code.reserve(8); + gs.code.push(CODE_START); + gs.gen_int(arg.i); + gs.code.push(CODE_RESULT); + gs.code.push(CODE_EXIT); + break; + case VAL_FLOAT: + gs.code.reserve(8); + gs.code.push(CODE_START); + gs.gen_float(arg.f); + gs.code.push(CODE_RESULT); + gs.code.push(CODE_EXIT); + break; + case VAL_STR: + case VAL_MACRO: + case VAL_CSTR: + gs.code.reserve(64); + gs.gen_main(arg.s); + arg.cleanup(); + break; + default: + gs.code.reserve(8); + gs.code.push(CODE_START); + gs.gen_null(); + gs.code.push(CODE_RESULT); + gs.code.push(CODE_EXIT); + break; + } + arg.set_code(reinterpret_cast(gs.code.disown() + 1)); + continue; + } + case CODE_COND: { + TaggedValue &arg = args[numargs - 1]; + switch (arg.get_type()) { + case VAL_STR: + case VAL_MACRO: + case VAL_CSTR: + if (arg.s[0]) { + GenState gs(cs); + gs.code.reserve(64); + gs.gen_main(arg.s); + arg.cleanup(); + arg.set_code(reinterpret_cast(gs.code.disown() + 1)); + } else arg.force_null(); + break; + } + continue; + } + + case CODE_IDENT: + args[numargs++].set_ident(cs.identmap[op >> 8]); + continue; + case CODE_IDENTARG: { + Ident *id = cs.identmap[op >> 8]; + if (!(cs.stack->usedargs & (1 << id->index))) { + id->push_arg(null_value, cs.stack->argstack[id->index], false); + cs.stack->usedargs |= 1 << id->index; + } + args[numargs++].set_ident(id); + continue; + } + case CODE_IDENTU: { + TaggedValue &arg = args[numargs - 1]; + Ident *id = arg.get_type() == VAL_STR || arg.get_type() == VAL_MACRO || arg.get_type() == VAL_CSTR ? cs.new_ident(ostd::ConstCharRange(arg.cstr, arg.len)) : cs.dummy; + if (id->index < MaxArguments && !(cs.stack->usedargs & (1 << id->index))) { + id->push_arg(null_value, cs.stack->argstack[id->index], false); + cs.stack->usedargs |= 1 << id->index; + } + arg.cleanup(); + arg.set_ident(id); + continue; + } + + case CODE_LOOKUPU|RET_STR: +#define LOOKUPU(aval, sval, ival, fval, nval) { \ + TaggedValue &arg = args[numargs-1]; \ + if(arg.get_type() != VAL_STR && arg.get_type() != VAL_MACRO && arg.get_type() != VAL_CSTR) continue; \ + Ident *id = cs.idents.at(arg.s); \ + if(id) switch(id->type) \ + { \ + case ID_ALIAS: \ + if(id->flags&IDF_UNKNOWN) break; \ + arg.cleanup(); \ + if(id->index < MaxArguments && !(cs.stack->usedargs&(1<index))) { nval; continue; } \ + aval; \ + continue; \ + case ID_SVAR: arg.cleanup(); sval; continue; \ + case ID_VAR: arg.cleanup(); ival; continue; \ + case ID_FVAR: arg.cleanup(); fval; continue; \ + case ID_COMMAND: \ + { \ + arg.cleanup(); \ + arg.set_null(); \ + TaggedValue buf[MaxArguments]; \ + callcommand(cs, id, buf, arg, 0, true); \ + force_arg(arg, op&CODE_RET_MASK); \ + continue; \ + } \ + default: arg.cleanup(); nval; continue; \ + } \ + cs_debug_code(cs, "unknown alias lookup: %s", arg.s); \ + arg.cleanup(); \ + nval; \ + continue; \ + } + LOOKUPU(arg.set_str(ostd::move(id->get_str())), + arg.set_str(*id->storage.sp), + arg.set_str(ostd::move(intstr(*id->storage.ip))), + arg.set_str(ostd::move(floatstr(*id->storage.fp))), + arg.set_str("")); + case CODE_LOOKUP|RET_STR: +#define LOOKUP(aval) { \ + Ident *id = cs.identmap[op>>8]; \ + if(id->flags&IDF_UNKNOWN) cs_debug_code(cs, "unknown alias lookup: %s", id->name); \ + aval; \ + continue; \ + } + LOOKUP(args[numargs++].set_str(ostd::move(id->get_str()))); + case CODE_LOOKUPARG|RET_STR: +#define LOOKUPARG(aval, nval) { \ + Ident *id = cs.identmap[op>>8]; \ + if(!(cs.stack->usedargs&(1<index))) { nval; continue; } \ + aval; \ + continue; \ + } + LOOKUPARG(args[numargs++].set_str(ostd::move(id->get_str())), args[numargs++].set_str("")); + case CODE_LOOKUPU|RET_INT: + LOOKUPU(arg.set_int(id->get_int()), + arg.set_int(parseint(*id->storage.sp)), + arg.set_int(*id->storage.ip), + arg.set_int(int(*id->storage.fp)), + arg.set_int(0)); + case CODE_LOOKUP|RET_INT: + LOOKUP(args[numargs++].set_int(id->get_int())); + case CODE_LOOKUPARG|RET_INT: + LOOKUPARG(args[numargs++].set_int(id->get_int()), args[numargs++].set_int(0)); + case CODE_LOOKUPU|RET_FLOAT: + LOOKUPU(arg.set_float(id->get_float()), + arg.set_float(parsefloat(*id->storage.sp)), + arg.set_float(float(*id->storage.ip)), + arg.set_float(*id->storage.fp), + arg.set_float(0.0f)); + case CODE_LOOKUP|RET_FLOAT: + LOOKUP(args[numargs++].set_float(id->get_float())); + case CODE_LOOKUPARG|RET_FLOAT: + LOOKUPARG(args[numargs++].set_float(id->get_float()), args[numargs++].set_float(0.0f)); + case CODE_LOOKUPU|RET_NULL: + LOOKUPU(id->get_val(arg), + arg.set_str(*id->storage.sp), + arg.set_int(*id->storage.ip), + arg.set_float(*id->storage.fp), + arg.set_null()); + case CODE_LOOKUP|RET_NULL: + LOOKUP(id->get_val(args[numargs++])); + case CODE_LOOKUPARG|RET_NULL: + LOOKUPARG(id->get_val(args[numargs++]), args[numargs++].set_null()); + + case CODE_LOOKUPMU|RET_STR: + LOOKUPU(id->get_cstr(arg), + arg.set_cstr(*id->storage.sp), + arg.set_str(ostd::move(intstr(*id->storage.ip))), + arg.set_str(ostd::move(floatstr(*id->storage.fp))), + arg.set_cstr("")); + case CODE_LOOKUPM|RET_STR: + LOOKUP(id->get_cstr(args[numargs++])); + case CODE_LOOKUPMARG|RET_STR: + LOOKUPARG(id->get_cstr(args[numargs++]), args[numargs++].set_cstr("")); + case CODE_LOOKUPMU|RET_NULL: + LOOKUPU(id->get_cval(arg), + arg.set_cstr(*id->storage.sp), + arg.set_int(*id->storage.ip), + arg.set_float(*id->storage.fp), + arg.set_null()); + case CODE_LOOKUPM|RET_NULL: + LOOKUP(id->get_cval(args[numargs++])); + case CODE_LOOKUPMARG|RET_NULL: + LOOKUPARG(id->get_cval(args[numargs++]), args[numargs++].set_null()); + + case CODE_SVAR|RET_STR: + case CODE_SVAR|RET_NULL: + args[numargs++].set_str(*cs.identmap[op >> 8]->storage.sp); + continue; + case CODE_SVAR|RET_INT: + args[numargs++].set_int(parseint(*cs.identmap[op >> 8]->storage.sp)); + continue; + case CODE_SVAR|RET_FLOAT: + args[numargs++].set_float(parsefloat(*cs.identmap[op >> 8]->storage.sp)); + continue; + case CODE_SVARM: + args[numargs++].set_cstr(*cs.identmap[op >> 8]->storage.sp); + continue; + case CODE_SVAR1: + cs.set_var_str_checked(cs.identmap[op >> 8], args[--numargs].s); + args[numargs].cleanup(); + continue; + + case CODE_IVAR|RET_INT: + case CODE_IVAR|RET_NULL: + args[numargs++].set_int(*cs.identmap[op >> 8]->storage.ip); + continue; + case CODE_IVAR|RET_STR: + args[numargs++].set_str(ostd::move(intstr(*cs.identmap[op >> 8]->storage.ip))); + continue; + case CODE_IVAR|RET_FLOAT: + args[numargs++].set_float(float(*cs.identmap[op >> 8]->storage.ip)); + continue; + case CODE_IVAR1: + cs.set_var_int_checked(cs.identmap[op >> 8], args[--numargs].i); + continue; + case CODE_IVAR2: + numargs -= 2; + cs.set_var_int_checked(cs.identmap[op >> 8], (args[numargs].i << 16) | (args[numargs + 1].i << 8)); + continue; + case CODE_IVAR3: + numargs -= 3; + cs.set_var_int_checked(cs.identmap[op >> 8], (args[numargs].i << 16) | (args[numargs + 1].i << 8) | args[numargs + 2].i); + continue; + + case CODE_FVAR|RET_FLOAT: + case CODE_FVAR|RET_NULL: + args[numargs++].set_float(*cs.identmap[op >> 8]->storage.fp); + continue; + case CODE_FVAR|RET_STR: + args[numargs++].set_str(ostd::move(floatstr(*cs.identmap[op >> 8]->storage.fp))); + continue; + case CODE_FVAR|RET_INT: + args[numargs++].set_int(int(*cs.identmap[op >> 8]->storage.fp)); + continue; + case CODE_FVAR1: + cs.set_var_float_checked(cs.identmap[op >> 8], args[--numargs].f); + continue; + + case CODE_COM|RET_NULL: + case CODE_COM|RET_STR: + case CODE_COM|RET_FLOAT: + case CODE_COM|RET_INT: { + Ident *id = cs.identmap[op >> 8]; + int offset = numargs - id->numargs; + result.force_null(); + id->cb_cftv(TvalRange(args + offset, id->numargs), result); + force_arg(result, op & CODE_RET_MASK); + free_args(args, numargs, offset); + continue; + } + + case CODE_COMV|RET_NULL: + case CODE_COMV|RET_STR: + case CODE_COMV|RET_FLOAT: + case CODE_COMV|RET_INT: { + Ident *id = cs.identmap[op >> 13]; + int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; + result.force_null(); + id->cb_cftv(ostd::iter(&args[offset], callargs), result); + force_arg(result, op & CODE_RET_MASK); + free_args(args, numargs, offset); + continue; + } + case CODE_COMC|RET_NULL: + case CODE_COMC|RET_STR: + case CODE_COMC|RET_FLOAT: + case CODE_COMC|RET_INT: { + Ident *id = cs.identmap[op >> 13]; + int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; + result.force_null(); + { + auto buf = ostd::appender(); + cscript::util::tvals_concat(buf, ostd::iter(&args[offset], callargs), " "); + TaggedValue tv; + tv.set_mstr(buf.get().iter()); + id->cb_cftv(TvalRange(&tv, 1), result); + } + force_arg(result, op & CODE_RET_MASK); + free_args(args, numargs, offset); + continue; + } + + case CODE_CONC|RET_NULL: + case CODE_CONC|RET_STR: + case CODE_CONC|RET_FLOAT: + case CODE_CONC|RET_INT: + case CODE_CONCW|RET_NULL: + case CODE_CONCW|RET_STR: + case CODE_CONCW|RET_FLOAT: + case CODE_CONCW|RET_INT: { + int numconc = op >> 8; + auto buf = ostd::appender(); + cscript::util::tvals_concat(buf, ostd::iter(&args[numargs - numconc], numconc), ((op & CODE_OP_MASK) == CODE_CONC) ? " " : ""); + free_args(args, numargs, numargs - numconc); + args[numargs].set_mstr(buf.get().iter()); + buf.get().disown(); + force_arg(args[numargs], op & CODE_RET_MASK); + numargs++; + continue; + } + + case CODE_CONCM|RET_NULL: + case CODE_CONCM|RET_STR: + case CODE_CONCM|RET_FLOAT: + case CODE_CONCM|RET_INT: { + int numconc = op >> 8; + auto buf = ostd::appender(); + cscript::util::tvals_concat(buf, ostd::iter(&args[numargs - numconc], numconc)); + free_args(args, numargs, numargs - numconc); + result.set_mstr(buf.get().iter()); + buf.get().disown(); + force_arg(result, op & CODE_RET_MASK); + continue; + } + + case CODE_ALIAS: + cs.identmap[op >> 8]->set_alias(cs, args[--numargs]); + continue; + case CODE_ALIASARG: + cs.identmap[op >> 8]->set_arg(cs, args[--numargs]); + continue; + case CODE_ALIASU: + numargs -= 2; + cs.set_alias(args[numargs].get_str(), args[numargs + 1]); + args[numargs].cleanup(); + continue; + +#define SKIPARGS(offset) offset + case CODE_CALL|RET_NULL: + case CODE_CALL|RET_STR: + case CODE_CALL|RET_FLOAT: + case CODE_CALL|RET_INT: { +#define FORCERESULT { \ + free_args(args, numargs, SKIPARGS(offset)); \ + force_arg(result, op&CODE_RET_MASK); \ + continue; \ + } +#define CALLALIAS(cs, result) { \ + IdentStack argstack[MaxArguments]; \ + for(int i = 0; i < callargs; i++) \ + (cs).identmap[i]->push_arg(args[offset + i], argstack[i], false); \ + int oldargs = (cs).numargs; \ + (cs).numargs = callargs; \ + int oldflags = (cs).identflags; \ + (cs).identflags |= id->flags&IDF_OVERRIDDEN; \ + IdentLink aliaslink = { id, (cs).stack, (1<code) id->code = reinterpret_cast(compilecode(cs, id->get_str())); \ + ostd::Uint32 *codep = reinterpret_cast(id->code); \ + codep[0] += 0x100; \ + runcode((cs), codep+1, (result)); \ + codep[0] -= 0x100; \ + if(int(codep[0]) < 0x100) delete[] codep; \ + (cs).stack = aliaslink.next; \ + (cs).identflags = oldflags; \ + for(int i = 0; i < callargs; i++) \ + (cs).identmap[i]->pop_arg(); \ + for(int argmask = aliaslink.usedargs&(~0<pop_arg(); argmask &= ~(1<> 13]; + int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; + if (id->flags & IDF_UNKNOWN) { + cs_debug_code(cs, "unknown command: %s", id->name); + FORCERESULT; + } + CALLALIAS(cs, result); + continue; + } + case CODE_CALLARG|RET_NULL: + case CODE_CALLARG|RET_STR: + case CODE_CALLARG|RET_FLOAT: + case CODE_CALLARG|RET_INT: { + result.force_null(); + Ident *id = cs.identmap[op >> 13]; + int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; + if (!(cs.stack->usedargs & (1 << id->index))) FORCERESULT; + CALLALIAS(cs, result); + continue; + } +#undef SKIPARGS + +#define SKIPARGS(offset) offset-1 + case CODE_CALLU|RET_NULL: + case CODE_CALLU|RET_STR: + case CODE_CALLU|RET_FLOAT: + case CODE_CALLU|RET_INT: { + int callargs = op >> 8, offset = numargs - callargs; + TaggedValue &idarg = args[offset - 1]; + if (idarg.get_type() != VAL_STR && idarg.get_type() != VAL_MACRO && idarg.get_type() != VAL_CSTR) { +litval: + result.cleanup(); + result = idarg; + force_arg(result, op & CODE_RET_MASK); + while (--numargs >= offset) args[numargs].cleanup(); + continue; + } + Ident *id = cs.idents.at(idarg.s); + if (!id) { +noid: + if (cs_check_num(idarg.s)) goto litval; + cs_debug_code(cs, "unknown command: %s", idarg.s); + result.force_null(); + FORCERESULT; + } + result.force_null(); + switch (id->type) { + default: + if (!id->cb_cftv) FORCERESULT; + /* fallthrough */ + case ID_COMMAND: + idarg.cleanup(); + callcommand(cs, id, &args[offset], result, callargs); + force_arg(result, op & CODE_RET_MASK); + numargs = offset - 1; + continue; + case ID_LOCAL: { + IdentStack locals[MaxArguments]; + idarg.cleanup(); + for (ostd::Size j = 0; j < ostd::Size(callargs); ++j) cs.force_ident(args[offset + j])->push_alias(locals[j]); + code = runcode(cs, code, result); + for (ostd::Size j = 0; j < ostd::Size(callargs); ++j) args[offset + j].id->pop_alias(); + goto exit; + } + case ID_VAR: + if (callargs <= 0) cs.print_var(id); + else cs.set_var_int_checked(id, ostd::iter(&args[offset], callargs)); + FORCERESULT; + case ID_FVAR: + if (callargs <= 0) cs.print_var(id); + else cs.set_var_float_checked(id, args[offset].force_float()); + FORCERESULT; + case ID_SVAR: + if (callargs <= 0) cs.print_var(id); + else cs.set_var_str_checked(id, args[offset].force_str()); + FORCERESULT; + case ID_ALIAS: + if (id->index < MaxArguments && !(cs.stack->usedargs & (1 << id->index))) FORCERESULT; + if (id->get_valtype() == VAL_NULL) goto noid; + idarg.cleanup(); + CALLALIAS(cs, result); + continue; + } + } +#undef SKIPARGS + } + } +exit: + --rundepth; + return code; +} + +void CsState::run_ret(Bytecode const *code, TaggedValue &ret) { + runcode(*this, reinterpret_cast(code), ret); +} + +void CsState::run_ret(ostd::ConstCharRange code, TaggedValue &ret) { + GenState gs(*this); + gs.code.reserve(64); + /* FIXME range */ + gs.gen_main(code.data(), VAL_ANY); + runcode(*this, gs.code.data() + 1, ret); + if (int(gs.code[0]) >= 0x100) + gs.code.disown(); +} + +/* TODO */ +void CsState::run_ret(Ident *id, TvalRange args, TaggedValue &ret) { + int nargs = int(args.size()); + ret.set_null(); + ++rundepth; + if (rundepth > MaxRunDepth) cs_debug_code(*this, "exceeded recursion limit"); + else if (id) switch (id->type) { + default: + if (!id->cb_cftv) break; + /* fallthrough */ + case ID_COMMAND: + if (nargs < id->numargs) { + TaggedValue buf[MaxArguments]; + memcpy(buf, args.data(), args.size() * sizeof(TaggedValue)); + callcommand(*this, id, buf, ret, nargs, false); + } else callcommand(*this, id, args.data(), ret, nargs, false); + nargs = 0; + break; + case ID_VAR: + if (args.empty()) print_var(id); + else set_var_int_checked(id, args); + break; + case ID_FVAR: + if (args.empty()) print_var(id); + else set_var_float_checked(id, args[0].force_float()); + break; + case ID_SVAR: + if (args.empty()) print_var(id); + else set_var_str_checked(id, args[0].force_str()); + break; + case ID_ALIAS: + if (id->index < MaxArguments && !(stack->usedargs & (1 << id->index))) break; + if (id->get_valtype() == VAL_NULL) break; +#define callargs nargs +#define offset 0 +#define op RET_NULL +#define SKIPARGS(offset) offset + CALLALIAS(*this, ret); +#undef callargs +#undef offset +#undef op +#undef SKIPARGS + break; + } + free_args(args.data(), nargs, 0); + --rundepth; +} + +ostd::String CsState::run_str(Bytecode const *code) { + TaggedValue ret; + run_ret(code, ret); + ostd::String s = ret.get_str(); + ret.cleanup(); + return s; +} + +ostd::String CsState::run_str(ostd::ConstCharRange code) { + TaggedValue ret; + run_ret(code, ret); + ostd::String s = ret.get_str(); + ret.cleanup(); + return s; +} + +ostd::String CsState::run_str(Ident *id, TvalRange args) { + TaggedValue ret; + run_ret(id, args, ret); + ostd::String s = ret.get_str(); + ret.cleanup(); + return s; +} + +int CsState::run_int(Bytecode const *code) { + TaggedValue ret; + run_ret(code, ret); + int i = ret.get_int(); + ret.cleanup(); + return i; +} + +int CsState::run_int(ostd::ConstCharRange code) { + TaggedValue ret; + run_ret(code, ret); + int i = ret.get_int(); + ret.cleanup(); + return i; +} + +int CsState::run_int(Ident *id, TvalRange args) { + TaggedValue ret; + run_ret(id, args, ret); + int i = ret.get_int(); + ret.cleanup(); + return i; +} + +float CsState::run_float(Bytecode const *code) { + TaggedValue ret; + run_ret(code, ret); + float f = ret.get_float(); + ret.cleanup(); + return f; +} + +float CsState::run_float(ostd::ConstCharRange code) { + TaggedValue ret; + run_ret(code, ret); + float f = ret.get_float(); + ret.cleanup(); + return f; +} + +float CsState::run_float(Ident *id, TvalRange args) { + TaggedValue ret; + run_ret(id, args, ret); + float f = ret.get_float(); + ret.cleanup(); + return f; +} + +bool CsState::run_bool(Bytecode const *code) { + TaggedValue ret; + run_ret(code, ret); + bool b = ret.get_bool(); + ret.cleanup(); + return b; +} + +bool CsState::run_bool(ostd::ConstCharRange code) { + TaggedValue ret; + run_ret(code, ret); + bool b = ret.get_bool(); + ret.cleanup(); + return b; +} + +bool CsState::run_bool(Ident *id, TvalRange args) { + TaggedValue ret; + run_ret(id, args, ret); + bool b = ret.get_bool(); + ret.cleanup(); + return b; +} + +bool CsState::run_file(ostd::ConstCharRange fname) { + ostd::ConstCharRange oldsrcfile = src_file, oldsrcstr = src_str; + char *buf = nullptr; + ostd::Size len; + + ostd::FileStream f(fname, ostd::StreamMode::read); + if (!f.is_open()) + return false; + + len = f.size(); + buf = new char[len + 1]; + if (f.get(buf, len) != len) { + delete[] buf; + return false; + } + buf[len] = '\0'; + + src_file = fname; + src_str = ostd::ConstCharRange(buf, len); + run_int(buf); + src_file = oldsrcfile; + src_str = oldsrcstr; + delete[] buf; + return true; +} + +} /* namespace cscript */ diff --git a/cs_vm.hh b/cs_vm.hh new file mode 100644 index 0000000..8e60461 --- /dev/null +++ b/cs_vm.hh @@ -0,0 +1,220 @@ +#ifndef LIBCUBESCRIPT_CS_VM_HH +#define LIBCUBESCRIPT_CS_VM_HH + +#include "cubescript.hh" + +#include + +#include +#include + +namespace cscript { + +static constexpr int MaxArguments = 25; +static constexpr int MaxResults = 7; + +enum { + ID_UNKNOWN = -1, ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND, ID_ALIAS, + ID_LOCAL, ID_DO, ID_DOARGS, ID_IF, ID_RESULT, ID_NOT, ID_AND, ID_OR +}; + +enum { + CODE_START = 0, + CODE_OFFSET, + CODE_NULL, CODE_TRUE, CODE_FALSE, CODE_NOT, + CODE_POP, + CODE_ENTER, CODE_ENTER_RESULT, + CODE_EXIT, CODE_RESULT_ARG, + CODE_VAL, CODE_VALI, + CODE_DUP, + CODE_MACRO, + CODE_BOOL, + CODE_BLOCK, CODE_EMPTY, + CODE_COMPILE, CODE_COND, + CODE_FORCE, + CODE_RESULT, + CODE_IDENT, CODE_IDENTU, CODE_IDENTARG, + CODE_COM, CODE_COMC, CODE_COMV, + CODE_CONC, CODE_CONCW, CODE_CONCM, CODE_DOWN, + CODE_SVAR, CODE_SVARM, CODE_SVAR1, + CODE_IVAR, CODE_IVAR1, CODE_IVAR2, CODE_IVAR3, + CODE_FVAR, CODE_FVAR1, + CODE_LOOKUP, CODE_LOOKUPU, CODE_LOOKUPARG, + CODE_LOOKUPM, CODE_LOOKUPMU, CODE_LOOKUPMARG, + CODE_ALIAS, CODE_ALIASU, CODE_ALIASARG, + CODE_CALL, CODE_CALLU, CODE_CALLARG, + CODE_PRINT, + CODE_LOCAL, + CODE_DO, CODE_DOARGS, + CODE_JUMP, CODE_JUMP_TRUE, CODE_JUMP_FALSE, + CODE_JUMP_RESULT_TRUE, CODE_JUMP_RESULT_FALSE, + + CODE_OP_MASK = 0x3F, + CODE_RET = 6, + CODE_RET_MASK = 0xC0, + + /* return type flags */ + RET_NULL = VAL_NULL << CODE_RET, + RET_STR = VAL_STR << CODE_RET, + RET_INT = VAL_INT << CODE_RET, + RET_FLOAT = VAL_FLOAT << CODE_RET, +}; + +struct NullValue: TaggedValue { + NullValue() { set_null(); } +} const null_value; + +template +static void cs_do_args(CsState &cs, F body) { + IdentStack argstack[MaxArguments]; + int argmask1 = cs.stack->usedargs; + for (int i = 0; argmask1; argmask1 >>= 1, ++i) if(argmask1 & 1) + cs.identmap[i]->undo_arg(argstack[i]); + IdentLink *prevstack = cs.stack->next; + IdentLink aliaslink = { + cs.stack->id, cs.stack, prevstack->usedargs, prevstack->argstack + }; + cs.stack = &aliaslink; + body(); + prevstack->usedargs = aliaslink.usedargs; + cs.stack = aliaslink.next; + int argmask2 = cs.stack->usedargs; + for(int i = 0; argmask2; argmask2 >>= 1, ++i) if(argmask2 & 1) + cs.identmap[i]->redo_arg(argstack[i]); +} + +ostd::ConstCharRange cs_debug_line(CsState &cs, + ostd::ConstCharRange p, + ostd::ConstCharRange fmt, + ostd::CharRange buf); + +void cs_debug_alias(CsState &cs); + +template +void cs_debug_code(CsState &cs, ostd::ConstCharRange fmt, A &&...args) { + if (cs.nodebug) return; + ostd::err.writefln(fmt, ostd::forward(args)...); + cs_debug_alias(cs); +} + +template +void cs_debug_code_line(CsState &cs, ostd::ConstCharRange p, + ostd::ConstCharRange fmt, A &&...args) { + if (cs.nodebug) return; + ostd::Array buf; + ostd::err.writefln(cs_debug_line(cs, p, fmt, ostd::CharRange(buf.data(), + buf.size())), + ostd::forward(args)...); + cs_debug_alias(cs); +} + +ostd::Uint32 *compilecode(CsState &cs, ostd::ConstCharRange str); + +struct GenState { + CsState &cs; + ostd::Vector code; + char const *source; + + GenState() = delete; + GenState(CsState &csr): cs(csr), code(), source(nullptr) {} + + void gen_str(ostd::ConstCharRange word, bool macro = false) { + if (word.size() <= 3 && !macro) { + ostd::Uint32 op = CODE_VALI | RET_STR; + for (ostd::Size i = 0; i < word.size(); ++i) + op |= ostd::Uint32(ostd::byte(word[i])) << ((i + 1) * 8); + code.push(op); + return; + } + code.push((macro ? CODE_MACRO : (CODE_VAL | RET_STR)) | + (word.size() << 8)); + code.push_n(reinterpret_cast(word.data()), + word.size() / sizeof(ostd::Uint32)); + ostd::Size esz = word.size() % sizeof(ostd::Uint32); + union { + char c[sizeof(ostd::Uint32)]; + ostd::Uint32 u; + } end; + end.u = 0; + memcpy(end.c, word.data() + word.size() - esz, esz); + code.push(end.u); + } + + void gen_str() { + code.push(CODE_VALI | RET_STR); + } + + void gen_null() { + code.push(CODE_VALI | RET_NULL); + } + + void gen_int(int i = 0) { + if (i >= -0x800000 && i <= 0x7FFFFF) + code.push(CODE_VALI | RET_INT | (i << 8)); + else { + code.push(CODE_VAL | RET_INT); + code.push(i); + } + } + + void gen_int(ostd::ConstCharRange word); + + void gen_float(float f = 0.0f) { + if (int(f) == f && f >= -0x800000 && f <= 0x7FFFFF) + code.push(CODE_VALI | RET_FLOAT | (int(f) << 8)); + else { + union { + float f; + ostd::Uint32 u; + } c; + c.f = f; + code.push(CODE_VAL | RET_FLOAT); + code.push(c.u); + } + } + + void gen_float(ostd::ConstCharRange word); + + void gen_ident(Ident *id) { + code.push(((id->index < MaxArguments) ? CODE_IDENTARG + : CODE_IDENT) | + (id->index << 8)); + } + + void gen_ident() { + gen_ident(cs.dummy); + } + + void gen_ident(ostd::ConstCharRange word) { + gen_ident(cs.new_ident(word)); + } + + void gen_value(int wordtype, ostd::ConstCharRange word + = ostd::ConstCharRange()); + + void gen_main(ostd::ConstCharRange s, int ret_type = VAL_ANY); + + char next_char() { + return *source++; + } + + char current() { + return *source; + } +}; + +void cs_set_macro( + TaggedValue &tv, Bytecode const *val, ostd::Size len +); + +int parseint(char const *s); +float parsefloat(char const *s); + +ostd::String intstr(int v); +ostd::String floatstr(float v); + +bool cs_check_num(ostd::ConstCharRange s); + +} /* namespace cscript */ + +#endif /* LIBCUBESCRIPT_CS_VM_HH */ diff --git a/cubescript.cc b/cubescript.cc index f9a8241..3a8ec0c 100644 --- a/cubescript.cc +++ b/cubescript.cc @@ -1,4 +1,5 @@ #include "cubescript.hh" +#include "cs_vm.hh" #include #include @@ -6,62 +7,11 @@ #include #include -#include #include namespace cscript { -static constexpr int MaxArguments = 25; -static constexpr int MaxResults = 7; - -enum { - ID_UNKNOWN = -1, ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND, ID_ALIAS, - ID_LOCAL, ID_DO, ID_DOARGS, ID_IF, ID_RESULT, ID_NOT, ID_AND, ID_OR -}; - -enum { - CODE_START = 0, - CODE_OFFSET, - CODE_NULL, CODE_TRUE, CODE_FALSE, CODE_NOT, - CODE_POP, - CODE_ENTER, CODE_ENTER_RESULT, - CODE_EXIT, CODE_RESULT_ARG, - CODE_VAL, CODE_VALI, - CODE_DUP, - CODE_MACRO, - CODE_BOOL, - CODE_BLOCK, CODE_EMPTY, - CODE_COMPILE, CODE_COND, - CODE_FORCE, - CODE_RESULT, - CODE_IDENT, CODE_IDENTU, CODE_IDENTARG, - CODE_COM, CODE_COMC, CODE_COMV, - CODE_CONC, CODE_CONCW, CODE_CONCM, CODE_DOWN, - CODE_SVAR, CODE_SVARM, CODE_SVAR1, - CODE_IVAR, CODE_IVAR1, CODE_IVAR2, CODE_IVAR3, - CODE_FVAR, CODE_FVAR1, - CODE_LOOKUP, CODE_LOOKUPU, CODE_LOOKUPARG, - CODE_LOOKUPM, CODE_LOOKUPMU, CODE_LOOKUPMARG, - CODE_ALIAS, CODE_ALIASU, CODE_ALIASARG, - CODE_CALL, CODE_CALLU, CODE_CALLARG, - CODE_PRINT, - CODE_LOCAL, - CODE_DO, CODE_DOARGS, - CODE_JUMP, CODE_JUMP_TRUE, CODE_JUMP_FALSE, - CODE_JUMP_RESULT_TRUE, CODE_JUMP_RESULT_FALSE, - - CODE_OP_MASK = 0x3F, - CODE_RET = 6, - CODE_RET_MASK = 0xC0, - - /* return type flags */ - RET_NULL = VAL_NULL << CODE_RET, - RET_STR = VAL_STR << CODE_RET, - RET_INT = VAL_INT << CODE_RET, - RET_FLOAT = VAL_FLOAT << CODE_RET, -}; - -static inline int parseint(char const *s) { +int parseint(char const *s) { return int(strtoul(s, nullptr, 0)); } @@ -70,7 +20,7 @@ int cs_parse_int(ostd::ConstCharRange s) { return parseint(s.data()); } -static inline float parsefloat(char const *s) { +float parsefloat(char const *s) { /* not all platforms (windows) can parse hexadecimal integers via strtod */ char *end; double val = strtod(s, &end); @@ -101,7 +51,7 @@ char *cs_dup_ostr(ostd::ConstCharRange s) { return r; } -static inline bool cs_check_num(ostd::ConstCharRange s) { +bool cs_check_num(ostd::ConstCharRange s) { if (isdigit(s[0])) return true; switch (s[0]) { @@ -174,12 +124,6 @@ Ident::Ident(int t, ostd::ConstCharRange n, ostd::ConstCharRange args, argmask(argmask), cb_cftv(ostd::move(f)) { } -struct NullValue: TaggedValue { - NullValue() { set_null(); } -} const null_value; - -static TaggedValue no_ret = null_value; - void cs_init_lib_base(CsState &cs); CsState::CsState() { @@ -254,24 +198,6 @@ void cs_debug_alias(CsState &cs) { } } -template -void cs_debug_code(CsState &cs, ostd::ConstCharRange fmt, A &&...args) { - if (cs.nodebug) return; - ostd::err.writefln(fmt, ostd::forward(args)...); - cs_debug_alias(cs); -} - -template -void cs_debug_code_line(CsState &cs, ostd::ConstCharRange p, - ostd::ConstCharRange fmt, A &&...args) { - if (cs.nodebug) return; - ostd::Array buf; - ostd::err.writefln(cs_debug_line(cs, p, fmt, ostd::CharRange(buf.data(), - buf.size())), - ostd::forward(args)...); - cs_debug_alias(cs); -} - void CsState::clear_override(Ident &id) { if (!(id.flags & IDF_OVERRIDDEN)) return; switch (id.type) { @@ -454,42 +380,12 @@ void TaggedValue::cleanup() { } } -static ostd::Uint32 const *skipcode(ostd::Uint32 const *code, TaggedValue *result = nullptr); - -void TaggedValue::copy_arg(TaggedValue &r) const { - r.cleanup(); - switch (get_type()) { - case VAL_INT: - case VAL_FLOAT: - case VAL_IDENT: - r = *this; - break; - case VAL_STR: - case VAL_CSTR: - case VAL_MACRO: - r.set_str(ostd::ConstCharRange(s, len)); - break; - case VAL_CODE: { - ostd::Uint32 const *bcode = reinterpret_cast(code); - ostd::Uint32 const *end = skipcode(bcode); - ostd::Uint32 *dst = new ostd::Uint32[end - bcode + 1]; - *dst++ = CODE_START; - memcpy(dst, bcode, (end - bcode) * sizeof(ostd::Uint32)); - r.set_code(reinterpret_cast(dst)); - break; - } - default: - r.set_null(); - break; - } -} - /* XXX: nasty */ struct InternalTval: IdentValue { int type; }; -static inline void cs_set_macro( +void cs_set_macro( TaggedValue &tv, Bytecode const *val, ostd::Size len ) { InternalTval &itv = reinterpret_cast(tv); @@ -563,20 +459,6 @@ ostd::ConstCharRange TaggedValue::force_str() { return s; } -static inline void force_arg(TaggedValue &v, int type) { - switch (type) { - case RET_STR: - if (v.get_type() != VAL_STR) v.force_str(); - break; - case RET_INT: - if (v.get_type() != VAL_INT) v.force_int(); - break; - case RET_FLOAT: - if (v.get_type() != VAL_FLOAT) v.force_float(); - break; - } -} - static inline int cs_get_int(IdentValue const &v, int type) { switch (type) { case VAL_FLOAT: @@ -745,11 +627,6 @@ void Ident::get_cval(TaggedValue &v) const { } } -static inline void free_args(TaggedValue *args, int &oldnum, int newnum) { - for (int i = newnum; i < oldnum; i++) args[i].cleanup(); - oldnum = newnum; -} - void Ident::clean_code() { ostd::Uint32 *bcode = reinterpret_cast(code); if (bcode) { @@ -832,25 +709,6 @@ IdentType Ident::get_type() const { return IdentType(type); } -template -static void cs_do_args(CsState &cs, F body) { - IdentStack argstack[MaxArguments]; - int argmask1 = cs.stack->usedargs; - for (int i = 0; argmask1; argmask1 >>= 1, ++i) if(argmask1 & 1) - cs.identmap[i]->undo_arg(argstack[i]); - IdentLink *prevstack = cs.stack->next; - IdentLink aliaslink = { - cs.stack->id, cs.stack, prevstack->usedargs, prevstack->argstack - }; - cs.stack = &aliaslink; - body(); - prevstack->usedargs = aliaslink.usedargs; - cs.stack = aliaslink.next; - int argmask2 = cs.stack->usedargs; - for(int i = 0; argmask2; argmask2 >>= 1, ++i) if(argmask2 & 1) - cs.identmap[i]->redo_arg(argstack[i]); -} - template bool cs_override_var(CsState &cs, Ident *id, SF sf, RF rf, CF cf) { if ((cs.identflags & IDF_OVERRIDDEN) || (id->flags & IDF_OVERRIDE)) { @@ -1217,148 +1075,59 @@ static inline int cs_ret_code(int type, int def = 0) { : (type << CODE_RET); } -struct GenState; - static void compilestatements(GenState &gs, int rettype, int brak = '\0', int prevargs = 0); static inline char const *compileblock(GenState &gs, char const *p, int rettype = RET_NULL, int brak = '\0'); -struct GenState { - CsState &cs; - ostd::Vector code; - char const *source; +void GenState::gen_int(ostd::ConstCharRange word) { + gen_int(cs_parse_int(word)); +} - GenState() = delete; - GenState(CsState &csr): cs(csr), code(), source(nullptr) {} +void GenState::gen_float(ostd::ConstCharRange word) { + gen_float(cs_parse_float(word)); +} - void gen_str(ostd::ConstCharRange word, bool macro = false) { - if (word.size() <= 3 && !macro) { - ostd::Uint32 op = CODE_VALI | RET_STR; - for (ostd::Size i = 0; i < word.size(); ++i) - op |= ostd::Uint32(ostd::byte(word[i])) << ((i + 1) * 8); - code.push(op); - return; - } - code.push((macro ? CODE_MACRO : (CODE_VAL | RET_STR)) | - (word.size() << 8)); - code.push_n(reinterpret_cast(word.data()), - word.size() / sizeof(ostd::Uint32)); - ostd::Size esz = word.size() % sizeof(ostd::Uint32); - union { - char c[sizeof(ostd::Uint32)]; - ostd::Uint32 u; - } end; - end.u = 0; - memcpy(end.c, word.data() + word.size() - esz, esz); - code.push(end.u); - } - - void gen_str() { - code.push(CODE_VALI | RET_STR); - } - - void gen_null() { - code.push(CODE_VALI | RET_NULL); - } - - void gen_int(int i = 0) { - if (i >= -0x800000 && i <= 0x7FFFFF) - code.push(CODE_VALI | RET_INT | (i << 8)); - else { - code.push(CODE_VAL | RET_INT); - code.push(i); - } - } - - void gen_int(ostd::ConstCharRange word) { - gen_int(cs_parse_int(word)); - } - - void gen_float(float f = 0.0f) { - if (int(f) == f && f >= -0x800000 && f <= 0x7FFFFF) - code.push(CODE_VALI | RET_FLOAT | (int(f) << 8)); - else { - union { - float f; - ostd::Uint32 u; - } c; - c.f = f; - code.push(CODE_VAL | RET_FLOAT); - code.push(c.u); - } - } - - void gen_float(ostd::ConstCharRange word) { - gen_float(cs_parse_float(word)); - } - - void gen_ident(Ident *id) { - code.push(((id->index < MaxArguments) ? CODE_IDENTARG - : CODE_IDENT) | - (id->index << 8)); - } - - void gen_ident() { - gen_ident(cs.dummy); - } - - void gen_ident(ostd::ConstCharRange word) { - gen_ident(cs.new_ident(word)); - } - - void gen_value(int wordtype, ostd::ConstCharRange word - = ostd::ConstCharRange()) { - switch (wordtype) { - case VAL_CANY: - if (!word.empty()) - gen_str(word, true); - else - gen_null(); - break; - case VAL_CSTR: +void GenState::gen_value(int wordtype, ostd::ConstCharRange word) { + switch (wordtype) { + case VAL_CANY: + if (!word.empty()) gen_str(word, true); - break; - case VAL_ANY: - if (!word.empty()) - gen_str(word); - else - gen_null(); - break; - case VAL_STR: + else + gen_null(); + break; + case VAL_CSTR: + gen_str(word, true); + break; + case VAL_ANY: + if (!word.empty()) gen_str(word); - break; - case VAL_FLOAT: - gen_float(word); - break; - case VAL_INT: - gen_int(word); - break; - case VAL_COND: - if (!word.empty()) - compileblock(*this, word.data()); - else - gen_null(); - break; - case VAL_CODE: + else + gen_null(); + break; + case VAL_STR: + gen_str(word); + break; + case VAL_FLOAT: + gen_float(word); + break; + case VAL_INT: + gen_int(word); + break; + case VAL_COND: + if (!word.empty()) compileblock(*this, word.data()); - break; - case VAL_IDENT: - gen_ident(word); - break; - default: - break; - } + else + gen_null(); + break; + case VAL_CODE: + compileblock(*this, word.data()); + break; + case VAL_IDENT: + gen_ident(word); + break; + default: + break; } - - void gen_main(ostd::ConstCharRange s, int ret_type = VAL_ANY); - - char next_char() { - return *source++; - } - - char current() { - return *source; - } -}; +} static inline void compileblock(GenState &gs) { gs.code.push(CODE_EMPTY); @@ -1401,13 +1170,6 @@ static inline void compileunescapestr(GenState &gs, bool macro = false) { if (*gs.source == '\"') gs.next_char(); } -static ostd::Uint32 emptyblock[VAL_ANY][2] = { - { CODE_START + 0x100, CODE_EXIT | RET_NULL }, - { CODE_START + 0x100, CODE_EXIT | RET_INT }, - { CODE_START + 0x100, CODE_EXIT | RET_FLOAT }, - { CODE_START + 0x100, CODE_EXIT | RET_STR } -}; - OSTD_EXPORT bool code_is_empty(Bytecode const *code) { if (!code) { return true; @@ -2367,7 +2129,7 @@ ostd::Uint32 *compilecode(CsState &cs, ostd::ConstCharRange str) { return code; } -static inline ostd::Uint32 const *forcecode(CsState &cs, TaggedValue &v) { +ostd::Uint32 const *forcecode(CsState &cs, TaggedValue &v) { if (v.get_type() != VAL_CODE) { GenState gs(cs); gs.code.reserve(64); @@ -2378,7 +2140,7 @@ static inline ostd::Uint32 const *forcecode(CsState &cs, TaggedValue &v) { return reinterpret_cast(v.code); } -static inline void forcecond(CsState &cs, TaggedValue &v) { +void forcecond(CsState &cs, TaggedValue &v) { switch (v.get_type()) { case VAL_STR: case VAL_MACRO: @@ -2453,1043 +2215,6 @@ BytecodeRef &BytecodeRef::operator=(BytecodeRef &&v) { return *this; } -static ostd::Uint32 const *skipcode(ostd::Uint32 const *code, TaggedValue *result) { - int depth = 0; - for (;;) { - ostd::Uint32 op = *code++; - switch (op & 0xFF) { - case CODE_MACRO: - case CODE_VAL|RET_STR: { - ostd::Uint32 len = op >> 8; - code += len / sizeof(ostd::Uint32) + 1; - continue; - } - case CODE_BLOCK: - case CODE_JUMP: - case CODE_JUMP_TRUE: - case CODE_JUMP_FALSE: - case CODE_JUMP_RESULT_TRUE: - case CODE_JUMP_RESULT_FALSE: { - ostd::Uint32 len = op >> 8; - code += len; - continue; - } - case CODE_ENTER: - case CODE_ENTER_RESULT: - ++depth; - continue; - case CODE_EXIT|RET_NULL: - case CODE_EXIT|RET_STR: - case CODE_EXIT|RET_INT: - case CODE_EXIT|RET_FLOAT: - if (depth <= 0) { - if (result) { - force_arg(*result, op & CODE_RET_MASK); - } - return code; - } - --depth; - continue; - } - } -} - -static inline void callcommand(CsState &cs, Ident *id, TaggedValue *args, TaggedValue &res, int numargs, bool lookup = false) { - int i = -1, fakeargs = 0; - bool rep = false; - for (char const *fmt = id->cargs; *fmt; fmt++) switch (*fmt) { - case 'i': - if (++i >= numargs) { - if (rep) break; - args[i].set_int(0); - fakeargs++; - } else args[i].force_int(); - break; - case 'b': - if (++i >= numargs) { - if (rep) break; - args[i].set_int(INT_MIN); - fakeargs++; - } else args[i].force_int(); - break; - case 'f': - if (++i >= numargs) { - if (rep) break; - args[i].set_float(0.0f); - fakeargs++; - } else args[i].force_float(); - break; - case 'F': - if (++i >= numargs) { - if (rep) break; - args[i].set_float(args[i - 1].get_float()); - fakeargs++; - } else args[i].force_float(); - break; - case 'S': - if (++i >= numargs) { - if (rep) break; - args[i].set_str(""); - fakeargs++; - } else args[i].force_str(); - break; - case 's': - if (++i >= numargs) { - if (rep) break; - args[i].set_cstr(""); - fakeargs++; - } else args[i].force_str(); - break; - case 'T': - case 't': - if (++i >= numargs) { - if (rep) break; - args[i].set_null(); - fakeargs++; - } - break; - case 'E': - if (++i >= numargs) { - if (rep) break; - args[i].set_null(); - fakeargs++; - } else forcecond(cs, args[i]); - break; - case 'e': - if (++i >= numargs) { - if (rep) break; - args[i].set_code(reinterpret_cast(emptyblock[VAL_NULL] + 1)); - fakeargs++; - } else forcecode(cs, args[i]); - break; - case 'r': - if (++i >= numargs) { - if (rep) break; - args[i].set_ident(cs.dummy); - fakeargs++; - } else cs.force_ident(args[i]); - break; - case '$': - if (++i < numargs) args[i].cleanup(); - args[i].set_ident(id); - break; - case 'N': - if (++i < numargs) args[i].cleanup(); - args[i].set_int(lookup ? -1 : i - fakeargs); - break; - case 'C': { - i = ostd::max(i + 1, numargs); - auto buf = ostd::appender(); - cscript::util::tvals_concat(buf, ostd::iter(args, i), " "); - TaggedValue tv; - tv.set_mstr(buf.get().iter()); - id->cb_cftv(TvalRange(&tv, 1), res); - goto cleanup; - } - case 'V': - i = ostd::max(i + 1, numargs); - id->cb_cftv(ostd::iter(args, i), res); - goto cleanup; - case '1': - case '2': - case '3': - case '4': - if (i + 1 < numargs) { - fmt -= *fmt - '0' + 1; - rep = true; - } - break; - } - ++i; - id->cb_cftv(TvalRange(args, i), res); -cleanup: - for (ostd::Size k = 0; k < ostd::Size(i); ++k) args[k].cleanup(); - for (; i < numargs; i++) args[i].cleanup(); -} - -static constexpr int MaxRunDepth = 255; -static thread_local int rundepth = 0; - -static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, TaggedValue &result) { - result.set_null(); - if (rundepth >= MaxRunDepth) { - cs_debug_code(cs, "exceeded recursion limit"); - return skipcode(code, (&result == &no_ret) ? nullptr : &result); - } - ++rundepth; - int numargs = 0; - TaggedValue args[MaxArguments + MaxResults]; - for (;;) { - ostd::Uint32 op = *code++; - switch (op & 0xFF) { - case CODE_START: - case CODE_OFFSET: - continue; - -#define RETOP(op, val) \ - case op: \ - result.cleanup(); \ - val; \ - continue; - - RETOP(CODE_NULL | RET_NULL, result.set_null()) - RETOP(CODE_NULL | RET_STR, result.set_str("")) - RETOP(CODE_NULL | RET_INT, result.set_int(0)) - RETOP(CODE_NULL | RET_FLOAT, result.set_float(0.0f)) - - RETOP(CODE_FALSE | RET_STR, result.set_str("0")) - case CODE_FALSE|RET_NULL: - RETOP(CODE_FALSE | RET_INT, result.set_int(0)) - RETOP(CODE_FALSE | RET_FLOAT, result.set_float(0.0f)) - - RETOP(CODE_TRUE | RET_STR, result.set_str("1")) - case CODE_TRUE|RET_NULL: - RETOP(CODE_TRUE | RET_INT, result.set_int(1)) - RETOP(CODE_TRUE | RET_FLOAT, result.set_float(1.0f)) - -#define RETPOP(op, val) \ - RETOP(op, { --numargs; val; args[numargs].cleanup(); }) - - RETPOP(CODE_NOT | RET_STR, result.set_str(args[numargs].get_bool() ? "0" : "1")) - case CODE_NOT|RET_NULL: - RETPOP(CODE_NOT | RET_INT, result.set_int(args[numargs].get_bool() ? 0 : 1)) - RETPOP(CODE_NOT | RET_FLOAT, result.set_float(args[numargs].get_bool() ? 0.0f : 1.0f)) - - case CODE_POP: - args[--numargs].cleanup(); - continue; - case CODE_ENTER: - code = runcode(cs, code, args[numargs++]); - continue; - case CODE_ENTER_RESULT: - result.cleanup(); - code = runcode(cs, code, result); - continue; - case CODE_EXIT|RET_STR: - case CODE_EXIT|RET_INT: - case CODE_EXIT|RET_FLOAT: - force_arg(result, op & CODE_RET_MASK); - /* fallthrough */ - case CODE_EXIT|RET_NULL: - goto exit; - case CODE_RESULT_ARG|RET_STR: - case CODE_RESULT_ARG|RET_INT: - case CODE_RESULT_ARG|RET_FLOAT: - force_arg(result, op & CODE_RET_MASK); - /* fallthrough */ - case CODE_RESULT_ARG|RET_NULL: - args[numargs++] = result; - result.set_null(); - continue; - case CODE_PRINT: - cs.print_var(cs.identmap[op >> 8]); - continue; - - case CODE_LOCAL: { - result.cleanup(); - int numlocals = op >> 8, offset = numargs - numlocals; - IdentStack locals[MaxArguments]; - for (int i = 0; i < numlocals; ++i) args[offset + i].id->push_alias(locals[i]); - code = runcode(cs, code, result); - for (int i = offset; i < numargs; i++) args[i].id->pop_alias(); - goto exit; - } - - case CODE_DOARGS|RET_NULL: - case CODE_DOARGS|RET_STR: - case CODE_DOARGS|RET_INT: - case CODE_DOARGS|RET_FLOAT: - if (cs.stack != &cs.noalias) { - cs_do_args(cs, [&]() { - result.cleanup(); - cs.run_ret(args[--numargs].code, result); - args[numargs].cleanup(); - force_arg(result, op & CODE_RET_MASK); - }); - continue; - } - /* fallthrough */ - case CODE_DO|RET_NULL: - case CODE_DO|RET_STR: - case CODE_DO|RET_INT: - case CODE_DO|RET_FLOAT: - result.cleanup(); - cs.run_ret(args[--numargs].code, result); - args[numargs].cleanup(); - force_arg(result, op & CODE_RET_MASK); - continue; - - case CODE_JUMP: { - ostd::Uint32 len = op >> 8; - code += len; - continue; - } - case CODE_JUMP_TRUE: { - ostd::Uint32 len = op >> 8; - if (args[--numargs].get_bool()) code += len; - args[numargs].cleanup(); - continue; - } - case CODE_JUMP_FALSE: { - ostd::Uint32 len = op >> 8; - if (!args[--numargs].get_bool()) code += len; - args[numargs].cleanup(); - continue; - } - case CODE_JUMP_RESULT_TRUE: { - ostd::Uint32 len = op >> 8; - result.cleanup(); - --numargs; - if (args[numargs].get_type() == VAL_CODE) { - cs.run_ret(args[numargs].code, result); - args[numargs].cleanup(); - } else result = args[numargs]; - if (result.get_bool()) code += len; - continue; - } - case CODE_JUMP_RESULT_FALSE: { - ostd::Uint32 len = op >> 8; - result.cleanup(); - --numargs; - if (args[numargs].get_type() == VAL_CODE) { - cs.run_ret(args[numargs].code, result); - args[numargs].cleanup(); - } else result = args[numargs]; - if (!result.get_bool()) code += len; - continue; - } - - case CODE_MACRO: { - ostd::Uint32 len = op >> 8; - cs_set_macro(args[numargs++], reinterpret_cast(code), len); - code += len / sizeof(ostd::Uint32) + 1; - continue; - } - - case CODE_VAL|RET_STR: { - ostd::Uint32 len = op >> 8; - args[numargs++].set_str(ostd::ConstCharRange(reinterpret_cast(code), len)); - code += len / sizeof(ostd::Uint32) + 1; - continue; - } - case CODE_VALI|RET_STR: { - char s[4] = { char((op >> 8) & 0xFF), char((op >> 16) & 0xFF), char((op >> 24) & 0xFF), '\0' }; - args[numargs++].set_str(s); - continue; - } - case CODE_VAL|RET_NULL: - case CODE_VALI|RET_NULL: - args[numargs++].set_null(); - continue; - case CODE_VAL|RET_INT: - args[numargs++].set_int(int(*code++)); - continue; - case CODE_VALI|RET_INT: - args[numargs++].set_int(int(op) >> 8); - continue; - case CODE_VAL|RET_FLOAT: - args[numargs++].set_float(*reinterpret_cast(code++)); - continue; - case CODE_VALI|RET_FLOAT: - args[numargs++].set_float(float(int(op) >> 8)); - continue; - - case CODE_DUP|RET_NULL: - args[numargs - 1].get_val(args[numargs]); - numargs++; - continue; - case CODE_DUP|RET_INT: - args[numargs].set_int(args[numargs - 1].get_int()); - numargs++; - continue; - case CODE_DUP|RET_FLOAT: - args[numargs].set_float(args[numargs - 1].get_float()); - numargs++; - continue; - case CODE_DUP|RET_STR: - args[numargs].set_str(ostd::move(args[numargs - 1].get_str())); - numargs++; - continue; - - case CODE_FORCE|RET_STR: - args[numargs - 1].force_str(); - continue; - case CODE_FORCE|RET_INT: - args[numargs - 1].force_int(); - continue; - case CODE_FORCE|RET_FLOAT: - args[numargs - 1].force_float(); - continue; - - case CODE_RESULT|RET_NULL: - result.cleanup(); - result = args[--numargs]; - continue; - case CODE_RESULT|RET_STR: - case CODE_RESULT|RET_INT: - case CODE_RESULT|RET_FLOAT: - result.cleanup(); - result = args[--numargs]; - force_arg(result, op & CODE_RET_MASK); - continue; - - case CODE_EMPTY|RET_NULL: - args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_NULL] + 1)); - break; - case CODE_EMPTY|RET_STR: - args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_STR] + 1)); - break; - case CODE_EMPTY|RET_INT: - args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_INT] + 1)); - break; - case CODE_EMPTY|RET_FLOAT: - args[numargs++].set_code(reinterpret_cast(emptyblock[VAL_FLOAT] + 1)); - break; - case CODE_BLOCK: { - ostd::Uint32 len = op >> 8; - args[numargs++].set_code(reinterpret_cast(code + 1)); - code += len; - continue; - } - case CODE_COMPILE: { - TaggedValue &arg = args[numargs - 1]; - GenState gs(cs); - switch (arg.get_type()) { - case VAL_INT: - gs.code.reserve(8); - gs.code.push(CODE_START); - gs.gen_int(arg.i); - gs.code.push(CODE_RESULT); - gs.code.push(CODE_EXIT); - break; - case VAL_FLOAT: - gs.code.reserve(8); - gs.code.push(CODE_START); - gs.gen_float(arg.f); - gs.code.push(CODE_RESULT); - gs.code.push(CODE_EXIT); - break; - case VAL_STR: - case VAL_MACRO: - case VAL_CSTR: - gs.code.reserve(64); - gs.gen_main(arg.s); - arg.cleanup(); - break; - default: - gs.code.reserve(8); - gs.code.push(CODE_START); - gs.gen_null(); - gs.code.push(CODE_RESULT); - gs.code.push(CODE_EXIT); - break; - } - arg.set_code(reinterpret_cast(gs.code.disown() + 1)); - continue; - } - case CODE_COND: { - TaggedValue &arg = args[numargs - 1]; - switch (arg.get_type()) { - case VAL_STR: - case VAL_MACRO: - case VAL_CSTR: - if (arg.s[0]) { - GenState gs(cs); - gs.code.reserve(64); - gs.gen_main(arg.s); - arg.cleanup(); - arg.set_code(reinterpret_cast(gs.code.disown() + 1)); - } else arg.force_null(); - break; - } - continue; - } - - case CODE_IDENT: - args[numargs++].set_ident(cs.identmap[op >> 8]); - continue; - case CODE_IDENTARG: { - Ident *id = cs.identmap[op >> 8]; - if (!(cs.stack->usedargs & (1 << id->index))) { - id->push_arg(null_value, cs.stack->argstack[id->index], false); - cs.stack->usedargs |= 1 << id->index; - } - args[numargs++].set_ident(id); - continue; - } - case CODE_IDENTU: { - TaggedValue &arg = args[numargs - 1]; - Ident *id = arg.get_type() == VAL_STR || arg.get_type() == VAL_MACRO || arg.get_type() == VAL_CSTR ? cs.new_ident(ostd::ConstCharRange(arg.cstr, arg.len)) : cs.dummy; - if (id->index < MaxArguments && !(cs.stack->usedargs & (1 << id->index))) { - id->push_arg(null_value, cs.stack->argstack[id->index], false); - cs.stack->usedargs |= 1 << id->index; - } - arg.cleanup(); - arg.set_ident(id); - continue; - } - - case CODE_LOOKUPU|RET_STR: -#define LOOKUPU(aval, sval, ival, fval, nval) { \ - TaggedValue &arg = args[numargs-1]; \ - if(arg.get_type() != VAL_STR && arg.get_type() != VAL_MACRO && arg.get_type() != VAL_CSTR) continue; \ - Ident *id = cs.idents.at(arg.s); \ - if(id) switch(id->type) \ - { \ - case ID_ALIAS: \ - if(id->flags&IDF_UNKNOWN) break; \ - arg.cleanup(); \ - if(id->index < MaxArguments && !(cs.stack->usedargs&(1<index))) { nval; continue; } \ - aval; \ - continue; \ - case ID_SVAR: arg.cleanup(); sval; continue; \ - case ID_VAR: arg.cleanup(); ival; continue; \ - case ID_FVAR: arg.cleanup(); fval; continue; \ - case ID_COMMAND: \ - { \ - arg.cleanup(); \ - arg.set_null(); \ - TaggedValue buf[MaxArguments]; \ - callcommand(cs, id, buf, arg, 0, true); \ - force_arg(arg, op&CODE_RET_MASK); \ - continue; \ - } \ - default: arg.cleanup(); nval; continue; \ - } \ - cs_debug_code(cs, "unknown alias lookup: %s", arg.s); \ - arg.cleanup(); \ - nval; \ - continue; \ - } - LOOKUPU(arg.set_str(ostd::move(id->get_str())), - arg.set_str(*id->storage.sp), - arg.set_str(ostd::move(intstr(*id->storage.ip))), - arg.set_str(ostd::move(floatstr(*id->storage.fp))), - arg.set_str("")); - case CODE_LOOKUP|RET_STR: -#define LOOKUP(aval) { \ - Ident *id = cs.identmap[op>>8]; \ - if(id->flags&IDF_UNKNOWN) cs_debug_code(cs, "unknown alias lookup: %s", id->name); \ - aval; \ - continue; \ - } - LOOKUP(args[numargs++].set_str(ostd::move(id->get_str()))); - case CODE_LOOKUPARG|RET_STR: -#define LOOKUPARG(aval, nval) { \ - Ident *id = cs.identmap[op>>8]; \ - if(!(cs.stack->usedargs&(1<index))) { nval; continue; } \ - aval; \ - continue; \ - } - LOOKUPARG(args[numargs++].set_str(ostd::move(id->get_str())), args[numargs++].set_str("")); - case CODE_LOOKUPU|RET_INT: - LOOKUPU(arg.set_int(id->get_int()), - arg.set_int(parseint(*id->storage.sp)), - arg.set_int(*id->storage.ip), - arg.set_int(int(*id->storage.fp)), - arg.set_int(0)); - case CODE_LOOKUP|RET_INT: - LOOKUP(args[numargs++].set_int(id->get_int())); - case CODE_LOOKUPARG|RET_INT: - LOOKUPARG(args[numargs++].set_int(id->get_int()), args[numargs++].set_int(0)); - case CODE_LOOKUPU|RET_FLOAT: - LOOKUPU(arg.set_float(id->get_float()), - arg.set_float(parsefloat(*id->storage.sp)), - arg.set_float(float(*id->storage.ip)), - arg.set_float(*id->storage.fp), - arg.set_float(0.0f)); - case CODE_LOOKUP|RET_FLOAT: - LOOKUP(args[numargs++].set_float(id->get_float())); - case CODE_LOOKUPARG|RET_FLOAT: - LOOKUPARG(args[numargs++].set_float(id->get_float()), args[numargs++].set_float(0.0f)); - case CODE_LOOKUPU|RET_NULL: - LOOKUPU(id->get_val(arg), - arg.set_str(*id->storage.sp), - arg.set_int(*id->storage.ip), - arg.set_float(*id->storage.fp), - arg.set_null()); - case CODE_LOOKUP|RET_NULL: - LOOKUP(id->get_val(args[numargs++])); - case CODE_LOOKUPARG|RET_NULL: - LOOKUPARG(id->get_val(args[numargs++]), args[numargs++].set_null()); - - case CODE_LOOKUPMU|RET_STR: - LOOKUPU(id->get_cstr(arg), - arg.set_cstr(*id->storage.sp), - arg.set_str(ostd::move(intstr(*id->storage.ip))), - arg.set_str(ostd::move(floatstr(*id->storage.fp))), - arg.set_cstr("")); - case CODE_LOOKUPM|RET_STR: - LOOKUP(id->get_cstr(args[numargs++])); - case CODE_LOOKUPMARG|RET_STR: - LOOKUPARG(id->get_cstr(args[numargs++]), args[numargs++].set_cstr("")); - case CODE_LOOKUPMU|RET_NULL: - LOOKUPU(id->get_cval(arg), - arg.set_cstr(*id->storage.sp), - arg.set_int(*id->storage.ip), - arg.set_float(*id->storage.fp), - arg.set_null()); - case CODE_LOOKUPM|RET_NULL: - LOOKUP(id->get_cval(args[numargs++])); - case CODE_LOOKUPMARG|RET_NULL: - LOOKUPARG(id->get_cval(args[numargs++]), args[numargs++].set_null()); - - case CODE_SVAR|RET_STR: - case CODE_SVAR|RET_NULL: - args[numargs++].set_str(*cs.identmap[op >> 8]->storage.sp); - continue; - case CODE_SVAR|RET_INT: - args[numargs++].set_int(parseint(*cs.identmap[op >> 8]->storage.sp)); - continue; - case CODE_SVAR|RET_FLOAT: - args[numargs++].set_float(parsefloat(*cs.identmap[op >> 8]->storage.sp)); - continue; - case CODE_SVARM: - args[numargs++].set_cstr(*cs.identmap[op >> 8]->storage.sp); - continue; - case CODE_SVAR1: - cs.set_var_str_checked(cs.identmap[op >> 8], args[--numargs].s); - args[numargs].cleanup(); - continue; - - case CODE_IVAR|RET_INT: - case CODE_IVAR|RET_NULL: - args[numargs++].set_int(*cs.identmap[op >> 8]->storage.ip); - continue; - case CODE_IVAR|RET_STR: - args[numargs++].set_str(ostd::move(intstr(*cs.identmap[op >> 8]->storage.ip))); - continue; - case CODE_IVAR|RET_FLOAT: - args[numargs++].set_float(float(*cs.identmap[op >> 8]->storage.ip)); - continue; - case CODE_IVAR1: - cs.set_var_int_checked(cs.identmap[op >> 8], args[--numargs].i); - continue; - case CODE_IVAR2: - numargs -= 2; - cs.set_var_int_checked(cs.identmap[op >> 8], (args[numargs].i << 16) | (args[numargs + 1].i << 8)); - continue; - case CODE_IVAR3: - numargs -= 3; - cs.set_var_int_checked(cs.identmap[op >> 8], (args[numargs].i << 16) | (args[numargs + 1].i << 8) | args[numargs + 2].i); - continue; - - case CODE_FVAR|RET_FLOAT: - case CODE_FVAR|RET_NULL: - args[numargs++].set_float(*cs.identmap[op >> 8]->storage.fp); - continue; - case CODE_FVAR|RET_STR: - args[numargs++].set_str(ostd::move(floatstr(*cs.identmap[op >> 8]->storage.fp))); - continue; - case CODE_FVAR|RET_INT: - args[numargs++].set_int(int(*cs.identmap[op >> 8]->storage.fp)); - continue; - case CODE_FVAR1: - cs.set_var_float_checked(cs.identmap[op >> 8], args[--numargs].f); - continue; - - case CODE_COM|RET_NULL: - case CODE_COM|RET_STR: - case CODE_COM|RET_FLOAT: - case CODE_COM|RET_INT: { - Ident *id = cs.identmap[op >> 8]; - int offset = numargs - id->numargs; - result.force_null(); - id->cb_cftv(TvalRange(args + offset, id->numargs), result); - force_arg(result, op & CODE_RET_MASK); - free_args(args, numargs, offset); - continue; - } - - case CODE_COMV|RET_NULL: - case CODE_COMV|RET_STR: - case CODE_COMV|RET_FLOAT: - case CODE_COMV|RET_INT: { - Ident *id = cs.identmap[op >> 13]; - int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; - result.force_null(); - id->cb_cftv(ostd::iter(&args[offset], callargs), result); - force_arg(result, op & CODE_RET_MASK); - free_args(args, numargs, offset); - continue; - } - case CODE_COMC|RET_NULL: - case CODE_COMC|RET_STR: - case CODE_COMC|RET_FLOAT: - case CODE_COMC|RET_INT: { - Ident *id = cs.identmap[op >> 13]; - int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; - result.force_null(); - { - auto buf = ostd::appender(); - cscript::util::tvals_concat(buf, ostd::iter(&args[offset], callargs), " "); - TaggedValue tv; - tv.set_mstr(buf.get().iter()); - id->cb_cftv(TvalRange(&tv, 1), result); - } - force_arg(result, op & CODE_RET_MASK); - free_args(args, numargs, offset); - continue; - } - - case CODE_CONC|RET_NULL: - case CODE_CONC|RET_STR: - case CODE_CONC|RET_FLOAT: - case CODE_CONC|RET_INT: - case CODE_CONCW|RET_NULL: - case CODE_CONCW|RET_STR: - case CODE_CONCW|RET_FLOAT: - case CODE_CONCW|RET_INT: { - int numconc = op >> 8; - auto buf = ostd::appender(); - cscript::util::tvals_concat(buf, ostd::iter(&args[numargs - numconc], numconc), ((op & CODE_OP_MASK) == CODE_CONC) ? " " : ""); - free_args(args, numargs, numargs - numconc); - args[numargs].set_mstr(buf.get().iter()); - buf.get().disown(); - force_arg(args[numargs], op & CODE_RET_MASK); - numargs++; - continue; - } - - case CODE_CONCM|RET_NULL: - case CODE_CONCM|RET_STR: - case CODE_CONCM|RET_FLOAT: - case CODE_CONCM|RET_INT: { - int numconc = op >> 8; - auto buf = ostd::appender(); - cscript::util::tvals_concat(buf, ostd::iter(&args[numargs - numconc], numconc)); - free_args(args, numargs, numargs - numconc); - result.set_mstr(buf.get().iter()); - buf.get().disown(); - force_arg(result, op & CODE_RET_MASK); - continue; - } - - case CODE_ALIAS: - cs.identmap[op >> 8]->set_alias(cs, args[--numargs]); - continue; - case CODE_ALIASARG: - cs.identmap[op >> 8]->set_arg(cs, args[--numargs]); - continue; - case CODE_ALIASU: - numargs -= 2; - cs.set_alias(args[numargs].get_str(), args[numargs + 1]); - args[numargs].cleanup(); - continue; - -#define SKIPARGS(offset) offset - case CODE_CALL|RET_NULL: - case CODE_CALL|RET_STR: - case CODE_CALL|RET_FLOAT: - case CODE_CALL|RET_INT: { -#define FORCERESULT { \ - free_args(args, numargs, SKIPARGS(offset)); \ - force_arg(result, op&CODE_RET_MASK); \ - continue; \ - } -#define CALLALIAS(cs, result) { \ - IdentStack argstack[MaxArguments]; \ - for(int i = 0; i < callargs; i++) \ - (cs).identmap[i]->push_arg(args[offset + i], argstack[i], false); \ - int oldargs = (cs).numargs; \ - (cs).numargs = callargs; \ - int oldflags = (cs).identflags; \ - (cs).identflags |= id->flags&IDF_OVERRIDDEN; \ - IdentLink aliaslink = { id, (cs).stack, (1<code) id->code = reinterpret_cast(compilecode(cs, id->get_str())); \ - ostd::Uint32 *codep = reinterpret_cast(id->code); \ - codep[0] += 0x100; \ - runcode((cs), codep+1, (result)); \ - codep[0] -= 0x100; \ - if(int(codep[0]) < 0x100) delete[] codep; \ - (cs).stack = aliaslink.next; \ - (cs).identflags = oldflags; \ - for(int i = 0; i < callargs; i++) \ - (cs).identmap[i]->pop_arg(); \ - for(int argmask = aliaslink.usedargs&(~0<pop_arg(); argmask &= ~(1<> 13]; - int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; - if (id->flags & IDF_UNKNOWN) { - cs_debug_code(cs, "unknown command: %s", id->name); - FORCERESULT; - } - CALLALIAS(cs, result); - continue; - } - case CODE_CALLARG|RET_NULL: - case CODE_CALLARG|RET_STR: - case CODE_CALLARG|RET_FLOAT: - case CODE_CALLARG|RET_INT: { - result.force_null(); - Ident *id = cs.identmap[op >> 13]; - int callargs = (op >> 8) & 0x1F, offset = numargs - callargs; - if (!(cs.stack->usedargs & (1 << id->index))) FORCERESULT; - CALLALIAS(cs, result); - continue; - } -#undef SKIPARGS - -#define SKIPARGS(offset) offset-1 - case CODE_CALLU|RET_NULL: - case CODE_CALLU|RET_STR: - case CODE_CALLU|RET_FLOAT: - case CODE_CALLU|RET_INT: { - int callargs = op >> 8, offset = numargs - callargs; - TaggedValue &idarg = args[offset - 1]; - if (idarg.get_type() != VAL_STR && idarg.get_type() != VAL_MACRO && idarg.get_type() != VAL_CSTR) { -litval: - result.cleanup(); - result = idarg; - force_arg(result, op & CODE_RET_MASK); - while (--numargs >= offset) args[numargs].cleanup(); - continue; - } - Ident *id = cs.idents.at(idarg.s); - if (!id) { -noid: - if (cs_check_num(idarg.s)) goto litval; - cs_debug_code(cs, "unknown command: %s", idarg.s); - result.force_null(); - FORCERESULT; - } - result.force_null(); - switch (id->type) { - default: - if (!id->cb_cftv) FORCERESULT; - /* fallthrough */ - case ID_COMMAND: - idarg.cleanup(); - callcommand(cs, id, &args[offset], result, callargs); - force_arg(result, op & CODE_RET_MASK); - numargs = offset - 1; - continue; - case ID_LOCAL: { - IdentStack locals[MaxArguments]; - idarg.cleanup(); - for (ostd::Size j = 0; j < ostd::Size(callargs); ++j) cs.force_ident(args[offset + j])->push_alias(locals[j]); - code = runcode(cs, code, result); - for (ostd::Size j = 0; j < ostd::Size(callargs); ++j) args[offset + j].id->pop_alias(); - goto exit; - } - case ID_VAR: - if (callargs <= 0) cs.print_var(id); - else cs.set_var_int_checked(id, ostd::iter(&args[offset], callargs)); - FORCERESULT; - case ID_FVAR: - if (callargs <= 0) cs.print_var(id); - else cs.set_var_float_checked(id, args[offset].force_float()); - FORCERESULT; - case ID_SVAR: - if (callargs <= 0) cs.print_var(id); - else cs.set_var_str_checked(id, args[offset].force_str()); - FORCERESULT; - case ID_ALIAS: - if (id->index < MaxArguments && !(cs.stack->usedargs & (1 << id->index))) FORCERESULT; - if (id->get_valtype() == VAL_NULL) goto noid; - idarg.cleanup(); - CALLALIAS(cs, result); - continue; - } - } -#undef SKIPARGS - } - } -exit: - --rundepth; - return code; -} - -void CsState::run_ret(Bytecode const *code, TaggedValue &ret) { - runcode(*this, reinterpret_cast(code), ret); -} - -void CsState::run_ret(ostd::ConstCharRange code, TaggedValue &ret) { - GenState gs(*this); - gs.code.reserve(64); - /* FIXME range */ - gs.gen_main(code.data(), VAL_ANY); - runcode(*this, gs.code.data() + 1, ret); - if (int(gs.code[0]) >= 0x100) - gs.code.disown(); -} - -/* TODO */ -void CsState::run_ret(Ident *id, TvalRange args, TaggedValue &ret) { - int nargs = int(args.size()); - ret.set_null(); - ++rundepth; - if (rundepth > MaxRunDepth) cs_debug_code(*this, "exceeded recursion limit"); - else if (id) switch (id->type) { - default: - if (!id->cb_cftv) break; - /* fallthrough */ - case ID_COMMAND: - if (nargs < id->numargs) { - TaggedValue buf[MaxArguments]; - memcpy(buf, args.data(), args.size() * sizeof(TaggedValue)); - callcommand(*this, id, buf, ret, nargs, false); - } else callcommand(*this, id, args.data(), ret, nargs, false); - nargs = 0; - break; - case ID_VAR: - if (args.empty()) print_var(id); - else set_var_int_checked(id, args); - break; - case ID_FVAR: - if (args.empty()) print_var(id); - else set_var_float_checked(id, args[0].force_float()); - break; - case ID_SVAR: - if (args.empty()) print_var(id); - else set_var_str_checked(id, args[0].force_str()); - break; - case ID_ALIAS: - if (id->index < MaxArguments && !(stack->usedargs & (1 << id->index))) break; - if (id->get_valtype() == VAL_NULL) break; -#define callargs nargs -#define offset 0 -#define op RET_NULL -#define SKIPARGS(offset) offset - CALLALIAS(*this, ret); -#undef callargs -#undef offset -#undef op -#undef SKIPARGS - break; - } - free_args(args.data(), nargs, 0); - --rundepth; -} - -ostd::String CsState::run_str(Bytecode const *code) { - TaggedValue ret; - run_ret(code, ret); - ostd::String s = ret.get_str(); - ret.cleanup(); - return s; -} - -ostd::String CsState::run_str(ostd::ConstCharRange code) { - TaggedValue ret; - run_ret(code, ret); - ostd::String s = ret.get_str(); - ret.cleanup(); - return s; -} - -ostd::String CsState::run_str(Ident *id, TvalRange args) { - TaggedValue ret; - run_ret(id, args, ret); - ostd::String s = ret.get_str(); - ret.cleanup(); - return s; -} - -int CsState::run_int(Bytecode const *code) { - TaggedValue ret; - run_ret(code, ret); - int i = ret.get_int(); - ret.cleanup(); - return i; -} - -int CsState::run_int(ostd::ConstCharRange code) { - TaggedValue ret; - run_ret(code, ret); - int i = ret.get_int(); - ret.cleanup(); - return i; -} - -int CsState::run_int(Ident *id, TvalRange args) { - TaggedValue ret; - run_ret(id, args, ret); - int i = ret.get_int(); - ret.cleanup(); - return i; -} - -float CsState::run_float(Bytecode const *code) { - TaggedValue ret; - run_ret(code, ret); - float f = ret.get_float(); - ret.cleanup(); - return f; -} - -float CsState::run_float(ostd::ConstCharRange code) { - TaggedValue ret; - run_ret(code, ret); - float f = ret.get_float(); - ret.cleanup(); - return f; -} - -float CsState::run_float(Ident *id, TvalRange args) { - TaggedValue ret; - run_ret(id, args, ret); - float f = ret.get_float(); - ret.cleanup(); - return f; -} - -bool CsState::run_bool(Bytecode const *code) { - TaggedValue ret; - run_ret(code, ret); - bool b = ret.get_bool(); - ret.cleanup(); - return b; -} - -bool CsState::run_bool(ostd::ConstCharRange code) { - TaggedValue ret; - run_ret(code, ret); - bool b = ret.get_bool(); - ret.cleanup(); - return b; -} - -bool CsState::run_bool(Ident *id, TvalRange args) { - TaggedValue ret; - run_ret(id, args, ret); - bool b = ret.get_bool(); - ret.cleanup(); - return b; -} - -bool CsState::run_file(ostd::ConstCharRange fname) { - ostd::ConstCharRange oldsrcfile = src_file, oldsrcstr = src_str; - char *buf = nullptr; - ostd::Size len; - - ostd::FileStream f(fname, ostd::StreamMode::read); - if (!f.is_open()) - return false; - - len = f.size(); - buf = new char[len + 1]; - if (f.get(buf, len) != len) { - delete[] buf; - return false; - } - buf[len] = '\0'; - - src_file = fname; - src_str = ostd::ConstCharRange(buf, len); - run_int(buf); - src_file = oldsrcfile; - src_str = oldsrcstr; - delete[] buf; - return true; -} - void cs_init_lib_io(CsState &cs) { cs_add_command(cs, "exec", "sb", [&cs](TvalRange args, TaggedValue &res) { auto file = args[0].get_strr();