2015-08-08 16:13:19 +00:00
|
|
|
#include "cubescript.hh"
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include <ostd/algorithm.hh>
|
|
|
|
#include <ostd/format.hh>
|
2015-08-12 17:50:02 +00:00
|
|
|
#include <ostd/array.hh>
|
2015-08-05 21:58:45 +00:00
|
|
|
|
2015-08-08 15:13:46 +00:00
|
|
|
namespace cscript {
|
|
|
|
|
2016-08-06 16:33:01 +00:00
|
|
|
static constexpr int MaxArguments = 25;
|
|
|
|
static constexpr int MaxResults = 7;
|
|
|
|
|
2016-08-10 17:33:43 +00:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2016-08-06 16:33:01 +00:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline int parseint(char const *s) {
|
2015-08-11 21:01:56 +00:00
|
|
|
return int(strtoul(s, nullptr, 0));
|
|
|
|
}
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
int cs_parse_int(ostd::ConstCharRange s) {
|
2015-08-12 01:54:13 +00:00
|
|
|
if (s.empty()) return 0;
|
|
|
|
return parseint(s.data());
|
|
|
|
}
|
|
|
|
|
2016-07-25 23:42:34 +00:00
|
|
|
static inline float parsefloat(char const *s) {
|
2015-08-11 21:01:56 +00:00
|
|
|
/* not all platforms (windows) can parse hexadecimal integers via strtod */
|
|
|
|
char *end;
|
|
|
|
double val = strtod(s, &end);
|
|
|
|
return val || end==s || (*end!='x' && *end!='X') ? float(val) : float(parseint(s));
|
|
|
|
}
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
float cs_parse_float(ostd::ConstCharRange s) {
|
2015-08-12 01:54:13 +00:00
|
|
|
if (s.empty()) return 0.0f;
|
|
|
|
return parsefloat(s.data());
|
|
|
|
}
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
ostd::String intstr(int v) {
|
|
|
|
char buf[256];
|
2016-08-08 21:05:29 +00:00
|
|
|
snprintf(buf, sizeof(buf), "%d", v);
|
2016-07-13 18:24:26 +00:00
|
|
|
return buf;
|
2015-08-11 21:01:56 +00:00
|
|
|
}
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
ostd::String floatstr(float v) {
|
|
|
|
char buf[256];
|
2016-08-08 21:05:29 +00:00
|
|
|
snprintf(buf, sizeof(buf), v == int(v) ? "%.1f" : "%.7g", v);
|
2016-07-13 18:24:26 +00:00
|
|
|
return buf;
|
2015-08-11 21:01:56 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
char *cs_dup_ostr(ostd::ConstCharRange s) {
|
2015-08-11 21:16:20 +00:00
|
|
|
char *r = new char[s.size() + 1];
|
|
|
|
memcpy(r, s.data(), s.size());
|
|
|
|
r[s.size()] = 0;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-08-11 20:41:12 +00:00
|
|
|
static inline bool cs_check_num(ostd::ConstCharRange s) {
|
2015-08-06 22:16:02 +00:00
|
|
|
if (isdigit(s[0]))
|
|
|
|
return true;
|
|
|
|
switch (s[0]) {
|
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
return isdigit(s[1]) || (s[1] == '.' && isdigit(s[2]));
|
|
|
|
case '.':
|
|
|
|
return isdigit(s[1]) != 0;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(): type(ID_UNKNOWN) {}
|
|
|
|
|
2015-08-11 21:16:20 +00:00
|
|
|
/* ID_VAR */
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, int m, int x, int *s,
|
2016-08-01 00:10:21 +00:00
|
|
|
VarCb f, int flagsv)
|
2016-08-10 17:33:43 +00:00
|
|
|
: type(ID_VAR), flags(flagsv | (m > x ? IDF_READONLY : 0)), name(n),
|
2016-08-01 18:17:13 +00:00
|
|
|
minval(m), maxval(x), cb_var(ostd::move(f)) {
|
2015-08-13 20:08:57 +00:00
|
|
|
storage.ip = s;
|
2015-08-11 21:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ID_FVAR */
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, float m, float x, float *s,
|
2016-08-01 00:10:21 +00:00
|
|
|
VarCb f, int flagsv)
|
2016-08-10 17:33:43 +00:00
|
|
|
: type(ID_FVAR), flags(flagsv | (m > x ? IDF_READONLY : 0)), name(n),
|
2016-08-01 18:17:13 +00:00
|
|
|
minvalf(m), maxvalf(x), cb_var(ostd::move(f)) {
|
2015-08-13 20:08:57 +00:00
|
|
|
storage.fp = s;
|
2015-08-11 21:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ID_SVAR */
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, char **s, VarCb f, int flagsv)
|
|
|
|
: type(ID_SVAR), flags(flagsv), name(n), cb_var(ostd::move(f)) {
|
2015-08-13 20:08:57 +00:00
|
|
|
storage.sp = s;
|
2015-08-11 21:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ID_ALIAS */
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, char *a, int flagsv)
|
|
|
|
: type(ID_ALIAS), valtype(VAL_STR), flags(flagsv), name(n), code(nullptr),
|
2015-08-11 21:16:20 +00:00
|
|
|
stack(nullptr) {
|
|
|
|
val.s = a;
|
2016-08-01 20:46:50 +00:00
|
|
|
val.len = strlen(a);
|
2015-08-11 21:16:20 +00:00
|
|
|
}
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, int a, int flagsv)
|
|
|
|
: type(ID_ALIAS), valtype(VAL_INT), flags(flagsv), name(n), code(nullptr),
|
2015-08-11 21:16:20 +00:00
|
|
|
stack(nullptr) {
|
|
|
|
val.i = a;
|
|
|
|
}
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, float a, int flagsv)
|
|
|
|
: type(ID_ALIAS), valtype(VAL_FLOAT), flags(flagsv), name(n), code(nullptr),
|
2015-08-11 21:16:20 +00:00
|
|
|
stack(nullptr) {
|
|
|
|
val.f = a;
|
|
|
|
}
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, int flagsv)
|
|
|
|
: type(ID_ALIAS), valtype(VAL_NULL), flags(flagsv), name(n), code(nullptr),
|
2015-08-11 21:16:20 +00:00
|
|
|
stack(nullptr) {
|
|
|
|
}
|
2016-08-10 17:33:43 +00:00
|
|
|
Ident::Ident(ostd::ConstCharRange n, TaggedValue const &v, int flagsv)
|
|
|
|
: type(ID_ALIAS), valtype(v.p_type), flags(flagsv), name(n), code(nullptr),
|
2015-08-11 21:16:20 +00:00
|
|
|
stack(nullptr) {
|
|
|
|
val = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ID_COMMAND */
|
|
|
|
Ident::Ident(int t, ostd::ConstCharRange n, ostd::ConstCharRange args,
|
2016-08-10 17:33:43 +00:00
|
|
|
ostd::Uint32 argmask, int numargs, CmdFunc f)
|
|
|
|
: type(t), numargs(numargs), flags(0), name(n),
|
2016-08-09 23:54:51 +00:00
|
|
|
cargs(!args.empty() ? cs_dup_ostr(args) : nullptr),
|
2016-08-01 18:17:13 +00:00
|
|
|
argmask(argmask), cb_cftv(ostd::move(f)) {
|
2015-08-11 21:16:20 +00:00
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
struct NullValue: TaggedValue {
|
2015-08-05 21:58:45 +00:00
|
|
|
NullValue() { set_null(); }
|
2016-07-17 17:50:40 +00:00
|
|
|
} const null_value;
|
2015-08-05 21:58:45 +00:00
|
|
|
|
|
|
|
static TaggedValue no_ret = null_value;
|
|
|
|
|
2016-08-06 15:34:10 +00:00
|
|
|
void cs_init_lib_base(CsState &cs);
|
|
|
|
|
2016-08-11 17:21:18 +00:00
|
|
|
CsState::CsState() {
|
2016-07-27 17:52:01 +00:00
|
|
|
noalias.id = nullptr;
|
|
|
|
noalias.next = nullptr;
|
|
|
|
noalias.usedargs = (1 << MaxArguments) - 1;
|
|
|
|
noalias.argstack = nullptr;
|
2016-02-06 23:17:28 +00:00
|
|
|
for (int i = 0; i < MaxArguments; ++i) {
|
2015-08-05 21:58:45 +00:00
|
|
|
char buf[32];
|
|
|
|
snprintf(buf, sizeof(buf), "arg%d", i + 1);
|
2016-07-17 17:50:40 +00:00
|
|
|
new_ident(static_cast<char const *>(buf), IDF_ARG);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2015-08-13 18:51:15 +00:00
|
|
|
dummy = new_ident("//dummy");
|
2016-08-10 17:33:43 +00:00
|
|
|
add_ident("numargs", MaxArguments, 0, &numargs);
|
|
|
|
add_ident("dbgalias", 0, 1000, &dbgalias);
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_init_lib_base(*this);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CsState::~CsState() {
|
|
|
|
for (Ident &i: idents.iter()) {
|
|
|
|
if (i.type == ID_ALIAS) {
|
2015-08-11 20:41:12 +00:00
|
|
|
i.force_null();
|
2016-08-06 18:12:38 +00:00
|
|
|
delete[] reinterpret_cast<ostd::Uint32 *>(i.code);
|
2015-08-05 21:58:45 +00:00
|
|
|
i.code = nullptr;
|
|
|
|
} else if (i.type == ID_COMMAND || i.type >= ID_LOCAL) {
|
2016-08-09 23:54:51 +00:00
|
|
|
delete[] i.cargs;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-12 17:50:02 +00:00
|
|
|
ostd::ConstCharRange cs_debug_line(CsState &cs,
|
|
|
|
ostd::ConstCharRange p,
|
|
|
|
ostd::ConstCharRange fmt,
|
|
|
|
ostd::CharRange buf) {
|
|
|
|
if (cs.src_str.empty()) return fmt;
|
|
|
|
ostd::Size num = 1;
|
|
|
|
ostd::ConstCharRange line(cs.src_str);
|
|
|
|
for (;;) {
|
|
|
|
ostd::ConstCharRange end = ostd::find(line, '\n');
|
|
|
|
if (!end.empty())
|
2015-08-13 22:26:04 +00:00
|
|
|
line = ostd::slice_until(line, end);
|
2015-08-12 17:50:02 +00:00
|
|
|
if (&p[0] >= &line[0] && &p[0] <= &line[line.size()]) {
|
|
|
|
ostd::CharRange r(buf);
|
|
|
|
if (!cs.src_file.empty())
|
|
|
|
ostd::format(r, "%s:%d: %s", cs.src_file, num, fmt);
|
|
|
|
else
|
|
|
|
ostd::format(r, "%d: %s", num, fmt);
|
|
|
|
r.put('\0');
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
if (end.empty()) break;
|
|
|
|
line = end;
|
|
|
|
line.pop_front();
|
|
|
|
++num;
|
|
|
|
}
|
|
|
|
return fmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cs_debug_alias(CsState &cs) {
|
|
|
|
if (!cs.dbgalias) return;
|
|
|
|
int total = 0, depth = 0;
|
|
|
|
for (IdentLink *l = cs.stack; l != &cs.noalias; l = l->next) total++;
|
|
|
|
for (IdentLink *l = cs.stack; l != &cs.noalias; l = l->next) {
|
|
|
|
Ident *id = l->id;
|
|
|
|
++depth;
|
|
|
|
if (depth < cs.dbgalias)
|
|
|
|
ostd::err.writefln(" %d) %s", total - depth + 1, id->name);
|
|
|
|
else if (l->next == &cs.noalias)
|
|
|
|
ostd::err.writefln(depth == cs.dbgalias ? " %d) %s"
|
|
|
|
: " ..%d) %s",
|
|
|
|
total - depth + 1, id->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ...A>
|
|
|
|
void cs_debug_code(CsState &cs, ostd::ConstCharRange fmt, A &&...args) {
|
|
|
|
if (cs.nodebug) return;
|
|
|
|
ostd::err.writefln(fmt, ostd::forward<A>(args)...);
|
|
|
|
cs_debug_alias(cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ...A>
|
|
|
|
void cs_debug_code_line(CsState &cs, ostd::ConstCharRange p,
|
|
|
|
ostd::ConstCharRange fmt, A &&...args) {
|
|
|
|
if (cs.nodebug) return;
|
|
|
|
ostd::Array<char, 256> buf;
|
|
|
|
ostd::err.writefln(cs_debug_line(cs, p, fmt, ostd::CharRange(buf.data(),
|
|
|
|
buf.size())),
|
|
|
|
ostd::forward<A>(args)...);
|
|
|
|
cs_debug_alias(cs);
|
|
|
|
}
|
|
|
|
|
2015-08-05 21:58:45 +00:00
|
|
|
void CsState::clear_override(Ident &id) {
|
|
|
|
if (!(id.flags & IDF_OVERRIDDEN)) return;
|
|
|
|
switch (id.type) {
|
|
|
|
case ID_ALIAS:
|
2015-08-13 21:23:46 +00:00
|
|
|
if (id.get_valtype() == VAL_STR) {
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!id.val.s[0]) break;
|
|
|
|
delete[] id.val.s;
|
|
|
|
}
|
|
|
|
id.clean_code();
|
|
|
|
id.valtype = VAL_STR;
|
2015-08-11 21:16:20 +00:00
|
|
|
id.val.s = cs_dup_ostr("");
|
2016-08-01 20:46:50 +00:00
|
|
|
id.val.len = 0;
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_VAR:
|
2015-08-13 20:08:57 +00:00
|
|
|
*id.storage.ip = id.overrideval.i;
|
2016-08-01 18:33:40 +00:00
|
|
|
id.changed();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_FVAR:
|
2015-08-13 20:08:57 +00:00
|
|
|
*id.storage.fp = id.overrideval.f;
|
2016-08-01 18:33:40 +00:00
|
|
|
id.changed();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_SVAR:
|
2015-08-13 20:08:57 +00:00
|
|
|
delete[] *id.storage.sp;
|
|
|
|
*id.storage.sp = id.overrideval.s;
|
2016-08-01 18:33:40 +00:00
|
|
|
id.changed();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
id.flags &= ~IDF_OVERRIDDEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::clear_overrides() {
|
|
|
|
for (Ident &id: idents.iter())
|
|
|
|
clear_override(id);
|
|
|
|
}
|
|
|
|
|
2015-08-06 22:16:02 +00:00
|
|
|
Ident *CsState::new_ident(ostd::ConstCharRange name, int flags) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id) {
|
2015-08-11 20:41:12 +00:00
|
|
|
if (cs_check_num(name)) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "number %s is not a valid identifier name",
|
|
|
|
name);
|
2015-08-06 22:16:02 +00:00
|
|
|
return dummy;
|
|
|
|
}
|
2016-08-10 17:33:43 +00:00
|
|
|
id = add_ident(name, flags);
|
2015-08-06 22:16:02 +00:00
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ident *CsState::force_ident(TaggedValue &v) {
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (v.get_type()) {
|
2015-08-06 22:16:02 +00:00
|
|
|
case VAL_IDENT:
|
|
|
|
return v.id;
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR: {
|
2015-08-13 18:51:15 +00:00
|
|
|
Ident *id = new_ident(v.s);
|
2015-08-06 22:16:02 +00:00
|
|
|
v.set_ident(id);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
case VAL_STR: {
|
2015-08-13 18:51:15 +00:00
|
|
|
Ident *id = new_ident(v.s);
|
2015-08-06 22:16:02 +00:00
|
|
|
delete[] v.s;
|
|
|
|
v.set_ident(id);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v.cleanup();
|
|
|
|
v.set_ident(dummy);
|
|
|
|
return dummy;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CsState::reset_var(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id) return false;
|
|
|
|
if (id->flags & IDF_READONLY) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "variable %s is read only", id->name);
|
2015-08-06 22:16:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
clear_override(*id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::touch_var(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (id) switch (id->type) {
|
|
|
|
case ID_VAR:
|
|
|
|
case ID_FVAR:
|
|
|
|
case ID_SVAR:
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-06 22:16:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-06 22:38:22 +00:00
|
|
|
void CsState::set_alias(ostd::ConstCharRange name, TaggedValue &v) {
|
2015-08-06 23:09:20 +00:00
|
|
|
Ident *id = idents.at(name);
|
2015-08-06 22:38:22 +00:00
|
|
|
if (id) {
|
|
|
|
switch (id->type) {
|
|
|
|
case ID_ALIAS:
|
2016-02-06 23:17:28 +00:00
|
|
|
if (id->index < MaxArguments)
|
2015-08-11 21:16:20 +00:00
|
|
|
id->set_arg(*this, v);
|
|
|
|
else
|
|
|
|
id->set_alias(*this, v);
|
2015-08-06 22:38:22 +00:00
|
|
|
return;
|
|
|
|
case ID_VAR:
|
2015-08-07 01:44:51 +00:00
|
|
|
set_var_int_checked(id, v.get_int());
|
2015-08-06 22:38:22 +00:00
|
|
|
break;
|
|
|
|
case ID_FVAR:
|
2015-08-07 01:44:51 +00:00
|
|
|
set_var_float_checked(id, v.get_float());
|
2015-08-06 22:38:22 +00:00
|
|
|
break;
|
|
|
|
case ID_SVAR:
|
2015-08-07 01:44:51 +00:00
|
|
|
set_var_str_checked(id, v.get_str());
|
2015-08-06 22:38:22 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "cannot redefine builtin %s with an alias",
|
|
|
|
id->name);
|
2015-08-06 22:38:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
v.cleanup();
|
2015-08-11 20:41:12 +00:00
|
|
|
} else if (cs_check_num(name)) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "cannot alias number %s", name);
|
2015-08-06 22:38:22 +00:00
|
|
|
v.cleanup();
|
|
|
|
} else {
|
2016-08-10 17:33:43 +00:00
|
|
|
add_ident(name, v, identflags);
|
2015-08-06 22:38:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-07 20:38:57 +00:00
|
|
|
void CsState::print_var_int(Ident *id, int i) {
|
|
|
|
if (i < 0) {
|
|
|
|
writefln("%s = %d", id->name, i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (id->flags & IDF_HEX) {
|
|
|
|
if (id->maxval == 0xFFFFFF)
|
|
|
|
writefln("%s = 0x%.6X (%d, %d, %d)", id->name,
|
|
|
|
i, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
|
|
|
|
else
|
|
|
|
writefln("%s = 0x%X", id->name, i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
writefln("%s = %d", id->name, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::print_var_float(Ident *id, float f) {
|
|
|
|
writefln("%s = %s", id->name, floatstr(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::print_var_str(Ident *id, ostd::ConstCharRange s) {
|
|
|
|
if (ostd::find(s, '"').empty())
|
|
|
|
writefln("%s = \"%s\"", id->name, s);
|
|
|
|
else
|
|
|
|
writefln("%s = [%s]", id->name, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::print_var(Ident *id) {
|
|
|
|
switch (id->type) {
|
|
|
|
case ID_VAR:
|
2015-08-13 20:08:57 +00:00
|
|
|
print_var_int(id, *id->storage.ip);
|
2015-08-07 20:38:57 +00:00
|
|
|
break;
|
|
|
|
case ID_FVAR:
|
2015-08-13 20:08:57 +00:00
|
|
|
print_var_float(id, *id->storage.fp);
|
2015-08-07 20:38:57 +00:00
|
|
|
break;
|
|
|
|
case ID_SVAR:
|
2015-08-13 20:08:57 +00:00
|
|
|
print_var_str(id, *id->storage.sp);
|
2015-08-07 20:38:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
void TaggedValue::cleanup() {
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_STR:
|
|
|
|
delete[] s;
|
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
2016-08-06 18:12:38 +00:00
|
|
|
ostd::Uint32 *bcode = const_cast<ostd::Uint32 *>(
|
|
|
|
reinterpret_cast<ostd::Uint32 const *>(code)
|
|
|
|
);
|
|
|
|
if (bcode[-1] == CODE_START) {
|
|
|
|
delete[] bcode;
|
2016-07-17 17:50:40 +00:00
|
|
|
}
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 22:03:44 +00:00
|
|
|
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: {
|
2016-08-06 18:12:38 +00:00
|
|
|
ostd::Uint32 const *bcode = reinterpret_cast<ostd::Uint32 const *>(code);
|
|
|
|
ostd::Uint32 const *end = skipcode(bcode);
|
|
|
|
ostd::Uint32 *dst = new ostd::Uint32[end - bcode + 1];
|
2016-08-03 22:03:44 +00:00
|
|
|
*dst++ = CODE_START;
|
2016-08-06 18:12:38 +00:00
|
|
|
memcpy(dst, bcode, (end - bcode) * sizeof(ostd::Uint32));
|
|
|
|
r.set_code(reinterpret_cast<Bytecode const *>(dst));
|
2016-08-03 22:03:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
r.set_null();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-06 18:51:32 +00:00
|
|
|
/* XXX: nasty */
|
|
|
|
struct InternalTval: IdentValue {
|
|
|
|
int type;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void cs_set_macro(
|
|
|
|
TaggedValue &tv, Bytecode const *val, ostd::Size len
|
|
|
|
) {
|
|
|
|
InternalTval &itv = reinterpret_cast<InternalTval &>(tv);
|
|
|
|
itv.type = VAL_MACRO;
|
|
|
|
itv.len = len;
|
|
|
|
itv.code = val;
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
void TaggedValue::force_null() {
|
2015-08-13 20:48:03 +00:00
|
|
|
if (get_type() == VAL_NULL) return;
|
2015-08-05 21:58:45 +00:00
|
|
|
cleanup();
|
|
|
|
set_null();
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
float TaggedValue::force_float() {
|
2015-08-05 21:58:45 +00:00
|
|
|
float rf = 0.0f;
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_INT:
|
|
|
|
rf = i;
|
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
|
|
|
rf = parsefloat(s);
|
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
cleanup();
|
|
|
|
set_float(rf);
|
|
|
|
return rf;
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
int TaggedValue::force_int() {
|
2015-08-05 21:58:45 +00:00
|
|
|
int ri = 0;
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_FLOAT:
|
|
|
|
ri = f;
|
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
|
|
|
ri = parseint(s);
|
|
|
|
break;
|
|
|
|
case VAL_INT:
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
cleanup();
|
|
|
|
set_int(ri);
|
|
|
|
return ri;
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
ostd::ConstCharRange TaggedValue::force_str() {
|
2016-07-13 18:24:26 +00:00
|
|
|
ostd::String rs;
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_FLOAT:
|
2016-07-13 18:24:26 +00:00
|
|
|
rs = ostd::move(floatstr(f));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_INT:
|
2016-07-13 18:24:26 +00:00
|
|
|
rs = ostd::move(intstr(i));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
2016-08-01 20:46:50 +00:00
|
|
|
rs = ostd::ConstCharRange(s, len);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
cleanup();
|
2016-07-13 18:24:26 +00:00
|
|
|
set_str(ostd::move(rs));
|
2015-08-11 21:47:25 +00:00
|
|
|
return s;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-01 20:35:42 +00:00
|
|
|
static inline void force_arg(TaggedValue &v, int type) {
|
2016-08-11 16:21:26 +00:00
|
|
|
switch (type) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case RET_STR:
|
2016-08-11 16:21:26 +00:00
|
|
|
if (v.get_type() != VAL_STR) v.force_str();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case RET_INT:
|
2016-08-11 16:21:26 +00:00
|
|
|
if (v.get_type() != VAL_INT) v.force_int();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case RET_FLOAT:
|
2016-08-11 16:21:26 +00:00
|
|
|
if (v.get_type() != VAL_FLOAT) v.force_float();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline int cs_get_int(IdentValue const &v, int type) {
|
2015-08-11 20:41:12 +00:00
|
|
|
switch (type) {
|
|
|
|
case VAL_FLOAT:
|
|
|
|
return int(v.f);
|
|
|
|
case VAL_INT:
|
|
|
|
return v.i;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
|
|
|
return parseint(v.s);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
int TaggedValue::get_int() const {
|
2015-08-13 20:48:03 +00:00
|
|
|
return cs_get_int(*this, get_type());
|
2015-08-11 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
int Ident::get_int() const {
|
2015-08-13 21:23:46 +00:00
|
|
|
return cs_get_int(val, get_valtype());
|
2015-08-11 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline float cs_get_float(IdentValue const &v, int type) {
|
2015-08-11 20:41:12 +00:00
|
|
|
switch (type) {
|
|
|
|
case VAL_FLOAT:
|
|
|
|
return v.f;
|
|
|
|
case VAL_INT:
|
|
|
|
return float(v.i);
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
|
|
|
return parsefloat(v.s);
|
|
|
|
}
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
float TaggedValue::get_float() const {
|
2015-08-13 20:48:03 +00:00
|
|
|
return cs_get_float(*this, get_type());
|
2015-08-11 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
float Ident::get_float() const {
|
2015-08-13 21:23:46 +00:00
|
|
|
return cs_get_float(val, get_valtype());
|
2015-08-11 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
Bytecode *TaggedValue::get_code() const {
|
2016-07-30 20:42:49 +00:00
|
|
|
if (get_type() != VAL_CODE)
|
|
|
|
return nullptr;
|
2016-08-06 18:12:38 +00:00
|
|
|
return const_cast<Bytecode *>(code);
|
2016-07-30 20:42:49 +00:00
|
|
|
}
|
|
|
|
|
2016-08-01 20:35:42 +00:00
|
|
|
Ident *TaggedValue::get_ident() const {
|
|
|
|
if (get_type() != VAL_IDENT)
|
|
|
|
return nullptr;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2016-08-01 20:46:50 +00:00
|
|
|
static inline ostd::String cs_get_str(IdentValue const &v, int type) {
|
2015-08-11 21:01:56 +00:00
|
|
|
switch (type) {
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
2016-08-01 20:46:50 +00:00
|
|
|
return ostd::ConstCharRange(v.s, v.len);
|
2015-08-11 21:01:56 +00:00
|
|
|
case VAL_INT:
|
|
|
|
return intstr(v.i);
|
|
|
|
case VAL_FLOAT:
|
|
|
|
return floatstr(v.f);
|
|
|
|
}
|
2016-07-13 18:24:26 +00:00
|
|
|
return ostd::String("");
|
2015-08-11 21:01:56 +00:00
|
|
|
}
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
ostd::String TaggedValue::get_str() const {
|
2016-08-01 20:46:50 +00:00
|
|
|
return cs_get_str(*this, get_type());
|
2015-08-11 21:01:56 +00:00
|
|
|
}
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
ostd::String Ident::get_str() const {
|
2016-08-01 20:46:50 +00:00
|
|
|
return cs_get_str(val, get_valtype());
|
2015-08-11 21:01:56 +00:00
|
|
|
}
|
|
|
|
|
2016-08-01 20:46:50 +00:00
|
|
|
static inline ostd::ConstCharRange cs_get_strr(IdentValue const &v, int type) {
|
2016-07-30 23:49:32 +00:00
|
|
|
switch (type) {
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
2016-08-01 20:46:50 +00:00
|
|
|
return ostd::ConstCharRange(v.s, v.len);
|
2016-07-30 23:49:32 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ostd::ConstCharRange();
|
|
|
|
}
|
|
|
|
|
|
|
|
ostd::ConstCharRange TaggedValue::get_strr() const {
|
2016-08-01 20:46:50 +00:00
|
|
|
return cs_get_strr(*this, get_type());
|
2016-07-30 23:49:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ostd::ConstCharRange Ident::get_strr() const {
|
2016-08-01 20:46:50 +00:00
|
|
|
return cs_get_strr(val, get_valtype());
|
2016-07-30 23:49:32 +00:00
|
|
|
}
|
|
|
|
|
2016-08-01 20:46:50 +00:00
|
|
|
static inline void cs_get_val(IdentValue const &v, int type, TaggedValue &r) {
|
2015-08-11 20:41:12 +00:00
|
|
|
switch (type) {
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
2015-08-13 21:46:03 +00:00
|
|
|
case VAL_CSTR: {
|
2016-08-01 20:46:50 +00:00
|
|
|
r.set_str(ostd::ConstCharRange(v.s, v.len));
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
2015-08-13 21:46:03 +00:00
|
|
|
}
|
2015-08-11 20:41:12 +00:00
|
|
|
case VAL_INT:
|
|
|
|
r.set_int(v.i);
|
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
|
|
|
r.set_float(v.f);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
r.set_null();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
void TaggedValue::get_val(TaggedValue &r) const {
|
2016-08-01 20:46:50 +00:00
|
|
|
cs_get_val(*this, get_type(), r);
|
2015-08-11 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
void Ident::get_val(TaggedValue &r) const {
|
2016-08-01 20:46:50 +00:00
|
|
|
cs_get_val(val, get_valtype(), r);
|
2015-08-11 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
void Ident::get_cstr(TaggedValue &v) const {
|
2015-08-13 21:23:46 +00:00
|
|
|
switch (get_valtype()) {
|
2015-08-11 20:41:12 +00:00
|
|
|
case VAL_MACRO:
|
2016-08-06 18:51:32 +00:00
|
|
|
cs_set_macro(v, val.code, val.len);
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_CSTR:
|
2016-08-01 20:46:50 +00:00
|
|
|
v.set_cstr(ostd::ConstCharRange(val.s, val.len));
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
|
|
|
case VAL_INT:
|
2016-07-13 18:24:26 +00:00
|
|
|
v.set_str(ostd::move(intstr(val.i)));
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
2016-07-13 18:24:26 +00:00
|
|
|
v.set_str(ostd::move(floatstr(val.f)));
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
v.set_cstr("");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:02:38 +00:00
|
|
|
void Ident::get_cval(TaggedValue &v) const {
|
2015-08-13 21:23:46 +00:00
|
|
|
switch (get_valtype()) {
|
2015-08-11 20:41:12 +00:00
|
|
|
case VAL_MACRO:
|
2016-08-06 18:51:32 +00:00
|
|
|
cs_set_macro(v, val.code, val.len);
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_CSTR:
|
2016-08-01 20:46:50 +00:00
|
|
|
v.set_cstr(ostd::ConstCharRange(val.s, val.len));
|
2015-08-11 20:41:12 +00:00
|
|
|
break;
|
|
|
|
case VAL_INT:
|
|
|
|
v.set_int(val.i);
|
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
|
|
|
v.set_float(val.f);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
v.set_null();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-05 21:58:45 +00:00
|
|
|
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() {
|
2016-08-06 18:12:38 +00:00
|
|
|
ostd::Uint32 *bcode = reinterpret_cast<ostd::Uint32 *>(code);
|
|
|
|
if (bcode) {
|
|
|
|
bcode[0] -= 0x100;
|
|
|
|
if (int(bcode[0]) < 0x100) delete[] bcode;
|
2015-08-05 21:58:45 +00:00
|
|
|
code = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
void Ident::push_arg(TaggedValue const &v, IdentStack &st, bool um) {
|
2015-08-06 22:02:56 +00:00
|
|
|
st.val = val;
|
|
|
|
st.valtype = valtype;
|
|
|
|
st.next = stack;
|
|
|
|
stack = &st;
|
2015-08-11 20:41:12 +00:00
|
|
|
set_value(v);
|
2015-08-06 22:02:56 +00:00
|
|
|
clean_code();
|
2015-08-13 18:11:54 +00:00
|
|
|
if (um)
|
|
|
|
flags &= ~IDF_UNKNOWN;
|
2015-08-06 22:02:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Ident::pop_arg() {
|
|
|
|
if (!stack) return;
|
|
|
|
IdentStack *st = stack;
|
2015-08-13 21:23:46 +00:00
|
|
|
if (get_valtype() == VAL_STR) delete[] val.s;
|
2015-08-11 20:41:12 +00:00
|
|
|
set_value(*stack);
|
2015-08-06 22:02:56 +00:00
|
|
|
clean_code();
|
|
|
|
stack = st->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ident::undo_arg(IdentStack &st) {
|
|
|
|
IdentStack *prev = stack;
|
|
|
|
st.val = val;
|
|
|
|
st.valtype = valtype;
|
|
|
|
st.next = prev;
|
|
|
|
stack = prev->next;
|
2015-08-11 20:41:12 +00:00
|
|
|
set_value(*prev);
|
2015-08-06 22:02:56 +00:00
|
|
|
clean_code();
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
void Ident::redo_arg(IdentStack const &st) {
|
2015-08-06 22:02:56 +00:00
|
|
|
IdentStack *prev = st.next;
|
|
|
|
prev->val = val;
|
|
|
|
prev->valtype = valtype;
|
|
|
|
stack = prev;
|
2015-08-11 20:41:12 +00:00
|
|
|
set_value(st);
|
2015-08-06 22:02:56 +00:00
|
|
|
clean_code();
|
|
|
|
}
|
|
|
|
|
2015-08-06 22:16:02 +00:00
|
|
|
void Ident::push_alias(IdentStack &stack) {
|
2016-02-06 23:17:28 +00:00
|
|
|
if (type == ID_ALIAS && index >= MaxArguments)
|
2015-08-06 22:16:02 +00:00
|
|
|
push_arg(null_value, stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ident::pop_alias() {
|
2016-02-06 23:17:28 +00:00
|
|
|
if (type == ID_ALIAS && index >= MaxArguments) pop_arg();
|
2015-08-06 22:16:02 +00:00
|
|
|
}
|
|
|
|
|
2015-08-06 22:38:22 +00:00
|
|
|
void Ident::set_arg(CsState &cs, TaggedValue &v) {
|
|
|
|
if (cs.stack->usedargs & (1 << index)) {
|
2015-08-13 21:23:46 +00:00
|
|
|
if (get_valtype() == VAL_STR) delete[] val.s;
|
2015-08-11 20:41:12 +00:00
|
|
|
set_value(v);
|
2015-08-06 22:38:22 +00:00
|
|
|
clean_code();
|
|
|
|
} else {
|
2015-08-13 18:11:54 +00:00
|
|
|
push_arg(v, cs.stack->argstack[index], false);
|
2015-08-06 22:38:22 +00:00
|
|
|
cs.stack->usedargs |= 1 << index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ident::set_alias(CsState &cs, TaggedValue &v) {
|
2015-08-13 21:23:46 +00:00
|
|
|
if (get_valtype() == VAL_STR) delete[] val.s;
|
2015-08-11 20:41:12 +00:00
|
|
|
set_value(v);
|
2015-08-06 22:38:22 +00:00
|
|
|
clean_code();
|
|
|
|
flags = (flags & cs.identflags) | cs.identflags;
|
|
|
|
}
|
|
|
|
|
2016-08-10 17:33:43 +00:00
|
|
|
IdentType Ident::get_type() const {
|
|
|
|
if (type > ID_ALIAS) {
|
|
|
|
return IdentType::unknown;
|
|
|
|
}
|
|
|
|
return IdentType(type);
|
|
|
|
}
|
|
|
|
|
2016-08-06 15:34:10 +00:00
|
|
|
template<typename F>
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2015-08-07 01:11:53 +00:00
|
|
|
template<typename SF, typename RF, typename CF>
|
|
|
|
bool cs_override_var(CsState &cs, Ident *id, SF sf, RF rf, CF cf) {
|
|
|
|
if ((cs.identflags & IDF_OVERRIDDEN) || (id->flags & IDF_OVERRIDE)) {
|
|
|
|
if (id->flags & IDF_PERSIST) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, "cannot override persistent variable '%s'",
|
2015-08-07 01:11:53 +00:00
|
|
|
id->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!(id->flags & IDF_OVERRIDDEN)) {
|
|
|
|
sf();
|
|
|
|
id->flags |= IDF_OVERRIDDEN;
|
|
|
|
} else cf();
|
|
|
|
} else {
|
|
|
|
if (id->flags & IDF_OVERRIDDEN) {
|
|
|
|
rf();
|
|
|
|
id->flags &= ~IDF_OVERRIDDEN;
|
|
|
|
}
|
|
|
|
cf();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::set_var_int(ostd::ConstCharRange name, int v,
|
|
|
|
bool dofunc, bool doclamp) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_VAR)
|
|
|
|
return;
|
|
|
|
bool success = cs_override_var(*this, id,
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { id->overrideval.i = *id->storage.ip; },
|
2015-08-07 01:11:53 +00:00
|
|
|
[]() {}, []() {});
|
|
|
|
if (!success)
|
|
|
|
return;
|
|
|
|
if (doclamp)
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.ip = ostd::clamp(v, id->minval, id->maxval);
|
2015-08-07 01:11:53 +00:00
|
|
|
else
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.ip = v;
|
2015-08-07 01:11:53 +00:00
|
|
|
if (dofunc)
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::set_var_float(ostd::ConstCharRange name, float v,
|
|
|
|
bool dofunc, bool doclamp) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_FVAR)
|
|
|
|
return;
|
|
|
|
bool success = cs_override_var(*this, id,
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { id->overrideval.f = *id->storage.fp; },
|
2015-08-07 01:11:53 +00:00
|
|
|
[]() {}, []() {});
|
|
|
|
if (!success)
|
|
|
|
return;
|
|
|
|
if (doclamp)
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.fp = ostd::clamp(v, id->minvalf, id->maxvalf);
|
2015-08-07 01:11:53 +00:00
|
|
|
else
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.fp = v;
|
2015-08-07 01:11:53 +00:00
|
|
|
if (dofunc)
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CsState::set_var_str(ostd::ConstCharRange name, ostd::ConstCharRange v,
|
|
|
|
bool dofunc) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_SVAR)
|
|
|
|
return;
|
|
|
|
bool success = cs_override_var(*this, id,
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { id->overrideval.s = *id->storage.sp; },
|
2015-08-07 01:11:53 +00:00
|
|
|
[&id]() { delete[] id->overrideval.s; },
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { delete[] *id->storage.sp; });
|
2015-08-07 01:11:53 +00:00
|
|
|
if (!success)
|
|
|
|
return;
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.sp = cs_dup_ostr(v);
|
2015-08-07 01:11:53 +00:00
|
|
|
if (dofunc)
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<int> CsState::get_var_int(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_VAR)
|
|
|
|
return ostd::nothing;
|
2015-08-13 20:08:57 +00:00
|
|
|
return *id->storage.ip;
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<float> CsState::get_var_float(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_FVAR)
|
|
|
|
return ostd::nothing;
|
2015-08-13 20:08:57 +00:00
|
|
|
return *id->storage.fp;
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<ostd::String> CsState::get_var_str(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_SVAR)
|
|
|
|
return ostd::nothing;
|
2015-08-13 20:08:57 +00:00
|
|
|
return ostd::String(*id->storage.sp);
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<int> CsState::get_var_min_int(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_VAR)
|
|
|
|
return ostd::nothing;
|
|
|
|
return id->minval;
|
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<int> CsState::get_var_max_int(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_VAR)
|
|
|
|
return ostd::nothing;
|
|
|
|
return id->maxval;
|
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<float> CsState::get_var_min_float(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_FVAR)
|
|
|
|
return ostd::nothing;
|
|
|
|
return id->minvalf;
|
|
|
|
}
|
|
|
|
|
|
|
|
ostd::Maybe<float> CsState::get_var_max_float(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_FVAR)
|
|
|
|
return ostd::nothing;
|
|
|
|
return id->maxvalf;
|
|
|
|
}
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
ostd::Maybe<ostd::String>
|
2015-08-07 01:11:53 +00:00
|
|
|
CsState::get_alias(ostd::ConstCharRange name) {
|
|
|
|
Ident *id = idents.at(name);
|
|
|
|
if (!id || id->type != ID_ALIAS)
|
|
|
|
return ostd::nothing;
|
2016-02-06 23:17:28 +00:00
|
|
|
if ((id->index < MaxArguments) && !(stack->usedargs & (1 << id->index)))
|
2015-08-07 01:11:53 +00:00
|
|
|
return ostd::nothing;
|
2016-07-13 18:24:26 +00:00
|
|
|
return ostd::move(id->get_str());
|
2015-08-07 01:11:53 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 01:44:51 +00:00
|
|
|
int cs_clamp_var(CsState &cs, Ident *id, int v) {
|
|
|
|
if (v < id->minval)
|
|
|
|
v = id->minval;
|
|
|
|
else if (v > id->maxval)
|
|
|
|
v = id->maxval;
|
|
|
|
else
|
|
|
|
return v;
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, (id->flags & IDF_HEX)
|
|
|
|
? ((id->minval <= 255)
|
|
|
|
? "valid range for '%s' is %d..0x%X"
|
|
|
|
: "valid range for '%s' is 0x%X..0x%X")
|
|
|
|
: "valid range for '%s' is %d..%d",
|
2015-08-07 01:44:51 +00:00
|
|
|
id->name, id->minval, id->maxval);
|
|
|
|
return v;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 01:44:51 +00:00
|
|
|
void CsState::set_var_int_checked(Ident *id, int v) {
|
|
|
|
if (id->flags & IDF_READONLY) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "variable '%s' is read only", id->name);
|
2015-08-07 01:44:51 +00:00
|
|
|
return;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2015-08-07 01:44:51 +00:00
|
|
|
bool success = cs_override_var(*this, id,
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { id->overrideval.i = *id->storage.ip; },
|
2015-08-07 01:44:51 +00:00
|
|
|
[]() {}, []() {});
|
|
|
|
if (!success)
|
|
|
|
return;
|
|
|
|
if (v < id->minval || v > id->maxval)
|
|
|
|
v = cs_clamp_var(*this, id, v);
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.ip = v;
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-27 08:16:22 +00:00
|
|
|
void CsState::set_var_int_checked(Ident *id, TvalRange args) {
|
2015-08-07 01:44:51 +00:00
|
|
|
int v = args[0].force_int();
|
|
|
|
if ((id->flags & IDF_HEX) && (args.size() > 1)) {
|
|
|
|
v = (v << 16) | (args[1].force_int() << 8);
|
|
|
|
if (args.size() > 2)
|
|
|
|
v |= args[2].force_int();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2015-08-07 01:44:51 +00:00
|
|
|
set_var_int_checked(id, v);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 01:44:51 +00:00
|
|
|
float cs_clamp_fvar(CsState &cs, Ident *id, float v) {
|
|
|
|
if (v < id->minvalf)
|
|
|
|
v = id->minvalf;
|
|
|
|
else if (v > id->maxvalf)
|
|
|
|
v = id->maxvalf;
|
|
|
|
else
|
|
|
|
return v;
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, "valid range for '%s' is %s..%s", floatstr(id->minvalf),
|
2015-08-07 01:44:51 +00:00
|
|
|
floatstr(id->maxvalf));
|
|
|
|
return v;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 01:44:51 +00:00
|
|
|
void CsState::set_var_float_checked(Ident *id, float v) {
|
|
|
|
if (id->flags & IDF_READONLY) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "variable '%s' is read only", id->name);
|
2015-08-07 01:44:51 +00:00
|
|
|
return;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2015-08-07 01:44:51 +00:00
|
|
|
bool success = cs_override_var(*this, id,
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { id->overrideval.f = *id->storage.fp; },
|
2015-08-07 01:44:51 +00:00
|
|
|
[]() {}, []() {});
|
|
|
|
if (!success)
|
|
|
|
return;
|
|
|
|
if (v < id->minvalf || v > id->maxvalf)
|
|
|
|
v = cs_clamp_fvar(*this, id, v);
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.fp = v;
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 01:44:51 +00:00
|
|
|
void CsState::set_var_str_checked(Ident *id, ostd::ConstCharRange v) {
|
|
|
|
if (id->flags & IDF_READONLY) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(*this, "variable '%s' is read only", id->name);
|
2015-08-07 01:44:51 +00:00
|
|
|
return;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2015-08-07 01:44:51 +00:00
|
|
|
bool success = cs_override_var(*this, id,
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { id->overrideval.s = *id->storage.sp; },
|
2015-08-07 01:44:51 +00:00
|
|
|
[&id]() { delete[] id->overrideval.s; },
|
2015-08-13 20:08:57 +00:00
|
|
|
[&id]() { delete[] *id->storage.sp; });
|
2015-08-07 01:44:51 +00:00
|
|
|
if (!success) return;
|
2015-08-13 20:08:57 +00:00
|
|
|
*id->storage.sp = cs_dup_ostr(v);
|
2016-08-01 18:33:40 +00:00
|
|
|
id->changed();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-10 17:33:43 +00:00
|
|
|
static bool cs_add_command(
|
|
|
|
CsState &cs, ostd::ConstCharRange name, ostd::ConstCharRange args,
|
|
|
|
CmdFunc func, int type = ID_COMMAND
|
|
|
|
) {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 argmask = 0;
|
2015-08-05 21:58:45 +00:00
|
|
|
int nargs = 0;
|
|
|
|
ostd::ConstCharRange fmt(args);
|
|
|
|
for (; !fmt.empty(); fmt.pop_front()) {
|
|
|
|
switch (fmt.front()) {
|
|
|
|
case 'i':
|
|
|
|
case 'b':
|
|
|
|
case 'f':
|
|
|
|
case 'F':
|
|
|
|
case 't':
|
|
|
|
case 'T':
|
|
|
|
case 'E':
|
|
|
|
case 'N':
|
|
|
|
case 'D':
|
2016-02-06 23:17:28 +00:00
|
|
|
if (nargs < MaxArguments) nargs++;
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
case 's':
|
|
|
|
case 'e':
|
|
|
|
case 'r':
|
|
|
|
case '$':
|
2016-02-06 23:17:28 +00:00
|
|
|
if (nargs < MaxArguments) {
|
2015-08-05 21:58:45 +00:00
|
|
|
argmask |= 1 << nargs;
|
|
|
|
nargs++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
2016-02-06 23:17:28 +00:00
|
|
|
if (nargs < MaxArguments)
|
2015-08-05 21:58:45 +00:00
|
|
|
fmt.push_front_n(fmt.front() - '0' + 1);
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
case 'V':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ostd::err.writefln("builtin %s declared with illegal type: %c",
|
|
|
|
name, fmt.front());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-08-10 17:33:43 +00:00
|
|
|
cs.add_ident(type, name, args, argmask, nargs, ostd::move(func));
|
2015-08-05 21:58:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-08-10 17:33:43 +00:00
|
|
|
bool CsState::add_command(
|
|
|
|
ostd::ConstCharRange name, ostd::ConstCharRange args, CmdFunc func
|
|
|
|
) {
|
|
|
|
return cs_add_command(*this, name, args, ostd::move(func));
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *parsestring(char const *p) {
|
2015-08-05 21:58:45 +00:00
|
|
|
for (; *p; p++) switch (*p) {
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\"':
|
|
|
|
return p;
|
|
|
|
case '^':
|
|
|
|
if (*++p) break;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2015-08-12 01:11:22 +00:00
|
|
|
ostd::ConstCharRange cs_parse_str(ostd::ConstCharRange str) {
|
|
|
|
for (; !str.empty(); str.pop_front())
|
|
|
|
switch (str.front()) {
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\"':
|
|
|
|
return str;
|
|
|
|
case '^':
|
|
|
|
str.pop_front();
|
|
|
|
if (!str.empty()) break;
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline void skipcomments(char const *&p) {
|
2015-08-05 21:58:45 +00:00
|
|
|
for (;;) {
|
|
|
|
p += strspn(p, " \t\r");
|
|
|
|
if (p[0] != '/' || p[1] != '/') break;
|
|
|
|
p += strcspn(p, "\n\0");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ostd::Vector<char> strbuf[4];
|
|
|
|
static int stridx = 0;
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline void cutstring(char const *&p, ostd::ConstCharRange &s) {
|
2015-08-05 21:58:45 +00:00
|
|
|
p++;
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *end = parsestring(p);
|
2015-08-05 21:58:45 +00:00
|
|
|
int maxlen = int(end - p) + 1;
|
|
|
|
|
|
|
|
stridx = (stridx + 1) % 4;
|
|
|
|
ostd::Vector<char> &buf = strbuf[stridx];
|
|
|
|
buf.reserve(maxlen);
|
|
|
|
|
2015-08-11 01:18:53 +00:00
|
|
|
auto writer = buf.iter_cap();
|
|
|
|
s = ostd::ConstCharRange(buf.data(),
|
|
|
|
util::unescape_string(writer, ostd::ConstCharRange(p, end)));
|
|
|
|
writer.put('\0');
|
2015-08-05 21:58:45 +00:00
|
|
|
p = end;
|
|
|
|
if (*p == '\"') p++;
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline char *cutstring(char const *&p) {
|
2015-08-05 21:58:45 +00:00
|
|
|
p++;
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *end = parsestring(p);
|
2015-08-05 21:58:45 +00:00
|
|
|
char *buf = new char[end - p + 1];
|
2015-08-11 01:18:53 +00:00
|
|
|
auto writer = ostd::CharRange(buf, end - p + 1);
|
|
|
|
util::unescape_string(writer, ostd::ConstCharRange(p, end));
|
|
|
|
writer.put('\0');
|
2015-08-05 21:58:45 +00:00
|
|
|
p = end;
|
|
|
|
if (*p == '\"') p++;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
char const *parseword(char const *p) {
|
2016-07-17 17:50:40 +00:00
|
|
|
constexpr int maxbrak = 100;
|
2015-08-05 21:58:45 +00:00
|
|
|
static char brakstack[maxbrak];
|
|
|
|
int brakdepth = 0;
|
|
|
|
for (;; p++) {
|
|
|
|
p += strcspn(p, "\"/;()[] \t\r\n\0");
|
|
|
|
switch (p[0]) {
|
|
|
|
case '"':
|
|
|
|
case ';':
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\0':
|
|
|
|
return p;
|
|
|
|
case '/':
|
|
|
|
if (p[1] == '/') return p;
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
case '(':
|
|
|
|
if (brakdepth >= maxbrak) return p;
|
|
|
|
brakstack[brakdepth++] = p[0];
|
|
|
|
break;
|
|
|
|
case ']':
|
|
|
|
if (brakdepth <= 0 || brakstack[--brakdepth] != '[') return p;
|
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
if (brakdepth <= 0 || brakstack[--brakdepth] != '(') return p;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline void cutword(char const *&p, ostd::ConstCharRange &s) {
|
|
|
|
char const *op = p;
|
2015-08-05 21:58:45 +00:00
|
|
|
p = parseword(p);
|
|
|
|
s = ostd::ConstCharRange(op, p - op);
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline char *cutword(char const *&p) {
|
|
|
|
char const *word = p;
|
2015-08-05 21:58:45 +00:00
|
|
|
p = parseword(p);
|
2015-08-11 21:16:20 +00:00
|
|
|
return p != word ? cs_dup_ostr(ostd::ConstCharRange(word, p - word)) : nullptr;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 18:42:12 +00:00
|
|
|
static inline int cs_ret_code(int type, int def = 0) {
|
2015-08-07 18:28:09 +00:00
|
|
|
return (type >= VAL_ANY) ? ((type == VAL_CSTR) ? RET_STR : def)
|
|
|
|
: (type << CODE_RET);
|
|
|
|
}
|
|
|
|
|
2015-08-07 19:32:50 +00:00
|
|
|
struct GenState;
|
|
|
|
|
2015-08-10 23:45:11 +00:00
|
|
|
static void compilestatements(GenState &gs, int rettype, int brak = '\0', int prevargs = 0);
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline char const *compileblock(GenState &gs, char const *p, int rettype = RET_NULL, int brak = '\0');
|
2015-08-07 19:32:50 +00:00
|
|
|
|
2015-08-07 18:28:09 +00:00
|
|
|
struct GenState {
|
|
|
|
CsState &cs;
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Vector<ostd::Uint32> code;
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *source;
|
2015-08-07 18:28:09 +00:00
|
|
|
|
|
|
|
GenState() = delete;
|
2016-03-04 21:28:41 +00:00
|
|
|
GenState(CsState &csr): cs(csr), code(), source(nullptr) {}
|
2015-08-07 18:28:09 +00:00
|
|
|
|
2015-08-07 18:42:12 +00:00
|
|
|
void gen_str(ostd::ConstCharRange word, bool macro = false) {
|
|
|
|
if (word.size() <= 3 && !macro) {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 op = CODE_VALI | RET_STR;
|
2015-08-07 18:42:12 +00:00
|
|
|
for (ostd::Size i = 0; i < word.size(); ++i)
|
2015-08-07 22:37:54 +00:00
|
|
|
op |= ostd::Uint32(ostd::byte(word[i])) << ((i + 1) * 8);
|
2015-08-07 18:42:12 +00:00
|
|
|
code.push(op);
|
|
|
|
return;
|
|
|
|
}
|
2015-08-07 22:37:54 +00:00
|
|
|
code.push((macro ? CODE_MACRO : (CODE_VAL | RET_STR)) |
|
|
|
|
(word.size() << 8));
|
2016-07-17 17:50:40 +00:00
|
|
|
code.push_n(reinterpret_cast<ostd::Uint32 const *>(word.data()),
|
2015-08-07 22:37:54 +00:00
|
|
|
word.size() / sizeof(ostd::Uint32));
|
|
|
|
ostd::Size esz = word.size() % sizeof(ostd::Uint32);
|
2015-08-07 18:42:12 +00:00
|
|
|
union {
|
2015-08-07 22:37:54 +00:00
|
|
|
char c[sizeof(ostd::Uint32)];
|
|
|
|
ostd::Uint32 u;
|
2015-08-07 18:42:12 +00:00
|
|
|
} end;
|
|
|
|
end.u = 0;
|
|
|
|
memcpy(end.c, word.data() + word.size() - esz, esz);
|
|
|
|
code.push(end.u);
|
|
|
|
}
|
2015-08-05 21:58:45 +00:00
|
|
|
|
2015-08-07 18:42:12 +00:00
|
|
|
void gen_str() {
|
|
|
|
code.push(CODE_VALI | RET_STR);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 19:14:31 +00:00
|
|
|
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) {
|
2015-08-12 01:54:13 +00:00
|
|
|
gen_int(cs_parse_int(word));
|
2015-08-07 19:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 u;
|
2015-08-07 19:14:31 +00:00
|
|
|
} c;
|
|
|
|
c.f = f;
|
|
|
|
code.push(CODE_VAL | RET_FLOAT);
|
|
|
|
code.push(c.u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gen_float(ostd::ConstCharRange word) {
|
2015-08-12 01:54:13 +00:00
|
|
|
gen_float(cs_parse_float(word));
|
2015-08-07 19:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gen_ident(Ident *id) {
|
2016-02-06 23:17:28 +00:00
|
|
|
code.push(((id->index < MaxArguments) ? CODE_IDENTARG
|
2015-08-07 19:14:31 +00:00
|
|
|
: CODE_IDENT) |
|
|
|
|
(id->index << 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
void gen_ident() {
|
|
|
|
gen_ident(cs.dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gen_ident(ostd::ConstCharRange word) {
|
2015-08-13 18:51:15 +00:00
|
|
|
gen_ident(cs.new_ident(word));
|
2015-08-07 19:14:31 +00:00
|
|
|
}
|
|
|
|
|
2015-08-10 18:45:22 +00:00
|
|
|
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:
|
|
|
|
gen_str(word, true);
|
|
|
|
break;
|
|
|
|
case VAL_ANY:
|
|
|
|
if (!word.empty())
|
|
|
|
gen_str(word);
|
|
|
|
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());
|
|
|
|
else
|
|
|
|
gen_null();
|
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
|
|
|
compileblock(*this, word.data());
|
|
|
|
break;
|
|
|
|
case VAL_IDENT:
|
|
|
|
gen_ident(word);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-09 00:17:06 +00:00
|
|
|
void gen_main(ostd::ConstCharRange s, int ret_type = VAL_ANY);
|
2015-08-11 21:47:25 +00:00
|
|
|
|
|
|
|
char next_char() {
|
|
|
|
return *source++;
|
|
|
|
}
|
2015-08-11 21:57:06 +00:00
|
|
|
|
|
|
|
char current() {
|
|
|
|
return *source;
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
};
|
2015-08-05 21:58:45 +00:00
|
|
|
|
2015-08-07 19:32:50 +00:00
|
|
|
static inline void compileblock(GenState &gs) {
|
|
|
|
gs.code.push(CODE_EMPTY);
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline char const *compileblock(GenState &gs, char const *p, int rettype, int brak) {
|
2015-08-07 19:32:50 +00:00
|
|
|
ostd::Size start = gs.code.size();
|
|
|
|
gs.code.push(CODE_BLOCK);
|
|
|
|
gs.code.push(CODE_OFFSET | ((start + 2) << 8));
|
2015-08-10 23:45:11 +00:00
|
|
|
if (p) {
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *op = gs.source;
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source = p;
|
|
|
|
compilestatements(gs, VAL_ANY, brak);
|
|
|
|
p = gs.source;
|
|
|
|
gs.source = op;
|
|
|
|
}
|
2015-08-07 19:32:50 +00:00
|
|
|
if (gs.code.size() > start + 2) {
|
|
|
|
gs.code.push(CODE_EXIT | rettype);
|
2015-08-07 22:37:54 +00:00
|
|
|
gs.code[start] |= ostd::Uint32(gs.code.size() - (start + 1)) << 8;
|
2015-08-07 19:32:50 +00:00
|
|
|
} else {
|
|
|
|
gs.code.resize(start);
|
|
|
|
gs.code.push(CODE_EMPTY | rettype);
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2015-08-10 23:45:11 +00:00
|
|
|
static inline void compileunescapestr(GenState &gs, bool macro = false) {
|
2015-08-11 21:47:25 +00:00
|
|
|
gs.next_char();
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *end = parsestring(gs.source);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(macro ? CODE_MACRO : CODE_VAL | RET_STR);
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.code.reserve(gs.code.size() + (end - gs.source) / sizeof(ostd::Uint32) + 1);
|
2016-07-17 17:50:40 +00:00
|
|
|
char *buf = reinterpret_cast<char *>(&gs.code[gs.code.size()]);
|
2015-08-11 01:18:53 +00:00
|
|
|
auto writer = ostd::CharRange(buf, (gs.code.capacity() - gs.code.size()) * sizeof(ostd::Uint32));
|
|
|
|
ostd::Size len = util::unescape_string(writer, ostd::ConstCharRange(gs.source, end));
|
|
|
|
writer.put('\0');
|
2015-08-07 22:37:54 +00:00
|
|
|
memset(&buf[len], 0, sizeof(ostd::Uint32) - len % sizeof(ostd::Uint32));
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.back() |= len << 8;
|
2015-08-07 22:37:54 +00:00
|
|
|
gs.code.advance(len / sizeof(ostd::Uint32) + 1);
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source = end;
|
2015-08-11 21:47:25 +00:00
|
|
|
if (*gs.source == '\"') gs.next_char();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 22:37:54 +00:00
|
|
|
static ostd::Uint32 emptyblock[VAL_ANY][2] = {
|
2015-08-05 21:58:45 +00:00
|
|
|
{ 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 }
|
|
|
|
};
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
OSTD_EXPORT bool code_is_empty(Bytecode const *code) {
|
2016-08-06 16:33:01 +00:00
|
|
|
if (!code) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-08-06 18:12:38 +00:00
|
|
|
return (*reinterpret_cast<
|
|
|
|
ostd::Uint32 const *
|
|
|
|
>(code) & CODE_OP_MASK) == CODE_EXIT;
|
2016-08-06 16:33:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TaggedValue::code_is_empty() const {
|
|
|
|
if (get_type() != VAL_CODE) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return cscript::code_is_empty(code);
|
|
|
|
}
|
|
|
|
|
2015-08-12 01:54:13 +00:00
|
|
|
static inline bool cs_get_bool(ostd::ConstCharRange s) {
|
|
|
|
if (s.empty())
|
|
|
|
return false;
|
|
|
|
switch (s.front()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
switch (s[1]) {
|
|
|
|
case '0':
|
|
|
|
break;
|
|
|
|
case '.':
|
2015-08-12 01:54:13 +00:00
|
|
|
return !isdigit(s[2]) || (cs_parse_float(s) != 0);
|
2015-08-05 21:58:45 +00:00
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case '0': {
|
|
|
|
char *end;
|
2015-08-12 01:54:13 +00:00
|
|
|
int val = int(strtoul(s.data(), &end, 0));
|
2015-08-05 21:58:45 +00:00
|
|
|
if (val) return true;
|
|
|
|
switch (*end) {
|
|
|
|
case 'e':
|
|
|
|
case '.':
|
2015-08-12 01:54:13 +00:00
|
|
|
return (cs_parse_float(s) != 0);
|
2015-08-05 21:58:45 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case '.':
|
2015-08-12 01:54:13 +00:00
|
|
|
return !isdigit(s[1]) || (cs_parse_float(s) != 0);
|
2015-08-05 21:58:45 +00:00
|
|
|
case '\0':
|
|
|
|
return false;
|
|
|
|
}
|
2015-08-12 01:54:13 +00:00
|
|
|
return true;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
bool TaggedValue::get_bool() const {
|
|
|
|
switch (get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_FLOAT:
|
2016-08-02 00:21:36 +00:00
|
|
|
return f != 0;
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_INT:
|
2016-08-02 00:21:36 +00:00
|
|
|
return i != 0;
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
2016-08-02 00:21:36 +00:00
|
|
|
return cs_get_bool(ostd::ConstCharRange(s, len));
|
2015-08-05 21:58:45 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:32:19 +00:00
|
|
|
static bool compilearg(GenState &gs, int wordtype, int prevargs = MaxResults, ostd::ConstCharRange *word = nullptr);
|
2015-08-05 21:58:45 +00:00
|
|
|
|
2016-02-06 23:17:28 +00:00
|
|
|
static void compilelookup(GenState &gs, int ltype, int prevargs = MaxResults) {
|
2015-08-05 21:58:45 +00:00
|
|
|
ostd::ConstCharRange lookup;
|
2015-08-11 21:57:06 +00:00
|
|
|
gs.next_char();
|
|
|
|
switch (gs.current()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case '(':
|
|
|
|
case '[':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!compilearg(gs, VAL_CSTR, prevargs)) goto invalid;
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '$':
|
2015-08-10 23:45:11 +00:00
|
|
|
compilelookup(gs, VAL_CSTR, prevargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '\"':
|
2015-08-10 23:45:11 +00:00
|
|
|
cutstring(gs.source, lookup);
|
2015-08-05 21:58:45 +00:00
|
|
|
goto lookupid;
|
|
|
|
default: {
|
2015-08-10 23:45:11 +00:00
|
|
|
cutword(gs.source, lookup);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!lookup.size()) goto invalid;
|
|
|
|
lookupid:
|
2015-08-13 18:51:15 +00:00
|
|
|
Ident *id = gs.cs.new_ident(lookup);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (id) switch (id->type) {
|
|
|
|
case ID_VAR:
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_IVAR | cs_ret_code(ltype, RET_INT) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
switch (ltype) {
|
|
|
|
case VAL_POP:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.pop();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_COMPILE);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_IDENT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_IDENTU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case ID_FVAR:
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_FVAR | cs_ret_code(ltype, RET_FLOAT) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
switch (ltype) {
|
|
|
|
case VAL_POP:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.pop();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_COMPILE);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_IDENT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_IDENTU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case ID_SVAR:
|
|
|
|
switch (ltype) {
|
|
|
|
case VAL_POP:
|
|
|
|
return;
|
|
|
|
case VAL_CANY:
|
|
|
|
case VAL_CSTR:
|
|
|
|
case VAL_CODE:
|
|
|
|
case VAL_IDENT:
|
|
|
|
case VAL_COND:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_SVARM | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_SVAR | cs_ret_code(ltype, RET_STR) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
case ID_ALIAS:
|
|
|
|
switch (ltype) {
|
|
|
|
case VAL_POP:
|
|
|
|
return;
|
|
|
|
case VAL_CANY:
|
|
|
|
case VAL_COND:
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((id->index < MaxArguments ? CODE_LOOKUPMARG : CODE_LOOKUPM) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CSTR:
|
|
|
|
case VAL_CODE:
|
|
|
|
case VAL_IDENT:
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((id->index < MaxArguments ? CODE_LOOKUPMARG : CODE_LOOKUPM) | RET_STR | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((id->index < MaxArguments ? CODE_LOOKUPARG : CODE_LOOKUP) | cs_ret_code(ltype, RET_STR) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
case ID_COMMAND: {
|
|
|
|
int comtype = CODE_COM, numargs = 0;
|
2016-02-06 23:17:28 +00:00
|
|
|
if (prevargs >= MaxResults) gs.code.push(CODE_ENTER);
|
2016-08-09 23:54:51 +00:00
|
|
|
for (char const *fmt = id->cargs; *fmt; fmt++) switch (*fmt) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case 'S':
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str();
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 's':
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str(ostd::ConstCharRange(), true);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'i':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int();
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'b':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int(INT_MIN);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'f':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_float();
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'F':
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_DUP | RET_FLOAT);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'E':
|
|
|
|
case 'T':
|
|
|
|
case 't':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_null();
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'e':
|
2015-08-07 18:28:09 +00:00
|
|
|
compileblock(gs);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'r':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_ident();
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case '$':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_ident(id);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'N':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int(-1);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
comtype = CODE_COMC;
|
|
|
|
goto compilecomv;
|
|
|
|
case 'V':
|
|
|
|
comtype = CODE_COMV;
|
|
|
|
goto compilecomv;
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
break;
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(comtype | cs_ret_code(ltype) | (id->index << 8));
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((prevargs >= MaxResults ? CODE_EXIT : CODE_RESULT_ARG) | cs_ret_code(ltype));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
compilecomv:
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(comtype | cs_ret_code(ltype) | (numargs << 8) | (id->index << 13));
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((prevargs >= MaxResults ? CODE_EXIT : CODE_RESULT_ARG) | cs_ret_code(ltype));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
goto invalid;
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str(lookup, true);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (ltype) {
|
|
|
|
case VAL_CANY:
|
|
|
|
case VAL_COND:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_LOOKUPMU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CSTR:
|
|
|
|
case VAL_CODE:
|
|
|
|
case VAL_IDENT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_LOOKUPMU | RET_STR);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_LOOKUPU | cs_ret_code(ltype));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
switch (ltype) {
|
|
|
|
case VAL_POP:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_POP);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_COMPILE);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_COND:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_COND);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_IDENT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_IDENTU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
invalid:
|
|
|
|
switch (ltype) {
|
|
|
|
case VAL_POP:
|
|
|
|
break;
|
|
|
|
case VAL_NULL:
|
|
|
|
case VAL_ANY:
|
|
|
|
case VAL_CANY:
|
|
|
|
case VAL_WORD:
|
|
|
|
case VAL_COND:
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_null();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-08-10 18:45:22 +00:00
|
|
|
gs.gen_value(ltype);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-11 00:21:39 +00:00
|
|
|
static bool compileblockstr(GenState &gs, ostd::ConstCharRange str, bool macro) {
|
2016-03-04 21:28:41 +00:00
|
|
|
int startc = gs.code.size();
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(macro ? CODE_MACRO : CODE_VAL | RET_STR);
|
2015-08-11 00:21:39 +00:00
|
|
|
gs.code.reserve(gs.code.size() + str.size() / sizeof(ostd::Uint32) + 1);
|
2016-07-17 17:50:40 +00:00
|
|
|
char *buf = reinterpret_cast<char *>(&gs.code[gs.code.size()]);
|
2015-08-05 21:58:45 +00:00
|
|
|
int len = 0;
|
2015-08-11 00:21:39 +00:00
|
|
|
while (!str.empty()) {
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *p = str.data();
|
2015-08-11 00:39:25 +00:00
|
|
|
str = ostd::find_one_of(str, ostd::ConstCharRange("\r/\"@]"));
|
|
|
|
memcpy(&buf[len], p, str.data() - p);
|
|
|
|
len += str.data() - p;
|
2015-08-11 00:21:39 +00:00
|
|
|
if (str.empty())
|
|
|
|
goto done;
|
|
|
|
switch (str.front()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case '\r':
|
2015-08-11 00:21:39 +00:00
|
|
|
str.pop_front();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '\"': {
|
2015-08-12 01:11:22 +00:00
|
|
|
ostd::ConstCharRange start = str;
|
|
|
|
start.pop_front();
|
|
|
|
ostd::ConstCharRange end = cs_parse_str(start);
|
|
|
|
if (!end.empty() && (end.front() == '\"'))
|
|
|
|
end.pop_front();
|
|
|
|
ostd::Size slen = str.distance_front(end);
|
|
|
|
memcpy(&buf[len], str.data(), slen);
|
|
|
|
len += slen;
|
|
|
|
str = end;
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '/':
|
2015-08-11 00:39:25 +00:00
|
|
|
if (str[1] == '/')
|
|
|
|
str = ostd::find(str, '\n');
|
2015-08-11 00:21:39 +00:00
|
|
|
else {
|
|
|
|
buf[len++] = str.front();
|
|
|
|
str.pop_front();
|
|
|
|
}
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '@':
|
|
|
|
case ']':
|
2015-08-11 00:21:39 +00:00
|
|
|
buf[len++] = str.front();
|
|
|
|
str.pop_front();
|
|
|
|
break;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
2015-08-07 22:37:54 +00:00
|
|
|
memset(&buf[len], '\0', sizeof(ostd::Uint32) - len % sizeof(ostd::Uint32));
|
|
|
|
gs.code.advance(len / sizeof(ostd::Uint32) + 1);
|
2016-03-04 21:28:41 +00:00
|
|
|
gs.code[startc] |= len << 8;
|
2015-08-05 21:58:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-10 23:45:11 +00:00
|
|
|
static bool compileblocksub(GenState &gs, int prevargs) {
|
2015-08-05 21:58:45 +00:00
|
|
|
ostd::ConstCharRange lookup;
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *op;
|
2015-08-11 21:57:06 +00:00
|
|
|
switch (gs.current()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case '(':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!compilearg(gs, VAL_CANY, prevargs)) return false;
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '[':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!compilearg(gs, VAL_CSTR, prevargs)) return false;
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_LOOKUPMU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '\"':
|
2015-08-10 23:45:11 +00:00
|
|
|
cutstring(gs.source, lookup);
|
2015-08-05 21:58:45 +00:00
|
|
|
goto lookupid;
|
|
|
|
default: {
|
2015-08-10 23:45:11 +00:00
|
|
|
op = gs.source;
|
2015-08-11 21:57:06 +00:00
|
|
|
while (isalnum(gs.current()) || gs.current() == '_') gs.next_char();
|
2015-08-10 23:45:11 +00:00
|
|
|
lookup = ostd::ConstCharRange(op, gs.source - op);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (lookup.empty()) return false;
|
|
|
|
lookupid:
|
2015-08-13 18:51:15 +00:00
|
|
|
Ident *id = gs.cs.new_ident(lookup);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (id) switch (id->type) {
|
|
|
|
case ID_VAR:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_IVAR | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
case ID_FVAR:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_FVAR | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
case ID_SVAR:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_SVARM | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
case ID_ALIAS:
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((id->index < MaxArguments ? CODE_LOOKUPMARG : CODE_LOOKUPM) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str(lookup, true);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_LOOKUPMU);
|
2015-08-05 21:58:45 +00:00
|
|
|
done:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-10 23:45:11 +00:00
|
|
|
static void compileblockmain(GenState &gs, int wordtype, int prevargs) {
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *line = gs.source, *start = gs.source;
|
2015-08-05 21:58:45 +00:00
|
|
|
int concs = 0;
|
|
|
|
for (int brak = 1; brak;) {
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source += strcspn(gs.source, "@\"/[]\0");
|
2015-08-11 21:47:25 +00:00
|
|
|
char c = gs.next_char();
|
2015-08-05 21:58:45 +00:00
|
|
|
switch (c) {
|
|
|
|
case '\0':
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code_line(gs.cs, line, "missing \"]\"");
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source--;
|
2015-08-05 21:58:45 +00:00
|
|
|
goto done;
|
|
|
|
case '\"':
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source = parsestring(gs.source);
|
2015-08-11 21:57:06 +00:00
|
|
|
if (gs.current() == '\"') gs.next_char();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '/':
|
2015-08-11 21:57:06 +00:00
|
|
|
if (gs.current() == '/') gs.source += strcspn(gs.source, "\n\0");
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
brak++;
|
|
|
|
break;
|
|
|
|
case ']':
|
|
|
|
brak--;
|
|
|
|
break;
|
|
|
|
case '@': {
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *esc = gs.source;
|
2015-08-11 21:57:06 +00:00
|
|
|
while (gs.current() == '@') gs.next_char();
|
2015-08-10 23:45:11 +00:00
|
|
|
int level = gs.source - (esc - 1);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (brak > level) continue;
|
2015-08-12 17:50:02 +00:00
|
|
|
else if (brak < level) cs_debug_code_line(gs.cs, line, "too many @s");
|
2016-02-06 23:17:28 +00:00
|
|
|
if (!concs && prevargs >= MaxResults) gs.code.push(CODE_ENTER);
|
|
|
|
if (concs + 2 > MaxArguments) {
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_CONCW | RET_STR | (concs << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
concs = 1;
|
|
|
|
}
|
2015-08-11 00:21:39 +00:00
|
|
|
if (compileblockstr(gs, ostd::ConstCharRange(start, esc - 1), true)) concs++;
|
2015-08-10 23:45:11 +00:00
|
|
|
if (compileblocksub(gs, prevargs + concs)) concs++;
|
|
|
|
if (concs) start = gs.source;
|
2016-02-06 23:17:28 +00:00
|
|
|
else if (prevargs >= MaxResults) gs.code.pop();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (gs.source - 1 > start) {
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!concs) switch (wordtype) {
|
|
|
|
case VAL_POP:
|
|
|
|
return;
|
|
|
|
case VAL_CODE:
|
|
|
|
case VAL_COND:
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source = compileblock(gs, start, RET_NULL, ']');
|
2015-08-05 21:58:45 +00:00
|
|
|
return;
|
|
|
|
case VAL_IDENT:
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.gen_ident(ostd::ConstCharRange(start, gs.source - 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (wordtype) {
|
|
|
|
case VAL_CSTR:
|
|
|
|
case VAL_CODE:
|
|
|
|
case VAL_IDENT:
|
|
|
|
case VAL_CANY:
|
|
|
|
case VAL_COND:
|
2015-08-11 00:21:39 +00:00
|
|
|
compileblockstr(gs, ostd::ConstCharRange(start, gs.source - 1), true);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-08-11 00:21:39 +00:00
|
|
|
compileblockstr(gs, ostd::ConstCharRange(start, gs.source - 1), concs > 0);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (concs > 1) concs++;
|
|
|
|
}
|
|
|
|
if (concs) {
|
2016-02-06 23:17:28 +00:00
|
|
|
if (prevargs >= MaxResults) {
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_CONCM | cs_ret_code(wordtype) | (concs << 8));
|
|
|
|
gs.code.push(CODE_EXIT | cs_ret_code(wordtype));
|
|
|
|
} else gs.code.push(CODE_CONCW | cs_ret_code(wordtype) | (concs << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
switch (wordtype) {
|
|
|
|
case VAL_POP:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (concs || gs.source - 1 > start) gs.code.push(CODE_POP);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_COND:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!concs && gs.source - 1 <= start) gs.gen_null();
|
2015-08-07 18:28:09 +00:00
|
|
|
else gs.code.push(CODE_COND);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!concs && gs.source - 1 <= start) compileblock(gs);
|
2015-08-07 18:28:09 +00:00
|
|
|
else gs.code.push(CODE_COMPILE);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_IDENT:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!concs && gs.source - 1 <= start) gs.gen_ident();
|
2015-08-07 18:28:09 +00:00
|
|
|
else gs.code.push(CODE_IDENTU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CSTR:
|
|
|
|
case VAL_CANY:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!concs && gs.source - 1 <= start)
|
|
|
|
gs.gen_str(ostd::ConstCharRange(), true);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_NULL:
|
|
|
|
case VAL_ANY:
|
|
|
|
case VAL_WORD:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!concs && gs.source - 1 <= start) gs.gen_str();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!concs) {
|
2015-08-10 23:45:11 +00:00
|
|
|
if (gs.source - 1 <= start) gs.gen_value(wordtype);
|
2015-08-07 18:28:09 +00:00
|
|
|
else gs.code.push(CODE_FORCE | (wordtype << CODE_RET));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:32:19 +00:00
|
|
|
static bool compilearg(GenState &gs, int wordtype, int prevargs, ostd::ConstCharRange *word) {
|
|
|
|
ostd::ConstCharRange unused;
|
|
|
|
if (!word) word = &unused;
|
2015-08-10 23:45:11 +00:00
|
|
|
skipcomments(gs.source);
|
2015-08-11 21:57:06 +00:00
|
|
|
switch (gs.current()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case '\"':
|
|
|
|
switch (wordtype) {
|
|
|
|
case VAL_POP:
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source = parsestring(gs.source + 1);
|
2015-08-11 21:57:06 +00:00
|
|
|
if (gs.current() == '\"') gs.next_char();
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_COND: {
|
2015-08-10 23:45:11 +00:00
|
|
|
char *s = cutstring(gs.source);
|
2015-08-07 18:28:09 +00:00
|
|
|
if (s[0]) compileblock(gs, s);
|
2015-08-07 19:14:31 +00:00
|
|
|
else gs.gen_null();
|
2015-08-05 21:58:45 +00:00
|
|
|
delete[] s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VAL_CODE: {
|
2015-08-10 23:45:11 +00:00
|
|
|
char *s = cutstring(gs.source);
|
2015-08-07 18:28:09 +00:00
|
|
|
compileblock(gs, s);
|
2015-08-05 21:58:45 +00:00
|
|
|
delete[] s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VAL_WORD:
|
2016-07-15 17:32:19 +00:00
|
|
|
cutstring(gs.source, *word);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_ANY:
|
|
|
|
case VAL_STR:
|
2015-08-10 23:45:11 +00:00
|
|
|
compileunescapestr(gs);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CANY:
|
|
|
|
case VAL_CSTR:
|
2015-08-10 23:45:11 +00:00
|
|
|
compileunescapestr(gs, true);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
ostd::ConstCharRange s;
|
2015-08-10 23:45:11 +00:00
|
|
|
cutstring(gs.source, s);
|
2015-08-10 18:45:22 +00:00
|
|
|
gs.gen_value(wordtype, s);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case '$':
|
2015-08-10 23:45:11 +00:00
|
|
|
compilelookup(gs, wordtype, prevargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
return true;
|
|
|
|
case '(':
|
2015-08-11 21:47:25 +00:00
|
|
|
gs.next_char();
|
2016-02-06 23:17:28 +00:00
|
|
|
if (prevargs >= MaxResults) {
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_ENTER);
|
2015-08-10 23:45:11 +00:00
|
|
|
compilestatements(gs, wordtype > VAL_ANY ? VAL_CANY : VAL_ANY, ')');
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_EXIT | cs_ret_code(wordtype));
|
2015-08-05 21:58:45 +00:00
|
|
|
} else {
|
2015-08-07 18:28:09 +00:00
|
|
|
ostd::Size start = gs.code.size();
|
2015-08-10 23:45:11 +00:00
|
|
|
compilestatements(gs, wordtype > VAL_ANY ? VAL_CANY : VAL_ANY, ')', prevargs);
|
2015-08-07 18:42:12 +00:00
|
|
|
if (gs.code.size() > start) gs.code.push(CODE_RESULT_ARG | cs_ret_code(wordtype));
|
2015-08-05 21:58:45 +00:00
|
|
|
else {
|
2015-08-10 18:45:22 +00:00
|
|
|
gs.gen_value(wordtype);
|
2015-08-05 21:58:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (wordtype) {
|
|
|
|
case VAL_POP:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_POP);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_COND:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_COND);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_CODE:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_COMPILE);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_IDENT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_IDENTU);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case '[':
|
2015-08-11 21:47:25 +00:00
|
|
|
gs.next_char();
|
2015-08-10 23:45:11 +00:00
|
|
|
compileblockmain(gs, wordtype, prevargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
switch (wordtype) {
|
|
|
|
case VAL_POP: {
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *s = gs.source;
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source = parseword(gs.source);
|
|
|
|
return gs.source != s;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
case VAL_COND: {
|
2015-08-10 23:45:11 +00:00
|
|
|
char *s = cutword(gs.source);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!s) return false;
|
2015-08-07 18:28:09 +00:00
|
|
|
compileblock(gs, s);
|
2015-08-05 21:58:45 +00:00
|
|
|
delete[] s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case VAL_CODE: {
|
2015-08-10 23:45:11 +00:00
|
|
|
char *s = cutword(gs.source);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!s) return false;
|
2015-08-07 18:28:09 +00:00
|
|
|
compileblock(gs, s);
|
2015-08-05 21:58:45 +00:00
|
|
|
delete[] s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case VAL_WORD:
|
2016-07-15 17:32:19 +00:00
|
|
|
cutword(gs.source, *word);
|
|
|
|
return !word->empty();
|
2015-08-05 21:58:45 +00:00
|
|
|
default: {
|
|
|
|
ostd::ConstCharRange s;
|
2015-08-10 23:45:11 +00:00
|
|
|
cutword(gs.source, s);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (s.empty()) return false;
|
2015-08-10 18:45:22 +00:00
|
|
|
gs.gen_value(wordtype, s);
|
2015-08-05 21:58:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-10 23:45:11 +00:00
|
|
|
static void compilestatements(GenState &gs, int rettype, int brak, int prevargs) {
|
2016-07-17 17:50:40 +00:00
|
|
|
char const *line = gs.source;
|
2015-08-05 21:58:45 +00:00
|
|
|
ostd::ConstCharRange idname;
|
|
|
|
int numargs;
|
|
|
|
for (;;) {
|
2015-08-10 23:45:11 +00:00
|
|
|
skipcomments(gs.source);
|
2015-08-05 21:58:45 +00:00
|
|
|
idname = ostd::ConstCharRange(nullptr, nullptr);
|
2016-07-15 17:32:19 +00:00
|
|
|
bool more = compilearg(gs, VAL_WORD, prevargs, &idname);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) goto endstatement;
|
2015-08-10 23:45:11 +00:00
|
|
|
skipcomments(gs.source);
|
2015-08-11 21:57:06 +00:00
|
|
|
if (gs.current() == '=') switch (gs.source[1]) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case '/':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (gs.source[2] != '/') break;
|
2015-08-05 21:58:45 +00:00
|
|
|
case ';':
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\0':
|
2015-08-11 21:47:25 +00:00
|
|
|
gs.next_char();
|
2015-08-05 21:58:45 +00:00
|
|
|
if (idname.data()) {
|
2015-08-13 18:51:15 +00:00
|
|
|
Ident *id = gs.cs.new_ident(idname);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (id) switch (id->type) {
|
|
|
|
case ID_ALIAS:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_ANY, prevargs))) gs.gen_str();
|
2016-02-06 23:17:28 +00:00
|
|
|
gs.code.push((id->index < MaxArguments ? CODE_ALIASARG : CODE_ALIAS) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto endstatement;
|
|
|
|
case ID_VAR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_INT, prevargs))) gs.gen_int();
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_IVAR1 | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto endstatement;
|
|
|
|
case ID_FVAR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_FLOAT, prevargs))) gs.gen_float();
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_FVAR1 | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto endstatement;
|
|
|
|
case ID_SVAR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_CSTR, prevargs))) gs.gen_str();
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_SVAR1 | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
goto endstatement;
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str(idname, true);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_ANY))) gs.gen_str();
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_ALIASU);
|
2015-08-05 21:58:45 +00:00
|
|
|
goto endstatement;
|
|
|
|
}
|
|
|
|
numargs = 0;
|
|
|
|
if (!idname.data()) {
|
|
|
|
noid:
|
2016-02-06 23:17:28 +00:00
|
|
|
while (numargs < MaxArguments && (more = compilearg(gs, VAL_CANY, prevargs + numargs))) numargs++;
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_CALLU | (numargs << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
} else {
|
2015-08-07 18:28:09 +00:00
|
|
|
Ident *id = gs.cs.idents.at(idname);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!id) {
|
2015-08-11 20:41:12 +00:00
|
|
|
if (!cs_check_num(idname)) {
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str(idname, true);
|
2015-08-05 21:58:45 +00:00
|
|
|
goto noid;
|
|
|
|
}
|
|
|
|
switch (rettype) {
|
|
|
|
case VAL_ANY:
|
|
|
|
case VAL_CANY: {
|
2016-07-17 17:50:40 +00:00
|
|
|
char *end = const_cast<char *>(idname.data());
|
2015-08-05 21:58:45 +00:00
|
|
|
int val = int(strtoul(idname.data(), &end, 0));
|
2015-08-07 18:42:12 +00:00
|
|
|
if (end < &idname[idname.size()]) gs.gen_str(idname, rettype == VAL_CANY);
|
2015-08-07 19:14:31 +00:00
|
|
|
else gs.gen_int(val);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2015-08-10 18:45:22 +00:00
|
|
|
gs.gen_value(rettype, idname);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_RESULT);
|
2015-08-05 21:58:45 +00:00
|
|
|
} else switch (id->type) {
|
|
|
|
case ID_ALIAS:
|
2016-02-06 23:17:28 +00:00
|
|
|
while (numargs < MaxArguments && (more = compilearg(gs, VAL_ANY, prevargs + numargs))) numargs++;
|
|
|
|
gs.code.push((id->index < MaxArguments ? CODE_CALLARG : CODE_CALL) | (numargs << 8) | (id->index << 13));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_COMMAND: {
|
|
|
|
int comtype = CODE_COM, fakeargs = 0;
|
|
|
|
bool rep = false;
|
2016-08-09 23:54:51 +00:00
|
|
|
for (char const *fmt = id->cargs; *fmt; fmt++) switch (*fmt) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case 'S':
|
|
|
|
case 's':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, *fmt == 's' ? VAL_CSTR : VAL_STR, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.gen_str(ostd::ConstCharRange(), *fmt == 's');
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
} else if (!fmt[1]) {
|
|
|
|
int numconc = 1;
|
2016-02-06 23:17:28 +00:00
|
|
|
while (numargs + numconc < MaxArguments && (more = compilearg(gs, VAL_CSTR, prevargs + numargs + numconc))) numconc++;
|
2015-08-07 18:28:09 +00:00
|
|
|
if (numconc > 1) gs.code.push(CODE_CONC | RET_STR | (numconc << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'i':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_INT, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int();
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'b':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_INT, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int(INT_MIN);
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'f':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_FLOAT, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_float();
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'F':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_FLOAT, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_DUP | RET_FLOAT);
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
case 't':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, *fmt == 't' ? VAL_CANY : VAL_ANY, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_null();
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'E':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_COND, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_null();
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'e':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_CODE, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 18:28:09 +00:00
|
|
|
compileblock(gs);
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'r':
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_IDENT, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_ident();
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
|
|
|
}
|
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case '$':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_ident(id);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'N':
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int(numargs - fakeargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs++;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
comtype = CODE_COMC;
|
2016-02-06 23:17:28 +00:00
|
|
|
if (more) while (numargs < MaxArguments && (more = compilearg(gs, VAL_CANY, prevargs + numargs))) numargs++;
|
2015-08-05 21:58:45 +00:00
|
|
|
goto compilecomv;
|
|
|
|
case 'V':
|
|
|
|
comtype = CODE_COMV;
|
2016-02-06 23:17:28 +00:00
|
|
|
if (more) while (numargs < MaxArguments && (more = compilearg(gs, VAL_CANY, prevargs + numargs))) numargs++;
|
2015-08-05 21:58:45 +00:00
|
|
|
goto compilecomv;
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
2016-02-06 23:17:28 +00:00
|
|
|
if (more && numargs < MaxArguments) {
|
2015-08-05 21:58:45 +00:00
|
|
|
int numrep = *fmt - '0' + 1;
|
|
|
|
fmt -= numrep;
|
|
|
|
rep = true;
|
2016-02-06 23:17:28 +00:00
|
|
|
} else for (; numargs > MaxArguments; numargs--) gs.code.push(CODE_POP);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(comtype | cs_ret_code(rettype) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
compilecomv:
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(comtype | cs_ret_code(rettype) | (numargs << 8) | (id->index << 13));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ID_LOCAL:
|
2016-02-06 23:17:28 +00:00
|
|
|
if (more) while (numargs < MaxArguments && (more = compilearg(gs, VAL_IDENT, prevargs + numargs))) numargs++;
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) while ((more = compilearg(gs, VAL_POP)));
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_LOCAL | (numargs << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_DO:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_CODE, prevargs);
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push((more ? CODE_DO : CODE_NULL) | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_DOARGS:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_CODE, prevargs);
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push((more ? CODE_DOARGS : CODE_NULL) | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_IF:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_CANY, prevargs);
|
2015-08-07 18:42:12 +00:00
|
|
|
if (!more) gs.code.push(CODE_NULL | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
else {
|
2015-08-07 18:28:09 +00:00
|
|
|
int start1 = gs.code.size();
|
2015-08-10 23:45:11 +00:00
|
|
|
more = compilearg(gs, VAL_CODE, prevargs + 1);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_POP);
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_NULL | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
} else {
|
2015-08-07 18:28:09 +00:00
|
|
|
int start2 = gs.code.size();
|
2015-08-10 23:45:11 +00:00
|
|
|
more = compilearg(gs, VAL_CODE, prevargs + 2);
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 inst1 = gs.code[start1], op1 = inst1 & ~CODE_RET_MASK, len1 = start2 - (start1 + 1);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
|
|
|
if (op1 == (CODE_BLOCK | (len1 << 8))) {
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code[start1] = (len1 << 8) | CODE_JUMP_FALSE;
|
|
|
|
gs.code[start1 + 1] = CODE_ENTER_RESULT;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code[start1 + len1] = (gs.code[start1 + len1] & ~CODE_RET_MASK) | cs_ret_code(rettype);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-08-07 18:28:09 +00:00
|
|
|
compileblock(gs);
|
2015-08-05 21:58:45 +00:00
|
|
|
} else {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 inst2 = gs.code[start2], op2 = inst2 & ~CODE_RET_MASK, len2 = gs.code.size() - (start2 + 1);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (op2 == (CODE_BLOCK | (len2 << 8))) {
|
|
|
|
if (op1 == (CODE_BLOCK | (len1 << 8))) {
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code[start1] = ((start2 - start1) << 8) | CODE_JUMP_FALSE;
|
|
|
|
gs.code[start1 + 1] = CODE_ENTER_RESULT;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code[start1 + len1] = (gs.code[start1 + len1] & ~CODE_RET_MASK) | cs_ret_code(rettype);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code[start2] = (len2 << 8) | CODE_JUMP;
|
|
|
|
gs.code[start2 + 1] = CODE_ENTER_RESULT;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code[start2 + len2] = (gs.code[start2 + len2] & ~CODE_RET_MASK) | cs_ret_code(rettype);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
} else if (op1 == (CODE_EMPTY | (len1 << 8))) {
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code[start1] = CODE_NULL | (inst2 & CODE_RET_MASK);
|
|
|
|
gs.code[start2] = (len2 << 8) | CODE_JUMP_TRUE;
|
|
|
|
gs.code[start2 + 1] = CODE_ENTER_RESULT;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code[start2 + len2] = (gs.code[start2 + len2] & ~CODE_RET_MASK) | cs_ret_code(rettype);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_COM | cs_ret_code(rettype) | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_RESULT:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_ANY, prevargs);
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push((more ? CODE_RESULT : CODE_NULL) | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_NOT:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_CANY, prevargs);
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push((more ? CODE_NOT : CODE_TRUE) | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_AND:
|
|
|
|
case ID_OR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) more = compilearg(gs, VAL_COND, prevargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) {
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push((id->type == ID_AND ? CODE_TRUE : CODE_FALSE) | cs_ret_code(rettype));
|
2015-08-05 21:58:45 +00:00
|
|
|
} else {
|
|
|
|
numargs++;
|
2015-08-07 18:28:09 +00:00
|
|
|
int start = gs.code.size(), end = start;
|
2016-02-06 23:17:28 +00:00
|
|
|
while (numargs < MaxArguments) {
|
2015-08-10 23:45:11 +00:00
|
|
|
more = compilearg(gs, VAL_COND, prevargs + numargs);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!more) break;
|
|
|
|
numargs++;
|
2015-08-07 22:37:54 +00:00
|
|
|
if ((gs.code[end] & ~CODE_RET_MASK) != (CODE_BLOCK | (ostd::Uint32(gs.code.size() - (end + 1)) << 8))) break;
|
2015-08-07 18:28:09 +00:00
|
|
|
end = gs.code.size();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
if (more) {
|
2016-02-06 23:17:28 +00:00
|
|
|
while (numargs < MaxArguments && (more = compilearg(gs, VAL_COND, prevargs + numargs))) numargs++;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code.push(CODE_COMV | cs_ret_code(rettype) | (numargs << 8) | (id->index << 13));
|
2015-08-05 21:58:45 +00:00
|
|
|
} else {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 op = id->type == ID_AND ? CODE_JUMP_RESULT_FALSE : CODE_JUMP_RESULT_TRUE;
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(op);
|
|
|
|
end = gs.code.size();
|
2015-08-05 21:58:45 +00:00
|
|
|
while (start + 1 < end) {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = gs.code[start] >> 8;
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code[start] = ((end - (start + 1)) << 8) | op;
|
|
|
|
gs.code[start + 1] = CODE_ENTER;
|
2015-08-07 18:42:12 +00:00
|
|
|
gs.code[start + len] = (gs.code[start + len] & ~CODE_RET_MASK) | cs_ret_code(rettype);
|
2015-08-05 21:58:45 +00:00
|
|
|
start += len + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_VAR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_INT, prevargs))) gs.code.push(CODE_PRINT | (id->index << 8));
|
|
|
|
else if (!(id->flags & IDF_HEX) || !(more = compilearg(gs, VAL_INT, prevargs + 1))) gs.code.push(CODE_IVAR1 | (id->index << 8));
|
|
|
|
else if (!(more = compilearg(gs, VAL_INT, prevargs + 2))) gs.code.push(CODE_IVAR2 | (id->index << 8));
|
2015-08-07 18:28:09 +00:00
|
|
|
else gs.code.push(CODE_IVAR3 | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_FVAR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_FLOAT, prevargs))) gs.code.push(CODE_PRINT | (id->index << 8));
|
2015-08-07 18:28:09 +00:00
|
|
|
else gs.code.push(CODE_FVAR1 | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_SVAR:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (!(more = compilearg(gs, VAL_CSTR, prevargs))) gs.code.push(CODE_PRINT | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
else {
|
|
|
|
do ++numargs;
|
2016-02-06 23:17:28 +00:00
|
|
|
while (numargs < MaxArguments && (more = compilearg(gs, VAL_CANY, prevargs + numargs)));
|
2015-08-07 18:28:09 +00:00
|
|
|
if (numargs > 1) gs.code.push(CODE_CONC | RET_STR | (numargs << 8));
|
|
|
|
gs.code.push(CODE_SVAR1 | (id->index << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
endstatement:
|
2015-08-10 23:45:11 +00:00
|
|
|
if (more) while (compilearg(gs, VAL_POP));
|
|
|
|
gs.source += strcspn(gs.source, ")];/\n\0");
|
2015-08-11 21:47:25 +00:00
|
|
|
char c = gs.next_char();
|
2015-08-05 21:58:45 +00:00
|
|
|
switch (c) {
|
|
|
|
case '\0':
|
2015-08-12 17:50:02 +00:00
|
|
|
if (c != brak) cs_debug_code_line(gs.cs, line, "missing \"%c\"", brak);
|
2015-08-10 23:45:11 +00:00
|
|
|
gs.source--;
|
2015-08-05 21:58:45 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case ')':
|
|
|
|
case ']':
|
|
|
|
if (c == brak) return;
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code_line(gs.cs, line, "unexpected \"%c\"", c);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '/':
|
2015-08-11 21:57:06 +00:00
|
|
|
if (gs.current() == '/') gs.source += strcspn(gs.source, "\n\0");
|
2015-08-05 21:58:45 +00:00
|
|
|
goto endstatement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-09 00:17:06 +00:00
|
|
|
void GenState::gen_main(ostd::ConstCharRange s, int ret_type) {
|
2015-08-10 23:45:11 +00:00
|
|
|
source = s.data();
|
2015-08-05 21:58:45 +00:00
|
|
|
code.push(CODE_START);
|
2015-08-10 23:45:11 +00:00
|
|
|
compilestatements(*this, VAL_ANY);
|
2015-08-07 18:28:09 +00:00
|
|
|
code.push(CODE_EXIT | ((ret_type < VAL_ANY) ? (ret_type << CODE_RET) : 0));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
ostd::Uint32 *compilecode(CsState &cs, ostd::ConstCharRange str) {
|
|
|
|
GenState gs(cs);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(64);
|
2015-08-09 00:17:06 +00:00
|
|
|
gs.gen_main(str);
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 *code = new ostd::Uint32[gs.code.size()];
|
|
|
|
memcpy(code, gs.code.data(), gs.code.size() * sizeof(ostd::Uint32));
|
2015-08-05 21:58:45 +00:00
|
|
|
code[0] += 0x100;
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static inline ostd::Uint32 const *forcecode(CsState &cs, TaggedValue &v) {
|
2015-08-13 20:48:03 +00:00
|
|
|
if (v.get_type() != VAL_CODE) {
|
2015-08-07 19:32:50 +00:00
|
|
|
GenState gs(cs);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(64);
|
|
|
|
gs.gen_main(v.get_str());
|
2015-08-05 21:58:45 +00:00
|
|
|
v.cleanup();
|
2016-08-06 18:12:38 +00:00
|
|
|
v.set_code(reinterpret_cast<Bytecode *>(gs.code.disown() + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2016-08-06 18:12:38 +00:00
|
|
|
return reinterpret_cast<ostd::Uint32 const *>(v.code);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 19:32:50 +00:00
|
|
|
static inline void forcecond(CsState &cs, TaggedValue &v) {
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (v.get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
2015-08-07 19:32:50 +00:00
|
|
|
if (v.s[0]) forcecode(cs, v);
|
2015-08-05 21:58:45 +00:00
|
|
|
else v.set_int(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-08 16:07:01 +00:00
|
|
|
void bcode_ref(ostd::Uint32 *code) {
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!code) return;
|
|
|
|
switch (*code & CODE_OP_MASK) {
|
|
|
|
case CODE_START:
|
|
|
|
*code += 0x100;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (code[-1]&CODE_OP_MASK) {
|
|
|
|
case CODE_START:
|
|
|
|
code[-1] += 0x100;
|
|
|
|
break;
|
|
|
|
case CODE_OFFSET:
|
|
|
|
code -= int(code[-1] >> 8);
|
|
|
|
*code += 0x100;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-08 16:07:01 +00:00
|
|
|
void bcode_unref(ostd::Uint32 *code) {
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!code) return;
|
|
|
|
switch (*code & CODE_OP_MASK) {
|
|
|
|
case CODE_START:
|
|
|
|
*code -= 0x100;
|
|
|
|
if (int(*code) < 0x100) delete[] code;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (code[-1]&CODE_OP_MASK) {
|
|
|
|
case CODE_START:
|
|
|
|
code[-1] -= 0x100;
|
|
|
|
if (int(code[-1]) < 0x100) delete[] &code[-1];
|
|
|
|
break;
|
|
|
|
case CODE_OFFSET:
|
|
|
|
code -= int(code[-1] >> 8);
|
|
|
|
*code -= 0x100;
|
|
|
|
if (int(*code) < 0x100) delete[] code;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
BytecodeRef::BytecodeRef(Bytecode *v): p_code(v) {
|
|
|
|
bcode_ref(reinterpret_cast<ostd::Uint32 *>(p_code));
|
|
|
|
}
|
|
|
|
BytecodeRef::BytecodeRef(BytecodeRef const &v): p_code(v.p_code) {
|
|
|
|
bcode_ref(reinterpret_cast<ostd::Uint32 *>(p_code));
|
|
|
|
}
|
2015-12-24 14:57:19 +00:00
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
BytecodeRef::~BytecodeRef() {
|
|
|
|
bcode_unref(reinterpret_cast<ostd::Uint32 *>(p_code));
|
|
|
|
}
|
2015-12-24 14:57:19 +00:00
|
|
|
|
2016-08-06 17:38:05 +00:00
|
|
|
BytecodeRef &BytecodeRef::operator=(BytecodeRef const &v) {
|
2016-08-06 18:12:38 +00:00
|
|
|
bcode_unref(reinterpret_cast<ostd::Uint32 *>(p_code));
|
2015-12-24 14:57:19 +00:00
|
|
|
p_code = v.p_code;
|
2016-08-06 18:12:38 +00:00
|
|
|
bcode_ref(reinterpret_cast<ostd::Uint32 *>(p_code));
|
2015-12-24 14:57:19 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2016-08-06 17:38:05 +00:00
|
|
|
BytecodeRef &BytecodeRef::operator=(BytecodeRef &&v) {
|
2016-08-06 18:12:38 +00:00
|
|
|
bcode_unref(reinterpret_cast<ostd::Uint32 *>(p_code));
|
2015-12-24 14:57:19 +00:00
|
|
|
p_code = v.p_code;
|
|
|
|
v.p_code = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2016-08-03 22:03:44 +00:00
|
|
|
static ostd::Uint32 const *skipcode(ostd::Uint32 const *code, TaggedValue *result) {
|
2015-08-05 21:58:45 +00:00
|
|
|
int depth = 0;
|
|
|
|
for (;;) {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 op = *code++;
|
2015-08-05 21:58:45 +00:00
|
|
|
switch (op & 0xFF) {
|
|
|
|
case CODE_MACRO:
|
|
|
|
case CODE_VAL|RET_STR: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
|
|
|
code += len / sizeof(ostd::Uint32) + 1;
|
2015-08-05 21:58:45 +00:00
|
|
|
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: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2015-08-05 21:58:45 +00:00
|
|
|
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) {
|
2016-08-01 20:35:42 +00:00
|
|
|
if (result) {
|
|
|
|
force_arg(*result, op & CODE_RET_MASK);
|
|
|
|
}
|
2015-08-05 21:58:45 +00:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
--depth;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-11 17:21:18 +00:00
|
|
|
static inline void callcommand(CsState &cs, Ident *id, TaggedValue *args, TaggedValue &res, int numargs, bool lookup = false) {
|
2015-08-05 21:58:45 +00:00
|
|
|
int i = -1, fakeargs = 0;
|
|
|
|
bool rep = false;
|
2016-08-09 23:54:51 +00:00
|
|
|
for (char const *fmt = id->cargs; *fmt; fmt++) switch (*fmt) {
|
2015-08-05 21:58:45 +00:00
|
|
|
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;
|
2016-07-13 18:24:26 +00:00
|
|
|
args[i].set_str("");
|
2015-08-05 21:58:45 +00:00
|
|
|
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++;
|
2015-08-07 19:32:50 +00:00
|
|
|
} else forcecond(cs, args[i]);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
if (++i >= numargs) {
|
|
|
|
if (rep) break;
|
2016-08-06 18:12:38 +00:00
|
|
|
args[i].set_code(reinterpret_cast<Bytecode *>(emptyblock[VAL_NULL] + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
2015-08-07 19:32:50 +00:00
|
|
|
} else forcecode(cs, args[i]);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
if (++i >= numargs) {
|
|
|
|
if (rep) break;
|
2015-08-07 19:32:50 +00:00
|
|
|
args[i].set_ident(cs.dummy);
|
2015-08-05 21:58:45 +00:00
|
|
|
fakeargs++;
|
2015-08-07 19:32:50 +00:00
|
|
|
} else cs.force_ident(args[i]);
|
2015-08-05 21:58:45 +00:00
|
|
|
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);
|
2016-08-09 23:10:09 +00:00
|
|
|
auto buf = ostd::appender<ostd::String>();
|
|
|
|
cscript::util::tvals_concat(buf, ostd::iter(args, i), " ");
|
2016-07-31 23:49:28 +00:00
|
|
|
TaggedValue tv;
|
2016-08-09 23:10:09 +00:00
|
|
|
tv.set_mstr(buf.get().iter());
|
2016-08-11 17:21:18 +00:00
|
|
|
id->cb_cftv(TvalRange(&tv, 1), res);
|
2015-08-05 21:58:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
case 'V':
|
|
|
|
i = ostd::max(i + 1, numargs);
|
2016-08-11 17:21:18 +00:00
|
|
|
id->cb_cftv(ostd::iter(args, i), res);
|
2015-08-05 21:58:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
if (i + 1 < numargs) {
|
|
|
|
fmt -= *fmt - '0' + 1;
|
|
|
|
rep = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++i;
|
2016-08-11 17:21:18 +00:00
|
|
|
id->cb_cftv(TvalRange(args, i), res);
|
2015-08-05 21:58:45 +00:00
|
|
|
cleanup:
|
|
|
|
for (ostd::Size k = 0; k < ostd::Size(i); ++k) args[k].cleanup();
|
|
|
|
for (; i < numargs; i++) args[i].cleanup();
|
|
|
|
}
|
|
|
|
|
2016-07-21 17:47:24 +00:00
|
|
|
static constexpr int MaxRunDepth = 255;
|
2016-07-23 00:08:58 +00:00
|
|
|
static thread_local int rundepth = 0;
|
2015-08-05 21:58:45 +00:00
|
|
|
|
2016-07-17 17:50:40 +00:00
|
|
|
static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, TaggedValue &result) {
|
2015-08-05 21:58:45 +00:00
|
|
|
result.set_null();
|
2016-07-23 00:08:58 +00:00
|
|
|
if (rundepth >= MaxRunDepth) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, "exceeded recursion limit");
|
2016-07-16 18:54:30 +00:00
|
|
|
return skipcode(code, (&result == &no_ret) ? nullptr : &result);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2016-07-23 00:08:58 +00:00
|
|
|
++rundepth;
|
2015-08-05 21:58:45 +00:00
|
|
|
int numargs = 0;
|
2016-08-11 17:21:18 +00:00
|
|
|
TaggedValue args[MaxArguments + MaxResults];
|
2015-08-05 21:58:45 +00:00
|
|
|
for (;;) {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 op = *code++;
|
2015-08-05 21:58:45 +00:00
|
|
|
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())
|
2016-07-13 18:24:26 +00:00
|
|
|
RETOP(CODE_NULL | RET_STR, result.set_str(""))
|
2015-08-05 21:58:45 +00:00
|
|
|
RETOP(CODE_NULL | RET_INT, result.set_int(0))
|
|
|
|
RETOP(CODE_NULL | RET_FLOAT, result.set_float(0.0f))
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
RETOP(CODE_FALSE | RET_STR, result.set_str("0"))
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_FALSE|RET_NULL:
|
|
|
|
RETOP(CODE_FALSE | RET_INT, result.set_int(0))
|
|
|
|
RETOP(CODE_FALSE | RET_FLOAT, result.set_float(0.0f))
|
|
|
|
|
2016-07-13 18:24:26 +00:00
|
|
|
RETOP(CODE_TRUE | RET_STR, result.set_str("1"))
|
2015-08-05 21:58:45 +00:00
|
|
|
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(); })
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
RETPOP(CODE_NOT | RET_STR, result.set_str(args[numargs].get_bool() ? "0" : "1"))
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_NOT|RET_NULL:
|
2016-08-02 00:21:36 +00:00
|
|
|
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))
|
2015-08-05 21:58:45 +00:00
|
|
|
|
|
|
|
case CODE_POP:
|
|
|
|
args[--numargs].cleanup();
|
|
|
|
continue;
|
|
|
|
case CODE_ENTER:
|
2015-08-07 19:32:50 +00:00
|
|
|
code = runcode(cs, code, args[numargs++]);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_ENTER_RESULT:
|
|
|
|
result.cleanup();
|
2015-08-07 19:32:50 +00:00
|
|
|
code = runcode(cs, code, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_EXIT|RET_STR:
|
|
|
|
case CODE_EXIT|RET_INT:
|
|
|
|
case CODE_EXIT|RET_FLOAT:
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
/* 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:
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
/* fallthrough */
|
|
|
|
case CODE_RESULT_ARG|RET_NULL:
|
|
|
|
args[numargs++] = result;
|
|
|
|
result.set_null();
|
|
|
|
continue;
|
|
|
|
case CODE_PRINT:
|
2015-08-07 20:38:57 +00:00
|
|
|
cs.print_var(cs.identmap[op >> 8]);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case CODE_LOCAL: {
|
|
|
|
result.cleanup();
|
|
|
|
int numlocals = op >> 8, offset = numargs - numlocals;
|
2016-02-06 23:17:28 +00:00
|
|
|
IdentStack locals[MaxArguments];
|
2015-08-06 22:16:02 +00:00
|
|
|
for (int i = 0; i < numlocals; ++i) args[offset + i].id->push_alias(locals[i]);
|
2015-08-07 19:32:50 +00:00
|
|
|
code = runcode(cs, code, result);
|
2015-08-06 22:16:02 +00:00
|
|
|
for (int i = offset; i < numargs; i++) args[i].id->pop_alias();
|
2015-08-05 21:58:45 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CODE_DOARGS|RET_NULL:
|
|
|
|
case CODE_DOARGS|RET_STR:
|
|
|
|
case CODE_DOARGS|RET_INT:
|
|
|
|
case CODE_DOARGS|RET_FLOAT:
|
2015-08-07 19:32:50 +00:00
|
|
|
if (cs.stack != &cs.noalias) {
|
|
|
|
cs_do_args(cs, [&]() {
|
2015-08-06 22:02:56 +00:00
|
|
|
result.cleanup();
|
2016-08-11 17:47:04 +00:00
|
|
|
cs.run_ret(args[--numargs].code, result);
|
2015-08-06 22:02:56 +00:00
|
|
|
args[numargs].cleanup();
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-06 22:02:56 +00:00
|
|
|
});
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case CODE_DO|RET_NULL:
|
|
|
|
case CODE_DO|RET_STR:
|
|
|
|
case CODE_DO|RET_INT:
|
|
|
|
case CODE_DO|RET_FLOAT:
|
|
|
|
result.cleanup();
|
2016-08-11 17:47:04 +00:00
|
|
|
cs.run_ret(args[--numargs].code, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
args[numargs].cleanup();
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case CODE_JUMP: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2015-08-05 21:58:45 +00:00
|
|
|
code += len;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_JUMP_TRUE: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2016-08-02 00:21:36 +00:00
|
|
|
if (args[--numargs].get_bool()) code += len;
|
2015-08-05 21:58:45 +00:00
|
|
|
args[numargs].cleanup();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_JUMP_FALSE: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2016-08-02 00:21:36 +00:00
|
|
|
if (!args[--numargs].get_bool()) code += len;
|
2015-08-05 21:58:45 +00:00
|
|
|
args[numargs].cleanup();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_JUMP_RESULT_TRUE: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2015-08-05 21:58:45 +00:00
|
|
|
result.cleanup();
|
|
|
|
--numargs;
|
2015-08-13 20:48:03 +00:00
|
|
|
if (args[numargs].get_type() == VAL_CODE) {
|
2016-08-11 17:47:04 +00:00
|
|
|
cs.run_ret(args[numargs].code, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
args[numargs].cleanup();
|
|
|
|
} else result = args[numargs];
|
2016-08-02 00:21:36 +00:00
|
|
|
if (result.get_bool()) code += len;
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_JUMP_RESULT_FALSE: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2015-08-05 21:58:45 +00:00
|
|
|
result.cleanup();
|
|
|
|
--numargs;
|
2015-08-13 20:48:03 +00:00
|
|
|
if (args[numargs].get_type() == VAL_CODE) {
|
2016-08-11 17:47:04 +00:00
|
|
|
cs.run_ret(args[numargs].code, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
args[numargs].cleanup();
|
|
|
|
} else result = args[numargs];
|
2016-08-02 00:21:36 +00:00
|
|
|
if (!result.get_bool()) code += len;
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CODE_MACRO: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2016-08-06 18:51:32 +00:00
|
|
|
cs_set_macro(args[numargs++], reinterpret_cast<Bytecode const *>(code), len);
|
2015-08-07 22:37:54 +00:00
|
|
|
code += len / sizeof(ostd::Uint32) + 1;
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CODE_VAL|RET_STR: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2016-07-17 17:50:40 +00:00
|
|
|
args[numargs++].set_str(ostd::ConstCharRange(reinterpret_cast<char const *>(code), len));
|
2015-08-07 22:37:54 +00:00
|
|
|
code += len / sizeof(ostd::Uint32) + 1;
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_VALI|RET_STR: {
|
|
|
|
char s[4] = { char((op >> 8) & 0xFF), char((op >> 16) & 0xFF), char((op >> 24) & 0xFF), '\0' };
|
2016-07-13 18:24:26 +00:00
|
|
|
args[numargs++].set_str(s);
|
2015-08-05 21:58:45 +00:00
|
|
|
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:
|
2016-07-17 17:50:40 +00:00
|
|
|
args[numargs++].set_float(*reinterpret_cast<float const *>(code++));
|
2015-08-05 21:58:45 +00:00
|
|
|
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;
|
2015-08-13 21:55:30 +00:00
|
|
|
case CODE_DUP|RET_STR:
|
2016-07-13 18:24:26 +00:00
|
|
|
args[numargs].set_str(ostd::move(args[numargs - 1].get_str()));
|
2015-08-05 21:58:45 +00:00
|
|
|
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];
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case CODE_EMPTY|RET_NULL:
|
2016-08-06 18:12:38 +00:00
|
|
|
args[numargs++].set_code(reinterpret_cast<Bytecode *>(emptyblock[VAL_NULL] + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case CODE_EMPTY|RET_STR:
|
2016-08-06 18:12:38 +00:00
|
|
|
args[numargs++].set_code(reinterpret_cast<Bytecode *>(emptyblock[VAL_STR] + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case CODE_EMPTY|RET_INT:
|
2016-08-06 18:12:38 +00:00
|
|
|
args[numargs++].set_code(reinterpret_cast<Bytecode *>(emptyblock[VAL_INT] + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case CODE_EMPTY|RET_FLOAT:
|
2016-08-06 18:12:38 +00:00
|
|
|
args[numargs++].set_code(reinterpret_cast<Bytecode *>(emptyblock[VAL_FLOAT] + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case CODE_BLOCK: {
|
2015-08-07 22:37:54 +00:00
|
|
|
ostd::Uint32 len = op >> 8;
|
2016-08-06 18:12:38 +00:00
|
|
|
args[numargs++].set_code(reinterpret_cast<Bytecode const *>(code + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
code += len;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_COMPILE: {
|
|
|
|
TaggedValue &arg = args[numargs - 1];
|
2015-08-07 19:32:50 +00:00
|
|
|
GenState gs(cs);
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (arg.get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_INT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(8);
|
|
|
|
gs.code.push(CODE_START);
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_int(arg.i);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_RESULT);
|
|
|
|
gs.code.push(CODE_EXIT);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(8);
|
|
|
|
gs.code.push(CODE_START);
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_float(arg.f);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_RESULT);
|
|
|
|
gs.code.push(CODE_EXIT);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(64);
|
|
|
|
gs.gen_main(arg.s);
|
2015-08-05 21:58:45 +00:00
|
|
|
arg.cleanup();
|
|
|
|
break;
|
|
|
|
default:
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(8);
|
|
|
|
gs.code.push(CODE_START);
|
2015-08-07 19:14:31 +00:00
|
|
|
gs.gen_null();
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.push(CODE_RESULT);
|
|
|
|
gs.code.push(CODE_EXIT);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-08-06 18:12:38 +00:00
|
|
|
arg.set_code(reinterpret_cast<Bytecode const *>(gs.code.disown() + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_COND: {
|
|
|
|
TaggedValue &arg = args[numargs - 1];
|
2015-08-13 20:48:03 +00:00
|
|
|
switch (arg.get_type()) {
|
2015-08-05 21:58:45 +00:00
|
|
|
case VAL_STR:
|
|
|
|
case VAL_MACRO:
|
|
|
|
case VAL_CSTR:
|
|
|
|
if (arg.s[0]) {
|
2015-08-07 19:32:50 +00:00
|
|
|
GenState gs(cs);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(64);
|
|
|
|
gs.gen_main(arg.s);
|
2015-08-05 21:58:45 +00:00
|
|
|
arg.cleanup();
|
2016-08-06 18:12:38 +00:00
|
|
|
arg.set_code(reinterpret_cast<Bytecode const *>(gs.code.disown() + 1));
|
2015-08-05 21:58:45 +00:00
|
|
|
} else arg.force_null();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CODE_IDENT:
|
2015-08-07 19:32:50 +00:00
|
|
|
args[numargs++].set_ident(cs.identmap[op >> 8]);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_IDENTARG: {
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op >> 8];
|
|
|
|
if (!(cs.stack->usedargs & (1 << id->index))) {
|
2015-08-13 18:11:54 +00:00
|
|
|
id->push_arg(null_value, cs.stack->argstack[id->index], false);
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.stack->usedargs |= 1 << id->index;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
args[numargs++].set_ident(id);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_IDENTU: {
|
|
|
|
TaggedValue &arg = args[numargs - 1];
|
2016-08-01 20:46:50 +00:00
|
|
|
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;
|
2016-02-06 23:17:28 +00:00
|
|
|
if (id->index < MaxArguments && !(cs.stack->usedargs & (1 << id->index))) {
|
2015-08-13 18:11:54 +00:00
|
|
|
id->push_arg(null_value, cs.stack->argstack[id->index], false);
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.stack->usedargs |= 1 << id->index;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
arg.cleanup();
|
|
|
|
arg.set_ident(id);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CODE_LOOKUPU|RET_STR:
|
|
|
|
#define LOOKUPU(aval, sval, ival, fval, nval) { \
|
|
|
|
TaggedValue &arg = args[numargs-1]; \
|
2015-08-13 20:48:03 +00:00
|
|
|
if(arg.get_type() != VAL_STR && arg.get_type() != VAL_MACRO && arg.get_type() != VAL_CSTR) continue; \
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.idents.at(arg.s); \
|
2015-08-05 21:58:45 +00:00
|
|
|
if(id) switch(id->type) \
|
|
|
|
{ \
|
|
|
|
case ID_ALIAS: \
|
|
|
|
if(id->flags&IDF_UNKNOWN) break; \
|
|
|
|
arg.cleanup(); \
|
2016-02-06 23:17:28 +00:00
|
|
|
if(id->index < MaxArguments && !(cs.stack->usedargs&(1<<id->index))) { nval; continue; } \
|
2015-08-05 21:58:45 +00:00
|
|
|
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(); \
|
2016-02-06 23:17:28 +00:00
|
|
|
TaggedValue buf[MaxArguments]; \
|
2016-08-11 17:21:18 +00:00
|
|
|
callcommand(cs, id, buf, arg, 0, true); \
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(arg, op&CODE_RET_MASK); \
|
2015-08-05 21:58:45 +00:00
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
default: arg.cleanup(); nval; continue; \
|
|
|
|
} \
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, "unknown alias lookup: %s", arg.s); \
|
2015-08-05 21:58:45 +00:00
|
|
|
arg.cleanup(); \
|
|
|
|
nval; \
|
|
|
|
continue; \
|
|
|
|
}
|
2016-07-13 18:24:26 +00:00
|
|
|
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(""));
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_LOOKUP|RET_STR:
|
|
|
|
#define LOOKUP(aval) { \
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op>>8]; \
|
2015-08-12 17:50:02 +00:00
|
|
|
if(id->flags&IDF_UNKNOWN) cs_debug_code(cs, "unknown alias lookup: %s", id->name); \
|
2015-08-05 21:58:45 +00:00
|
|
|
aval; \
|
|
|
|
continue; \
|
|
|
|
}
|
2016-07-13 18:24:26 +00:00
|
|
|
LOOKUP(args[numargs++].set_str(ostd::move(id->get_str())));
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_LOOKUPARG|RET_STR:
|
|
|
|
#define LOOKUPARG(aval, nval) { \
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op>>8]; \
|
|
|
|
if(!(cs.stack->usedargs&(1<<id->index))) { nval; continue; } \
|
2015-08-05 21:58:45 +00:00
|
|
|
aval; \
|
|
|
|
continue; \
|
|
|
|
}
|
2016-07-13 18:24:26 +00:00
|
|
|
LOOKUPARG(args[numargs++].set_str(ostd::move(id->get_str())), args[numargs++].set_str(""));
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_LOOKUPU|RET_INT:
|
|
|
|
LOOKUPU(arg.set_int(id->get_int()),
|
2015-08-13 20:08:57 +00:00
|
|
|
arg.set_int(parseint(*id->storage.sp)),
|
|
|
|
arg.set_int(*id->storage.ip),
|
|
|
|
arg.set_int(int(*id->storage.fp)),
|
2015-08-05 21:58:45 +00:00
|
|
|
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()),
|
2015-08-13 20:08:57 +00:00
|
|
|
arg.set_float(parsefloat(*id->storage.sp)),
|
|
|
|
arg.set_float(float(*id->storage.ip)),
|
|
|
|
arg.set_float(*id->storage.fp),
|
2015-08-05 21:58:45 +00:00
|
|
|
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),
|
2016-07-13 18:24:26 +00:00
|
|
|
arg.set_str(*id->storage.sp),
|
2015-08-13 20:08:57 +00:00
|
|
|
arg.set_int(*id->storage.ip),
|
|
|
|
arg.set_float(*id->storage.fp),
|
2015-08-05 21:58:45 +00:00
|
|
|
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:
|
2015-08-11 20:41:12 +00:00
|
|
|
LOOKUPU(id->get_cstr(arg),
|
2015-08-13 20:08:57 +00:00
|
|
|
arg.set_cstr(*id->storage.sp),
|
2016-07-13 18:24:26 +00:00
|
|
|
arg.set_str(ostd::move(intstr(*id->storage.ip))),
|
|
|
|
arg.set_str(ostd::move(floatstr(*id->storage.fp))),
|
2015-08-05 21:58:45 +00:00
|
|
|
arg.set_cstr(""));
|
|
|
|
case CODE_LOOKUPM|RET_STR:
|
2015-08-11 20:41:12 +00:00
|
|
|
LOOKUP(id->get_cstr(args[numargs++]));
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_LOOKUPMARG|RET_STR:
|
2015-08-11 20:41:12 +00:00
|
|
|
LOOKUPARG(id->get_cstr(args[numargs++]), args[numargs++].set_cstr(""));
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_LOOKUPMU|RET_NULL:
|
2015-08-11 20:41:12 +00:00
|
|
|
LOOKUPU(id->get_cval(arg),
|
2015-08-13 20:08:57 +00:00
|
|
|
arg.set_cstr(*id->storage.sp),
|
|
|
|
arg.set_int(*id->storage.ip),
|
|
|
|
arg.set_float(*id->storage.fp),
|
2015-08-05 21:58:45 +00:00
|
|
|
arg.set_null());
|
|
|
|
case CODE_LOOKUPM|RET_NULL:
|
2015-08-11 20:41:12 +00:00
|
|
|
LOOKUP(id->get_cval(args[numargs++]));
|
2015-08-05 21:58:45 +00:00
|
|
|
case CODE_LOOKUPMARG|RET_NULL:
|
2015-08-11 20:41:12 +00:00
|
|
|
LOOKUPARG(id->get_cval(args[numargs++]), args[numargs++].set_null());
|
2015-08-05 21:58:45 +00:00
|
|
|
|
|
|
|
case CODE_SVAR|RET_STR:
|
|
|
|
case CODE_SVAR|RET_NULL:
|
2016-07-13 18:24:26 +00:00
|
|
|
args[numargs++].set_str(*cs.identmap[op >> 8]->storage.sp);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_SVAR|RET_INT:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_int(parseint(*cs.identmap[op >> 8]->storage.sp));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_SVAR|RET_FLOAT:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_float(parsefloat(*cs.identmap[op >> 8]->storage.sp));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_SVARM:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_cstr(*cs.identmap[op >> 8]->storage.sp);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_SVAR1:
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.set_var_str_checked(cs.identmap[op >> 8], args[--numargs].s);
|
2015-08-05 21:58:45 +00:00
|
|
|
args[numargs].cleanup();
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case CODE_IVAR|RET_INT:
|
|
|
|
case CODE_IVAR|RET_NULL:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_int(*cs.identmap[op >> 8]->storage.ip);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_IVAR|RET_STR:
|
2016-07-13 18:24:26 +00:00
|
|
|
args[numargs++].set_str(ostd::move(intstr(*cs.identmap[op >> 8]->storage.ip)));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_IVAR|RET_FLOAT:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_float(float(*cs.identmap[op >> 8]->storage.ip));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_IVAR1:
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.set_var_int_checked(cs.identmap[op >> 8], args[--numargs].i);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_IVAR2:
|
|
|
|
numargs -= 2;
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.set_var_int_checked(cs.identmap[op >> 8], (args[numargs].i << 16) | (args[numargs + 1].i << 8));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_IVAR3:
|
|
|
|
numargs -= 3;
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.set_var_int_checked(cs.identmap[op >> 8], (args[numargs].i << 16) | (args[numargs + 1].i << 8) | args[numargs + 2].i);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case CODE_FVAR|RET_FLOAT:
|
|
|
|
case CODE_FVAR|RET_NULL:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_float(*cs.identmap[op >> 8]->storage.fp);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_FVAR|RET_STR:
|
2016-07-13 18:24:26 +00:00
|
|
|
args[numargs++].set_str(ostd::move(floatstr(*cs.identmap[op >> 8]->storage.fp)));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_FVAR|RET_INT:
|
2015-08-13 20:08:57 +00:00
|
|
|
args[numargs++].set_int(int(*cs.identmap[op >> 8]->storage.fp));
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_FVAR1:
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.set_var_float_checked(cs.identmap[op >> 8], args[--numargs].f);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case CODE_COM|RET_NULL:
|
|
|
|
case CODE_COM|RET_STR:
|
|
|
|
case CODE_COM|RET_FLOAT:
|
|
|
|
case CODE_COM|RET_INT: {
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op >> 8];
|
2015-08-05 21:58:45 +00:00
|
|
|
int offset = numargs - id->numargs;
|
|
|
|
result.force_null();
|
2016-08-11 17:21:18 +00:00
|
|
|
id->cb_cftv(TvalRange(args + offset, id->numargs), result);
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
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: {
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op >> 13];
|
2015-08-05 21:58:45 +00:00
|
|
|
int callargs = (op >> 8) & 0x1F, offset = numargs - callargs;
|
|
|
|
result.force_null();
|
2016-08-11 17:21:18 +00:00
|
|
|
id->cb_cftv(ostd::iter(&args[offset], callargs), result);
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
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: {
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op >> 13];
|
2015-08-05 21:58:45 +00:00
|
|
|
int callargs = (op >> 8) & 0x1F, offset = numargs - callargs;
|
|
|
|
result.force_null();
|
|
|
|
{
|
2016-08-09 23:10:09 +00:00
|
|
|
auto buf = ostd::appender<ostd::String>();
|
|
|
|
cscript::util::tvals_concat(buf, ostd::iter(&args[offset], callargs), " ");
|
2016-07-31 23:49:28 +00:00
|
|
|
TaggedValue tv;
|
2016-08-09 23:10:09 +00:00
|
|
|
tv.set_mstr(buf.get().iter());
|
2016-08-11 17:21:18 +00:00
|
|
|
id->cb_cftv(TvalRange(&tv, 1), result);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
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;
|
2016-08-09 23:10:09 +00:00
|
|
|
auto buf = ostd::appender<ostd::String>();
|
|
|
|
cscript::util::tvals_concat(buf, ostd::iter(&args[numargs - numconc], numconc), ((op & CODE_OP_MASK) == CODE_CONC) ? " " : "");
|
2015-08-05 21:58:45 +00:00
|
|
|
free_args(args, numargs, numargs - numconc);
|
2016-08-09 23:10:09 +00:00
|
|
|
args[numargs].set_mstr(buf.get().iter());
|
|
|
|
buf.get().disown();
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(args[numargs], op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
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;
|
2016-08-09 23:10:09 +00:00
|
|
|
auto buf = ostd::appender<ostd::String>();
|
|
|
|
cscript::util::tvals_concat(buf, ostd::iter(&args[numargs - numconc], numconc));
|
2015-08-05 21:58:45 +00:00
|
|
|
free_args(args, numargs, numargs - numconc);
|
2016-08-09 23:10:09 +00:00
|
|
|
result.set_mstr(buf.get().iter());
|
|
|
|
buf.get().disown();
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CODE_ALIAS:
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.identmap[op >> 8]->set_alias(cs, args[--numargs]);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_ALIASARG:
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.identmap[op >> 8]->set_arg(cs, args[--numargs]);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
case CODE_ALIASU:
|
|
|
|
numargs -= 2;
|
2015-08-07 19:32:50 +00:00
|
|
|
cs.set_alias(args[numargs].get_str(), args[numargs + 1]);
|
2015-08-05 21:58:45 +00:00
|
|
|
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)); \
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op&CODE_RET_MASK); \
|
2015-08-05 21:58:45 +00:00
|
|
|
continue; \
|
|
|
|
}
|
2015-08-07 20:04:31 +00:00
|
|
|
#define CALLALIAS(cs, result) { \
|
2016-02-06 23:17:28 +00:00
|
|
|
IdentStack argstack[MaxArguments]; \
|
2015-08-05 21:58:45 +00:00
|
|
|
for(int i = 0; i < callargs; i++) \
|
2015-08-13 18:11:54 +00:00
|
|
|
(cs).identmap[i]->push_arg(args[offset + i], argstack[i], false); \
|
2015-08-07 20:04:31 +00:00
|
|
|
int oldargs = (cs).numargs; \
|
|
|
|
(cs).numargs = callargs; \
|
|
|
|
int oldflags = (cs).identflags; \
|
|
|
|
(cs).identflags |= id->flags&IDF_OVERRIDDEN; \
|
|
|
|
IdentLink aliaslink = { id, (cs).stack, (1<<callargs)-1, argstack }; \
|
|
|
|
(cs).stack = &aliaslink; \
|
2016-08-06 18:12:38 +00:00
|
|
|
if(!id->code) id->code = reinterpret_cast<Bytecode *>(compilecode(cs, id->get_str())); \
|
|
|
|
ostd::Uint32 *codep = reinterpret_cast<ostd::Uint32 *>(id->code); \
|
2016-03-04 21:28:41 +00:00
|
|
|
codep[0] += 0x100; \
|
|
|
|
runcode((cs), codep+1, (result)); \
|
|
|
|
codep[0] -= 0x100; \
|
|
|
|
if(int(codep[0]) < 0x100) delete[] codep; \
|
2015-08-07 20:04:31 +00:00
|
|
|
(cs).stack = aliaslink.next; \
|
|
|
|
(cs).identflags = oldflags; \
|
2015-08-05 21:58:45 +00:00
|
|
|
for(int i = 0; i < callargs; i++) \
|
2015-08-07 20:04:31 +00:00
|
|
|
(cs).identmap[i]->pop_arg(); \
|
2015-08-05 21:58:45 +00:00
|
|
|
for(int argmask = aliaslink.usedargs&(~0<<callargs), i = callargs; argmask; i++) \
|
2015-08-07 20:04:31 +00:00
|
|
|
if(argmask&(1<<i)) { (cs).identmap[i]->pop_arg(); argmask &= ~(1<<i); } \
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op&CODE_RET_MASK); \
|
2015-08-07 20:04:31 +00:00
|
|
|
(cs).numargs = oldargs; \
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs = SKIPARGS(offset); \
|
|
|
|
}
|
|
|
|
result.force_null();
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op >> 13];
|
2015-08-05 21:58:45 +00:00
|
|
|
int callargs = (op >> 8) & 0x1F, offset = numargs - callargs;
|
|
|
|
if (id->flags & IDF_UNKNOWN) {
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, "unknown command: %s", id->name);
|
2015-08-05 21:58:45 +00:00
|
|
|
FORCERESULT;
|
|
|
|
}
|
2015-08-07 20:04:31 +00:00
|
|
|
CALLALIAS(cs, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case CODE_CALLARG|RET_NULL:
|
|
|
|
case CODE_CALLARG|RET_STR:
|
|
|
|
case CODE_CALLARG|RET_FLOAT:
|
|
|
|
case CODE_CALLARG|RET_INT: {
|
|
|
|
result.force_null();
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.identmap[op >> 13];
|
2015-08-05 21:58:45 +00:00
|
|
|
int callargs = (op >> 8) & 0x1F, offset = numargs - callargs;
|
2015-08-07 19:32:50 +00:00
|
|
|
if (!(cs.stack->usedargs & (1 << id->index))) FORCERESULT;
|
2015-08-07 20:04:31 +00:00
|
|
|
CALLALIAS(cs, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
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];
|
2015-08-13 20:48:03 +00:00
|
|
|
if (idarg.get_type() != VAL_STR && idarg.get_type() != VAL_MACRO && idarg.get_type() != VAL_CSTR) {
|
2015-08-05 21:58:45 +00:00
|
|
|
litval:
|
|
|
|
result.cleanup();
|
|
|
|
result = idarg;
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
while (--numargs >= offset) args[numargs].cleanup();
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-07 19:32:50 +00:00
|
|
|
Ident *id = cs.idents.at(idarg.s);
|
2015-08-05 21:58:45 +00:00
|
|
|
if (!id) {
|
|
|
|
noid:
|
2015-08-08 16:07:01 +00:00
|
|
|
if (cs_check_num(idarg.s)) goto litval;
|
2015-08-12 17:50:02 +00:00
|
|
|
cs_debug_code(cs, "unknown command: %s", idarg.s);
|
2015-08-05 21:58:45 +00:00
|
|
|
result.force_null();
|
|
|
|
FORCERESULT;
|
|
|
|
}
|
|
|
|
result.force_null();
|
|
|
|
switch (id->type) {
|
|
|
|
default:
|
2016-08-01 00:10:21 +00:00
|
|
|
if (!id->cb_cftv) FORCERESULT;
|
2015-08-05 21:58:45 +00:00
|
|
|
/* fallthrough */
|
|
|
|
case ID_COMMAND:
|
|
|
|
idarg.cleanup();
|
2016-08-11 17:21:18 +00:00
|
|
|
callcommand(cs, id, &args[offset], result, callargs);
|
2016-08-01 20:35:42 +00:00
|
|
|
force_arg(result, op & CODE_RET_MASK);
|
2015-08-05 21:58:45 +00:00
|
|
|
numargs = offset - 1;
|
|
|
|
continue;
|
|
|
|
case ID_LOCAL: {
|
2016-02-06 23:17:28 +00:00
|
|
|
IdentStack locals[MaxArguments];
|
2015-08-05 21:58:45 +00:00
|
|
|
idarg.cleanup();
|
2015-08-07 19:32:50 +00:00
|
|
|
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);
|
2015-08-06 22:16:02 +00:00
|
|
|
for (ostd::Size j = 0; j < ostd::Size(callargs); ++j) args[offset + j].id->pop_alias();
|
2015-08-05 21:58:45 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
case ID_VAR:
|
2015-08-07 20:38:57 +00:00
|
|
|
if (callargs <= 0) cs.print_var(id);
|
2015-08-07 19:32:50 +00:00
|
|
|
else cs.set_var_int_checked(id, ostd::iter(&args[offset], callargs));
|
2015-08-05 21:58:45 +00:00
|
|
|
FORCERESULT;
|
|
|
|
case ID_FVAR:
|
2015-08-07 20:38:57 +00:00
|
|
|
if (callargs <= 0) cs.print_var(id);
|
2015-08-07 19:32:50 +00:00
|
|
|
else cs.set_var_float_checked(id, args[offset].force_float());
|
2015-08-05 21:58:45 +00:00
|
|
|
FORCERESULT;
|
|
|
|
case ID_SVAR:
|
2015-08-07 20:38:57 +00:00
|
|
|
if (callargs <= 0) cs.print_var(id);
|
2015-08-07 19:32:50 +00:00
|
|
|
else cs.set_var_str_checked(id, args[offset].force_str());
|
2015-08-05 21:58:45 +00:00
|
|
|
FORCERESULT;
|
|
|
|
case ID_ALIAS:
|
2016-02-06 23:17:28 +00:00
|
|
|
if (id->index < MaxArguments && !(cs.stack->usedargs & (1 << id->index))) FORCERESULT;
|
2015-08-13 21:23:46 +00:00
|
|
|
if (id->get_valtype() == VAL_NULL) goto noid;
|
2015-08-05 21:58:45 +00:00
|
|
|
idarg.cleanup();
|
2015-08-07 20:04:31 +00:00
|
|
|
CALLALIAS(cs, result);
|
2015-08-05 21:58:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#undef SKIPARGS
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
2016-07-23 00:08:58 +00:00
|
|
|
--rundepth;
|
2015-08-05 21:58:45 +00:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
void CsState::run_ret(Bytecode const *code, TaggedValue &ret) {
|
|
|
|
runcode(*this, reinterpret_cast<ostd::Uint32 const *>(code), ret);
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 21:28:41 +00:00
|
|
|
void CsState::run_ret(ostd::ConstCharRange code, TaggedValue &ret) {
|
2015-08-07 20:04:31 +00:00
|
|
|
GenState gs(*this);
|
2015-08-07 18:28:09 +00:00
|
|
|
gs.code.reserve(64);
|
2015-08-07 20:04:31 +00:00
|
|
|
/* FIXME range */
|
|
|
|
gs.gen_main(code.data(), VAL_ANY);
|
2016-03-04 21:28:41 +00:00
|
|
|
runcode(*this, gs.code.data() + 1, ret);
|
2015-08-07 20:04:31 +00:00
|
|
|
if (int(gs.code[0]) >= 0x100)
|
|
|
|
gs.code.disown();
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 20:04:31 +00:00
|
|
|
/* TODO */
|
2015-08-27 08:16:22 +00:00
|
|
|
void CsState::run_ret(Ident *id, TvalRange args, TaggedValue &ret) {
|
2016-03-04 21:28:41 +00:00
|
|
|
int nargs = int(args.size());
|
2015-08-07 20:04:31 +00:00
|
|
|
ret.set_null();
|
2015-08-05 21:58:45 +00:00
|
|
|
++rundepth;
|
2016-07-21 17:47:24 +00:00
|
|
|
if (rundepth > MaxRunDepth) cs_debug_code(*this, "exceeded recursion limit");
|
2015-08-05 21:58:45 +00:00
|
|
|
else if (id) switch (id->type) {
|
|
|
|
default:
|
2016-08-01 00:10:21 +00:00
|
|
|
if (!id->cb_cftv) break;
|
2015-08-05 21:58:45 +00:00
|
|
|
/* fallthrough */
|
|
|
|
case ID_COMMAND:
|
2016-03-04 21:28:41 +00:00
|
|
|
if (nargs < id->numargs) {
|
2016-02-06 23:17:28 +00:00
|
|
|
TaggedValue buf[MaxArguments];
|
2015-08-07 20:04:31 +00:00
|
|
|
memcpy(buf, args.data(), args.size() * sizeof(TaggedValue));
|
2016-08-11 17:21:18 +00:00
|
|
|
callcommand(*this, id, buf, ret, nargs, false);
|
|
|
|
} else callcommand(*this, id, args.data(), ret, nargs, false);
|
2016-03-04 21:28:41 +00:00
|
|
|
nargs = 0;
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_VAR:
|
2015-08-07 20:38:57 +00:00
|
|
|
if (args.empty()) print_var(id);
|
2015-08-07 20:04:31 +00:00
|
|
|
else set_var_int_checked(id, args);
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_FVAR:
|
2015-08-07 20:38:57 +00:00
|
|
|
if (args.empty()) print_var(id);
|
2015-08-07 20:04:31 +00:00
|
|
|
else set_var_float_checked(id, args[0].force_float());
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_SVAR:
|
2015-08-07 20:38:57 +00:00
|
|
|
if (args.empty()) print_var(id);
|
2015-08-07 20:04:31 +00:00
|
|
|
else set_var_str_checked(id, args[0].force_str());
|
2015-08-05 21:58:45 +00:00
|
|
|
break;
|
|
|
|
case ID_ALIAS:
|
2016-02-06 23:17:28 +00:00
|
|
|
if (id->index < MaxArguments && !(stack->usedargs & (1 << id->index))) break;
|
2015-08-13 21:23:46 +00:00
|
|
|
if (id->get_valtype() == VAL_NULL) break;
|
2016-03-04 21:28:41 +00:00
|
|
|
#define callargs nargs
|
2015-08-05 21:58:45 +00:00
|
|
|
#define offset 0
|
|
|
|
#define op RET_NULL
|
|
|
|
#define SKIPARGS(offset) offset
|
2015-08-07 20:04:31 +00:00
|
|
|
CALLALIAS(*this, ret);
|
2015-08-05 21:58:45 +00:00
|
|
|
#undef callargs
|
|
|
|
#undef offset
|
|
|
|
#undef op
|
|
|
|
#undef SKIPARGS
|
|
|
|
break;
|
|
|
|
}
|
2016-03-04 21:28:41 +00:00
|
|
|
free_args(args.data(), nargs, 0);
|
2015-08-05 21:58:45 +00:00
|
|
|
--rundepth;
|
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
ostd::String CsState::run_str(Bytecode const *code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
2016-08-11 16:21:26 +00:00
|
|
|
run_ret(code, ret);
|
|
|
|
ostd::String s = ret.get_str();
|
|
|
|
ret.cleanup();
|
|
|
|
return s;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-06 00:44:13 +00:00
|
|
|
ostd::String CsState::run_str(ostd::ConstCharRange code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(code, ret);
|
2016-08-11 16:21:26 +00:00
|
|
|
ostd::String s = ret.get_str();
|
|
|
|
ret.cleanup();
|
|
|
|
return s;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-27 08:16:22 +00:00
|
|
|
ostd::String CsState::run_str(Ident *id, TvalRange args) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(id, args, ret);
|
2016-08-11 16:21:26 +00:00
|
|
|
ostd::String s = ret.get_str();
|
|
|
|
ret.cleanup();
|
|
|
|
return s;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
int CsState::run_int(Bytecode const *code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
2016-08-11 16:21:26 +00:00
|
|
|
run_ret(code, ret);
|
2016-03-04 21:28:41 +00:00
|
|
|
int i = ret.get_int();
|
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2016-08-11 16:21:26 +00:00
|
|
|
int CsState::run_int(ostd::ConstCharRange code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
2016-08-11 16:21:26 +00:00
|
|
|
run_ret(code, ret);
|
2016-03-04 21:28:41 +00:00
|
|
|
int i = ret.get_int();
|
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2015-08-27 08:16:22 +00:00
|
|
|
int CsState::run_int(Ident *id, TvalRange args) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(id, args, ret);
|
|
|
|
int i = ret.get_int();
|
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
float CsState::run_float(Bytecode const *code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
2016-08-11 16:21:26 +00:00
|
|
|
run_ret(code, ret);
|
2016-03-04 21:28:41 +00:00
|
|
|
float f = ret.get_float();
|
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:02:06 +00:00
|
|
|
float CsState::run_float(ostd::ConstCharRange code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(code, ret);
|
|
|
|
float f = ret.get_float();
|
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2015-08-27 08:16:22 +00:00
|
|
|
float CsState::run_float(Ident *id, TvalRange args) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(id, args, ret);
|
|
|
|
float f = ret.get_float();
|
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2016-08-06 18:12:38 +00:00
|
|
|
bool CsState::run_bool(Bytecode const *code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
2016-08-11 16:21:26 +00:00
|
|
|
run_ret(code, ret);
|
2016-08-02 00:21:36 +00:00
|
|
|
bool b = ret.get_bool();
|
2016-03-04 21:28:41 +00:00
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2015-08-06 01:02:06 +00:00
|
|
|
bool CsState::run_bool(ostd::ConstCharRange code) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(code, ret);
|
2016-08-02 00:21:36 +00:00
|
|
|
bool b = ret.get_bool();
|
2016-03-04 21:28:41 +00:00
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2015-08-27 08:16:22 +00:00
|
|
|
bool CsState::run_bool(Ident *id, TvalRange args) {
|
2016-03-04 21:28:41 +00:00
|
|
|
TaggedValue ret;
|
|
|
|
run_ret(id, args, ret);
|
2016-08-02 00:21:36 +00:00
|
|
|
bool b = ret.get_bool();
|
2016-03-04 21:28:41 +00:00
|
|
|
ret.cleanup();
|
2015-08-05 21:58:45 +00:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2015-09-12 15:21:40 +00:00
|
|
|
bool CsState::run_file(ostd::ConstCharRange fname) {
|
2015-08-07 19:32:50 +00:00
|
|
|
ostd::ConstCharRange oldsrcfile = src_file, oldsrcstr = src_str;
|
2015-08-05 21:58:45 +00:00
|
|
|
char *buf = nullptr;
|
|
|
|
ostd::Size len;
|
|
|
|
|
2015-08-06 01:07:16 +00:00
|
|
|
ostd::FileStream f(fname, ostd::StreamMode::read);
|
2015-09-12 15:21:40 +00:00
|
|
|
if (!f.is_open())
|
|
|
|
return false;
|
2015-08-05 21:58:45 +00:00
|
|
|
|
|
|
|
len = f.size();
|
|
|
|
buf = new char[len + 1];
|
|
|
|
if (f.get(buf, len) != len) {
|
|
|
|
delete[] buf;
|
2015-09-12 15:21:40 +00:00
|
|
|
return false;
|
2015-08-05 21:58:45 +00:00
|
|
|
}
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
2015-08-07 19:32:50 +00:00
|
|
|
src_file = fname;
|
|
|
|
src_str = ostd::ConstCharRange(buf, len);
|
|
|
|
run_int(buf);
|
|
|
|
src_file = oldsrcfile;
|
|
|
|
src_str = oldsrcstr;
|
2015-08-05 21:58:45 +00:00
|
|
|
delete[] buf;
|
|
|
|
return true;
|
|
|
|
}
|
2015-08-06 01:07:16 +00:00
|
|
|
|
2016-08-06 15:34:10 +00:00
|
|
|
void cs_init_lib_io(CsState &cs) {
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "exec", "sb", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
auto file = args[0].get_strr();
|
|
|
|
bool ret = cs.run_file(file);
|
|
|
|
if (!ret) {
|
|
|
|
if (args[1].get_int())
|
|
|
|
ostd::err.writefln("could not run file \"%s\"", file);
|
2016-08-11 17:14:18 +00:00
|
|
|
res.set_int(0);
|
2016-08-06 15:34:10 +00:00
|
|
|
} else
|
2016-08-11 17:14:18 +00:00
|
|
|
res.set_int(1);
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "echo", "C", [](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
ostd::writeln(args[0].get_strr());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void cs_set_iter(Ident &id, int i, IdentStack &stack) {
|
|
|
|
if (id.stack == &stack) {
|
|
|
|
if (id.get_valtype() != VAL_INT) {
|
|
|
|
if (id.get_valtype() == VAL_STR) {
|
|
|
|
delete[] id.val.s;
|
|
|
|
id.val.s = nullptr;
|
|
|
|
id.val.len = 0;
|
|
|
|
}
|
|
|
|
id.clean_code();
|
|
|
|
id.valtype = VAL_INT;
|
|
|
|
}
|
|
|
|
id.val.i = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
TaggedValue v;
|
|
|
|
v.set_int(i);
|
|
|
|
id.push_arg(v, stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void cs_do_loop(CsState &cs, Ident &id, int offset, int n,
|
2016-08-06 18:12:38 +00:00
|
|
|
int step, Bytecode *cond, Bytecode *body) {
|
2016-08-06 15:34:10 +00:00
|
|
|
if (n <= 0 || (id.type != ID_ALIAS))
|
|
|
|
return;
|
|
|
|
IdentStack stack;
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
cs_set_iter(id, offset + i * step, stack);
|
|
|
|
if (cond && !cs.run_bool(cond)) break;
|
|
|
|
cs.run_int(body);
|
|
|
|
}
|
|
|
|
id.pop_arg();
|
|
|
|
}
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
static inline void cs_loop_conc(
|
|
|
|
CsState &cs, TaggedValue &res, Ident &id, int offset, int n,
|
|
|
|
int step, Bytecode *body, bool space
|
|
|
|
) {
|
2016-08-06 15:34:10 +00:00
|
|
|
if (n <= 0 || id.type != ID_ALIAS)
|
|
|
|
return;
|
|
|
|
IdentStack stack;
|
|
|
|
ostd::Vector<char> s;
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
cs_set_iter(id, offset + i * step, stack);
|
|
|
|
TaggedValue v;
|
|
|
|
cs.run_ret(body, v);
|
|
|
|
ostd::String vstr = ostd::move(v.get_str());
|
|
|
|
if (space && i) s.push(' ');
|
|
|
|
s.push_n(vstr.data(), vstr.size());
|
|
|
|
v.cleanup();
|
|
|
|
}
|
|
|
|
if (n > 0) id.pop_arg();
|
|
|
|
s.push('\0');
|
|
|
|
ostd::Size len = s.size() - 1;
|
2016-08-11 17:14:18 +00:00
|
|
|
res.set_mstr(ostd::CharRange(s.disown(), len));
|
2016-08-06 15:34:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cs_init_lib_base(CsState &cs) {
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "do", "e", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
cs.run_ret(args[0].get_code(), res);
|
2016-08-06 15:34:10 +00:00
|
|
|
}, ID_DO);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "doargs", "e", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
if (cs.stack != &cs.noalias)
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_do_args(cs, [&]() { cs.run_ret(args[0].get_code(), res); });
|
2016-08-06 15:34:10 +00:00
|
|
|
else
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[0].get_code(), res);
|
2016-08-06 15:34:10 +00:00
|
|
|
}, ID_DOARGS);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "if", "tee", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
cs.run_ret((args[0].get_bool() ? args[1] : args[2]).get_code(), res);
|
2016-08-06 15:34:10 +00:00
|
|
|
}, ID_IF);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "result", "T", [](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
TaggedValue &v = args[0];
|
2016-08-11 17:14:18 +00:00
|
|
|
res = v;
|
2016-08-06 15:34:10 +00:00
|
|
|
v.set_null();
|
|
|
|
}, ID_RESULT);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "!", "t", [](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_int(!args[0].get_bool());
|
2016-08-06 15:34:10 +00:00
|
|
|
}, ID_NOT);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "&&", "E1V", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
if (args.empty())
|
2016-08-11 17:14:18 +00:00
|
|
|
res.set_int(1);
|
2016-08-06 15:34:10 +00:00
|
|
|
else for (ostd::Size i = 0; i < args.size(); ++i) {
|
2016-08-11 17:14:18 +00:00
|
|
|
if (i) res.cleanup();
|
2016-08-06 15:34:10 +00:00
|
|
|
if (args[i].get_type() == VAL_CODE)
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[i].code, res);
|
2016-08-06 15:34:10 +00:00
|
|
|
else
|
2016-08-11 17:14:18 +00:00
|
|
|
res = args[i];
|
|
|
|
if (!res.get_bool()) break;
|
2016-08-06 15:34:10 +00:00
|
|
|
}
|
|
|
|
}, ID_AND);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "||", "E1V", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
if (args.empty())
|
2016-08-11 17:14:18 +00:00
|
|
|
res.set_int(0);
|
2016-08-06 15:34:10 +00:00
|
|
|
else for (ostd::Size i = 0; i < args.size(); ++i) {
|
2016-08-11 17:14:18 +00:00
|
|
|
if (i) res.cleanup();
|
2016-08-06 15:34:10 +00:00
|
|
|
if (args[i].get_type() == VAL_CODE)
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[i].code, res);
|
2016-08-06 15:34:10 +00:00
|
|
|
else
|
2016-08-11 17:14:18 +00:00
|
|
|
res = args[i];
|
|
|
|
if (res.get_bool()) break;
|
2016-08-06 15:34:10 +00:00
|
|
|
}
|
|
|
|
}, ID_OR);
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "?", "tTT", [](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set(args[0].get_bool() ? args[1] : args[2]);
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "cond", "ee2V", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
for (ostd::Size i = 0; i < args.size(); i += 2) {
|
|
|
|
if ((i + 1) < args.size()) {
|
|
|
|
if (cs.run_bool(args[i].code)) {
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[i + 1].code, res);
|
2016-08-06 15:34:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[i].code, res);
|
2016-08-06 15:34:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
#define CS_CMD_CASE(name, fmt, type, acc, compare) \
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, name, fmt "te2V", [&cs](TvalRange args, TaggedValue &res) { \
|
2016-08-06 15:34:10 +00:00
|
|
|
type val = ostd::move(acc); \
|
|
|
|
ostd::Size i; \
|
|
|
|
for (i = 1; (i + 1) < args.size(); i += 2) { \
|
|
|
|
if (compare) { \
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[i + 1].code, res); \
|
2016-08-06 15:34:10 +00:00
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
});
|
|
|
|
|
|
|
|
CS_CMD_CASE("case", "i", int, args[0].get_int(),
|
|
|
|
((args[i].get_type() == VAL_NULL) ||
|
|
|
|
(args[i].get_int() == val)));
|
|
|
|
|
|
|
|
CS_CMD_CASE("casef", "f", float, args[0].get_float(),
|
|
|
|
((args[i].get_type() == VAL_NULL) ||
|
|
|
|
(args[i].get_float() == val)));
|
|
|
|
|
|
|
|
CS_CMD_CASE("cases", "s", ostd::String, args[0].get_str(),
|
|
|
|
((args[i].get_type() == VAL_NULL) ||
|
|
|
|
(args[i].get_str() == val)));
|
|
|
|
|
|
|
|
#undef CS_CMD_CASE
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "pushif", "rTe", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
Ident *id = args[0].get_ident();
|
|
|
|
TaggedValue &v = args[1];
|
2016-08-06 18:12:38 +00:00
|
|
|
Bytecode *code = args[2].get_code();
|
2016-08-06 15:34:10 +00:00
|
|
|
if ((id->type != ID_ALIAS) || (id->index < MaxArguments))
|
|
|
|
return;
|
|
|
|
if (v.get_bool()) {
|
|
|
|
IdentStack stack;
|
|
|
|
id->push_arg(v, stack);
|
|
|
|
v.set_null();
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(code, res);
|
2016-08-06 15:34:10 +00:00
|
|
|
id->pop_arg();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loop", "rie", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), 0, args[1].get_int(), 1, nullptr,
|
|
|
|
args[2].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loop+", "riie", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
|
|
|
nullptr, args[3].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loop*", "riie", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), 0, args[1].get_int(), args[2].get_int(),
|
|
|
|
nullptr, args[3].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loop+*", "riiie", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
|
|
|
|
args[2].get_int(), nullptr, args[4].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopwhile", "riee", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), 0, args[1].get_int(), 1,
|
|
|
|
args[2].get_code(), args[3].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopwhile+", "riiee", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
|
|
|
args[3].get_code(), args[4].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopwhile*", "riiee", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
|
|
|
|
args[3].get_code(), args[4].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopwhile+*", "riiiee", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_do_loop(
|
|
|
|
cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
|
|
|
|
args[2].get_int(), args[4].get_code(), args[5].get_code()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "while", "ee", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 18:12:38 +00:00
|
|
|
Bytecode *cond = args[0].get_code(), *body = args[1].get_code();
|
2016-08-06 15:34:10 +00:00
|
|
|
while (cs.run_bool(cond)) {
|
|
|
|
cs.run_int(body);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcat", "rie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), 0, args[1].get_int(), 1,
|
2016-08-06 15:34:10 +00:00
|
|
|
args[2].get_code(), true
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcat+", "riie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
2016-08-06 15:34:10 +00:00
|
|
|
args[3].get_code(), true
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcat*", "riie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
|
2016-08-06 15:34:10 +00:00
|
|
|
args[3].get_code(), true
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcat+*", "riiie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
|
2016-08-06 15:34:10 +00:00
|
|
|
args[2].get_int(), args[4].get_code(), true
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcatword", "rie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), 0, args[1].get_int(), 1,
|
2016-08-06 15:34:10 +00:00
|
|
|
args[2].get_code(), false
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcatword+", "riie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
2016-08-06 15:34:10 +00:00
|
|
|
args[3].get_code(), false
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcatword*", "riie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
|
2016-08-06 15:34:10 +00:00
|
|
|
args[3].get_code(), false
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "loopconcatword+*", "riiie", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
cs_loop_conc(
|
2016-08-11 17:14:18 +00:00
|
|
|
cs, res, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
|
2016-08-06 15:34:10 +00:00
|
|
|
args[2].get_int(), args[4].get_code(), false
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "nodebug", "e", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
++cs.nodebug;
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[0].get_code(), res);
|
2016-08-06 15:34:10 +00:00
|
|
|
--cs.nodebug;
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "push", "rTe", [&cs](TvalRange args, TaggedValue &res) {
|
2016-08-06 15:34:10 +00:00
|
|
|
Ident *id = args[0].get_ident();
|
|
|
|
if (id->type != ID_ALIAS || id->index < MaxArguments) return;
|
|
|
|
IdentStack stack;
|
|
|
|
TaggedValue &v = args[1];
|
|
|
|
id->push_arg(v, stack);
|
|
|
|
v.set_null();
|
2016-08-11 17:14:18 +00:00
|
|
|
cs.run_ret(args[2].get_code(), res);
|
2016-08-06 15:34:10 +00:00
|
|
|
id->pop_arg();
|
|
|
|
});
|
|
|
|
|
2016-08-10 17:33:43 +00:00
|
|
|
cs_add_command(cs, "local", nullptr, nullptr, ID_LOCAL);
|
2016-08-06 15:34:10 +00:00
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "resetvar", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_int(cs.reset_var(args[0].get_strr()));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "alias", "sT", [&cs](TvalRange args, TaggedValue &) {
|
2016-08-06 15:34:10 +00:00
|
|
|
TaggedValue &v = args[1];
|
|
|
|
cs.set_alias(args[0].get_strr(), v);
|
|
|
|
v.set_null();
|
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "getvarmin", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_int(cs.get_var_min_int(args[0].get_strr()).value_or(0));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "getvarmax", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_int(cs.get_var_max_int(args[0].get_strr()).value_or(0));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "getfvarmin", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_float(cs.get_var_min_float(args[0].get_strr()).value_or(0.0f));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "getfvarmax", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_float(cs.get_var_max_float(args[0].get_strr()).value_or(0.0f));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "identexists", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_int(cs.have_ident(args[0].get_strr()));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
|
|
|
|
2016-08-11 17:14:18 +00:00
|
|
|
cs_add_command(cs, "getalias", "s", [&cs](TvalRange args, TaggedValue &res) {
|
|
|
|
res.set_str(ostd::move(cs.get_alias(args[0].get_strr()).value_or("")));
|
2016-08-06 15:34:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-02 00:21:36 +00:00
|
|
|
void cs_init_lib_math(CsState &cs);
|
|
|
|
void cs_init_lib_string(CsState &cs);
|
|
|
|
void cs_init_lib_list(CsState &cs);
|
2015-08-08 00:40:29 +00:00
|
|
|
|
2016-03-17 21:21:45 +00:00
|
|
|
OSTD_EXPORT void init_libs(CsState &cs, int libs) {
|
2016-02-28 22:21:28 +00:00
|
|
|
if (libs & CS_LIB_IO ) cs_init_lib_io(cs);
|
|
|
|
if (libs & CS_LIB_MATH ) cs_init_lib_math(cs);
|
|
|
|
if (libs & CS_LIB_STRING) cs_init_lib_string(cs);
|
|
|
|
if (libs & CS_LIB_LIST ) cs_init_lib_list(cs);
|
|
|
|
}
|
|
|
|
|
2016-02-07 21:22:39 +00:00
|
|
|
} /* namespace cscript */
|