start splitting into multiple source files
parent
b584c7f857
commit
395615e2cb
20
Makefile
20
Makefile
|
@ -2,15 +2,18 @@ OSTD_PATH = ../octastd
|
|||
|
||||
LIBCS_CXXFLAGS = \
|
||||
-std=c++14 -Wall -Wextra -Wshadow -Wold-style-cast -I. \
|
||||
-fPIC -fvisibility=hidden \
|
||||
-I$(OSTD_PATH)
|
||||
-fvisibility=hidden -I$(OSTD_PATH)
|
||||
|
||||
LIBCS_LDFLAGS = -shared
|
||||
|
||||
LIBCS_OBJ = \
|
||||
cubescript.o
|
||||
cubescript.o \
|
||||
lib_str.o \
|
||||
lib_math.o \
|
||||
lib_list.o \
|
||||
lib_base.o
|
||||
|
||||
LIBCS_LIB = libcubescript.so
|
||||
LIBCS_LIB = libcubescript.a
|
||||
|
||||
.cc.o:
|
||||
$(CXX) $(CXXFLAGS) $(LIBCS_CXXFLAGS) -c -o $@ $<
|
||||
|
@ -20,10 +23,13 @@ all: library
|
|||
library: $(LIBCS_LIB)
|
||||
|
||||
$(LIBCS_LIB): $(LIBCS_OBJ)
|
||||
$(CXX) $(CXXFLAGS) $(LIBCS_CXXFLAGS) \
|
||||
$(LDFLAGS) $(LIBCS_LDFLAGS) -o $@ $(LIBCS_OBJ)
|
||||
ar rcs $(LIBCS_LIB) $(LIBCS_OBJ)
|
||||
|
||||
clean:
|
||||
rm -f $(LIBCS_LIB) $(LIBCS_OBJ)
|
||||
|
||||
cubescript.o: cubescript.hh
|
||||
cubescript.o: cubescript.hh lib_list.hh cs_private.hh
|
||||
lib_str.o: cubescript.hh
|
||||
lib_math.o: cubescript.hh
|
||||
lib_list.o: cubescript.hh lib_list.hh cs_private.hh
|
||||
lib_base.o: cubescript.hh cs_private.hh
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef CS_PRIVATE_HH
|
||||
#define CS_PRIVATE_HH
|
||||
|
||||
#include "cubescript.hh"
|
||||
|
||||
namespace cscript {
|
||||
|
||||
static constexpr int MaxArguments = 25;
|
||||
static constexpr int MaxResults = 7;
|
||||
static constexpr int MaxComargs = 12;
|
||||
|
||||
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_COMD, 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,
|
||||
};
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
} /*namespace cscript */
|
||||
|
||||
#endif
|
1543
cubescript.cc
1543
cubescript.cc
File diff suppressed because it is too large
Load Diff
|
@ -135,6 +135,8 @@ struct OSTD_EXPORT TaggedValue: IdentValue {
|
|||
Ident *get_ident() const;
|
||||
void get_val(TaggedValue &r) const;
|
||||
|
||||
bool get_bool() const;
|
||||
|
||||
void force_null();
|
||||
float force_float();
|
||||
int force_int();
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
#include "cubescript.hh"
|
||||
#include "cs_private.hh"
|
||||
|
||||
namespace cscript {
|
||||
|
||||
static void cs_init_lib_base_var(CsState &cs) {
|
||||
cs.add_command("nodebug", "e", [&cs](TvalRange args) {
|
||||
++cs.nodebug;
|
||||
cs.run_ret(args[0].get_code());
|
||||
--cs.nodebug;
|
||||
});
|
||||
|
||||
cs.add_command("push", "rTe", [&cs](TvalRange args) {
|
||||
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();
|
||||
cs.run_ret(args[2].get_code());
|
||||
id->pop_arg();
|
||||
});
|
||||
|
||||
cs.add_command("local", nullptr, nullptr, ID_LOCAL);
|
||||
|
||||
cs.add_command("resetvar", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_int(cs.reset_var(args[0].get_strr()));
|
||||
});
|
||||
|
||||
cs.add_command("alias", "sT", [&cs](TvalRange args) {
|
||||
TaggedValue &v = args[1];
|
||||
cs.set_alias(args[0].get_strr(), v);
|
||||
v.set_null();
|
||||
});
|
||||
|
||||
cs.add_command("getvarmin", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_int(cs.get_var_min_int(args[0].get_strr()).value_or(0));
|
||||
});
|
||||
cs.add_command("getvarmax", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_int(cs.get_var_max_int(args[0].get_strr()).value_or(0));
|
||||
});
|
||||
cs.add_command("getfvarmin", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_float(cs.get_var_min_float(args[0].get_strr()).value_or(0.0f));
|
||||
});
|
||||
cs.add_command("getfvarmax", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_float(cs.get_var_max_float(args[0].get_strr()).value_or(0.0f));
|
||||
});
|
||||
|
||||
cs.add_command("identexists", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_int(cs.have_ident(args[0].get_strr()));
|
||||
});
|
||||
|
||||
cs.add_command("getalias", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_str(ostd::move(cs.get_alias(args[0].get_strr()).value_or("")));
|
||||
});
|
||||
}
|
||||
|
||||
void cs_init_lib_io(CsState &cs) {
|
||||
cs.add_command("exec", "sb", [&cs](TvalRange args) {
|
||||
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);
|
||||
cs.result->set_int(0);
|
||||
} else
|
||||
cs.result->set_int(1);
|
||||
});
|
||||
|
||||
cs.add_command("echo", "C", [](TvalRange args) {
|
||||
ostd::writeln(args[0].get_strr());
|
||||
});
|
||||
}
|
||||
|
||||
static void cs_init_lib_base_loops(CsState &cs);
|
||||
|
||||
void cs_init_lib_base(CsState &cs) {
|
||||
cs.add_command("do", "e", [&cs](TvalRange args) {
|
||||
cs.run_ret(args[0].get_code());
|
||||
}, ID_DO);
|
||||
|
||||
cs.add_command("doargs", "e", [&cs](TvalRange args) {
|
||||
if (cs.stack != &cs.noalias)
|
||||
cs_do_args(cs, [&]() { cs.run_ret(args[0].get_code()); });
|
||||
else
|
||||
cs.run_ret(args[0].get_code());
|
||||
}, ID_DOARGS);
|
||||
|
||||
cs.add_command("if", "tee", [&cs](TvalRange args) {
|
||||
cs.run_ret((args[0].get_bool() ? args[1] : args[2]).get_code());
|
||||
}, ID_IF);
|
||||
|
||||
cs.add_command("result", "T", [&cs](TvalRange args) {
|
||||
TaggedValue &v = args[0];
|
||||
*cs.result = v;
|
||||
v.set_null();
|
||||
}, ID_RESULT);
|
||||
|
||||
cs.add_command("!", "t", [&cs](TvalRange args) {
|
||||
cs.result->set_int(!args[0].get_bool());
|
||||
}, ID_NOT);
|
||||
|
||||
cs.add_command("&&", "E1V", [&cs](TvalRange args) {
|
||||
if (args.empty())
|
||||
cs.result->set_int(1);
|
||||
else for (ostd::Size i = 0; i < args.size(); ++i) {
|
||||
if (i) cs.result->cleanup();
|
||||
if (args[i].get_type() == VAL_CODE)
|
||||
cs.run_ret(args[i].code);
|
||||
else
|
||||
*cs.result = args[i];
|
||||
if (!cs.result->get_bool()) break;
|
||||
}
|
||||
}, ID_AND);
|
||||
|
||||
cs.add_command("||", "E1V", [&cs](TvalRange args) {
|
||||
if (args.empty())
|
||||
cs.result->set_int(0);
|
||||
else for (ostd::Size i = 0; i < args.size(); ++i) {
|
||||
if (i) cs.result->cleanup();
|
||||
if (args[i].get_type() == VAL_CODE)
|
||||
cs.run_ret(args[i].code);
|
||||
else
|
||||
*cs.result = args[i];
|
||||
if (cs.result->get_bool()) break;
|
||||
}
|
||||
}, ID_OR);
|
||||
|
||||
cs.add_command("?", "tTT", [&cs](TvalRange args) {
|
||||
cs.result->set(args[0].get_bool() ? args[1] : args[2]);
|
||||
});
|
||||
|
||||
cs.add_command("cond", "ee2V", [&cs](TvalRange args) {
|
||||
for (ostd::Size i = 0; i < args.size(); i += 2) {
|
||||
if ((i + 1) < args.size()) {
|
||||
if (cs.run_bool(args[i].code)) {
|
||||
cs.run_ret(args[i + 1].code);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cs.run_ret(args[i].code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
#define CS_CMD_CASE(name, fmt, type, acc, compare) \
|
||||
cs.add_command(name, fmt "te2V", [&cs](TvalRange args) { \
|
||||
type val = ostd::move(acc); \
|
||||
ostd::Size i; \
|
||||
for (i = 1; (i + 1) < args.size(); i += 2) { \
|
||||
if (compare) { \
|
||||
cs.run_ret(args[i + 1].code); \
|
||||
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
|
||||
|
||||
cs.add_command("pushif", "rTe", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
TaggedValue &v = args[1];
|
||||
ostd::Uint32 *code = args[2].get_code();
|
||||
if ((id->type != ID_ALIAS) || (id->index < MaxArguments))
|
||||
return;
|
||||
if (v.get_bool()) {
|
||||
IdentStack stack;
|
||||
id->push_arg(v, stack);
|
||||
v.set_null();
|
||||
cs.run_ret(code);
|
||||
id->pop_arg();
|
||||
}
|
||||
});
|
||||
|
||||
cs_init_lib_base_loops(cs);
|
||||
cs_init_lib_base_var(cs);
|
||||
}
|
||||
|
||||
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,
|
||||
int step, ostd::Uint32 *cond, ostd::Uint32 *body) {
|
||||
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();
|
||||
}
|
||||
|
||||
static inline void cs_loop_conc(CsState &cs, Ident &id, int offset, int n,
|
||||
int step, ostd::Uint32 *body, bool space) {
|
||||
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;
|
||||
cs.result->set_mstr(ostd::CharRange(s.disown(), len));
|
||||
}
|
||||
|
||||
static void cs_init_lib_base_loops(CsState &cs) {
|
||||
cs.add_command("loop", "rie", [&cs](TvalRange args) {
|
||||
cs_do_loop(
|
||||
cs, *args[0].get_ident(), 0, args[1].get_int(), 1, nullptr,
|
||||
args[2].get_code()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loop+", "riie", [&cs](TvalRange args) {
|
||||
cs_do_loop(
|
||||
cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
||||
nullptr, args[3].get_code()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loop*", "riie", [&cs](TvalRange args) {
|
||||
cs_do_loop(
|
||||
cs, *args[0].get_ident(), 0, args[1].get_int(), args[2].get_int(),
|
||||
nullptr, args[3].get_code()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loop+*", "riiie", [&cs](TvalRange args) {
|
||||
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()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopwhile", "riee", [&cs](TvalRange args) {
|
||||
cs_do_loop(
|
||||
cs, *args[0].get_ident(), 0, args[1].get_int(), 1,
|
||||
args[2].get_code(), args[3].get_code()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopwhile+", "riiee", [&cs](TvalRange args) {
|
||||
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()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopwhile*", "riiee", [&cs](TvalRange args) {
|
||||
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()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopwhile+*", "riiiee", [&cs](TvalRange args) {
|
||||
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()
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("while", "ee", [&cs](TvalRange args) {
|
||||
ostd::Uint32 *cond = args[0].get_code(), *body = args[1].get_code();
|
||||
while (cs.run_bool(cond)) {
|
||||
cs.run_int(body);
|
||||
}
|
||||
});
|
||||
|
||||
cs.add_command("loopconcat", "rie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), 0, args[1].get_int(), 1,
|
||||
args[2].get_code(), true
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcat+", "riie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
||||
args[3].get_code(), true
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcat*", "riie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
|
||||
args[3].get_code(), true
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcat+*", "riiie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
|
||||
args[2].get_int(), args[4].get_code(), true
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcatword", "rie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), 0, args[1].get_int(), 1,
|
||||
args[2].get_code(), false
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcatword+", "riie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
|
||||
args[3].get_code(), false
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcatword*", "riie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
|
||||
args[3].get_code(), false
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("loopconcatword+*", "riiie", [&cs](TvalRange args) {
|
||||
cs_loop_conc(
|
||||
cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
|
||||
args[2].get_int(), args[4].get_code(), false
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
} /* namespace cscript */
|
|
@ -0,0 +1,571 @@
|
|||
#include "cubescript.hh"
|
||||
#include "cs_private.hh"
|
||||
#include "lib_list.hh"
|
||||
|
||||
namespace cscript {
|
||||
|
||||
char *cs_dup_ostr(ostd::ConstCharRange s);
|
||||
int cs_parse_int(ostd::ConstCharRange s);
|
||||
float cs_parse_float(ostd::ConstCharRange s);
|
||||
|
||||
struct NullValue: TaggedValue {
|
||||
NullValue() { set_null(); }
|
||||
} const null_value;
|
||||
|
||||
static inline void cs_set_iter(Ident &id, char *val, IdentStack &stack) {
|
||||
if (id.stack == &stack) {
|
||||
if (id.get_valtype() == VAL_STR) {
|
||||
delete[] id.val.s;
|
||||
} else {
|
||||
id.valtype = VAL_STR;
|
||||
}
|
||||
id.clean_code();
|
||||
id.val.s = val;
|
||||
id.val.len = strlen(val);
|
||||
return;
|
||||
}
|
||||
TaggedValue v;
|
||||
v.set_mstr(val);
|
||||
id.push_arg(v, stack);
|
||||
}
|
||||
|
||||
static void cs_loop_list_conc(CsState &cs, Ident *id, ostd::ConstCharRange list,
|
||||
ostd::Uint32 const *body, bool space) {
|
||||
if (id->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack;
|
||||
ostd::Vector<char> r;
|
||||
int n = 0;
|
||||
for (ListParser p(list); p.parse(); ++n) {
|
||||
char *val = p.element().disown();
|
||||
cs_set_iter(*id, val, stack);
|
||||
if (n && space)
|
||||
r.push(' ');
|
||||
TaggedValue v;
|
||||
cs.run_ret(body, v);
|
||||
ostd::String vstr = ostd::move(v.get_str());
|
||||
r.push_n(vstr.data(), vstr.size());
|
||||
v.cleanup();
|
||||
}
|
||||
if (n >= 0)
|
||||
id->pop_arg();
|
||||
r.push('\0');
|
||||
ostd::Size len = r.size();
|
||||
cs.result->set_mstr(ostd::CharRange(r.disown(), len - 1));
|
||||
}
|
||||
|
||||
int cs_list_includes(ostd::ConstCharRange list, ostd::ConstCharRange needle) {
|
||||
int offset = 0;
|
||||
for (ListParser p(list); p.parse();) {
|
||||
if (p.item == needle)
|
||||
return offset;
|
||||
++offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cs_init_lib_list_sort(CsState &cs);
|
||||
|
||||
void cs_init_lib_list(CsState &cs) {
|
||||
cs.add_command("listlen", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_int(int(util::list_length(args[0].get_strr())));
|
||||
});
|
||||
|
||||
cs.add_command("at", "si1V", [&cs](TvalRange args) {
|
||||
if (args.empty())
|
||||
return;
|
||||
ostd::String str = ostd::move(args[0].get_str());
|
||||
ListParser p(str);
|
||||
p.item = str;
|
||||
for (ostd::Size i = 1; i < args.size(); ++i) {
|
||||
p.input = str;
|
||||
int pos = args[i].get_int();
|
||||
for (; pos > 0; --pos)
|
||||
if (!p.parse()) break;
|
||||
if (pos > 0 || !p.parse())
|
||||
p.item = p.quote = ostd::ConstCharRange();
|
||||
}
|
||||
auto elem = p.element();
|
||||
auto er = p.element().iter();
|
||||
elem.disown();
|
||||
cs.result->set_mstr(er);
|
||||
});
|
||||
|
||||
cs.add_command("sublist", "siiN", [&cs](TvalRange args) {
|
||||
int skip = args[1].get_int(),
|
||||
count = args[2].get_int(),
|
||||
numargs = args[2].get_int();
|
||||
|
||||
int offset = ostd::max(skip, 0),
|
||||
len = (numargs >= 3) ? ostd::max(count, 0) : -1;
|
||||
|
||||
ListParser p(args[0].get_strr());
|
||||
for (int i = 0; i < offset; ++i)
|
||||
if (!p.parse()) break;
|
||||
if (len < 0) {
|
||||
if (offset > 0)
|
||||
p.skip();
|
||||
cs.result->set_str(p.input);
|
||||
return;
|
||||
}
|
||||
|
||||
char const *list = p.input.data();
|
||||
p.quote = ostd::ConstCharRange();
|
||||
if (len > 0 && p.parse())
|
||||
while (--len > 0 && p.parse());
|
||||
char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list;
|
||||
cs.result->set_str(ostd::ConstCharRange(list, qend - list));
|
||||
});
|
||||
|
||||
cs.add_command("listfind", "rse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
auto body = args[2].get_code();
|
||||
if (id->type != ID_ALIAS) {
|
||||
cs.result->set_int(-1);
|
||||
return;
|
||||
}
|
||||
IdentStack stack;
|
||||
int n = -1;
|
||||
for (ListParser p(args[1].get_strr()); p.parse();) {
|
||||
++n;
|
||||
cs_set_iter(*id, cs_dup_ostr(p.item), stack);
|
||||
if (cs.run_bool(body)) {
|
||||
cs.result->set_int(n);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
cs.result->set_int(-1);
|
||||
found:
|
||||
if (n >= 0)
|
||||
id->pop_arg();
|
||||
});
|
||||
|
||||
cs.add_command("listassoc", "rse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
auto body = args[2].get_code();
|
||||
if (id->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack;
|
||||
int n = -1;
|
||||
for (ListParser p(args[1].get_strr()); p.parse();) {
|
||||
++n;
|
||||
cs_set_iter(*id, cs_dup_ostr(p.item), stack);
|
||||
if (cs.run_bool(body)) {
|
||||
if (p.parse()) {
|
||||
auto elem = p.element();
|
||||
auto er = elem.iter();
|
||||
elem.disown();
|
||||
cs.result->set_mstr(er);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!p.parse())
|
||||
break;
|
||||
}
|
||||
if (n >= 0)
|
||||
id->pop_arg();
|
||||
});
|
||||
|
||||
#define CS_CMD_LIST_FIND(name, fmt, gmeth, cmp) \
|
||||
cs.add_command(name, "s" fmt "i", [&cs](TvalRange args) { \
|
||||
int n = 0, skip = args[2].get_int(); \
|
||||
auto val = args[1].gmeth(); \
|
||||
for (ListParser p(args[0].get_strr()); p.parse(); ++n) { \
|
||||
if (cmp) { \
|
||||
cs.result->set_int(n); \
|
||||
return; \
|
||||
} \
|
||||
for (int i = 0; i < skip; ++i) { \
|
||||
if (!p.parse()) \
|
||||
goto notfound; \
|
||||
++n; \
|
||||
} \
|
||||
} \
|
||||
notfound: \
|
||||
cs.result->set_int(-1); \
|
||||
});
|
||||
|
||||
CS_CMD_LIST_FIND("listfind=", "i", get_int, cs_parse_int(p.item) == val);
|
||||
CS_CMD_LIST_FIND("listfind=f", "f", get_float, cs_parse_float(p.item) == val);
|
||||
CS_CMD_LIST_FIND("listfind=s", "s", get_strr, p.item == val);
|
||||
|
||||
#undef CS_CMD_LIST_FIND
|
||||
|
||||
#define CS_CMD_LIST_ASSOC(name, fmt, gmeth, cmp) \
|
||||
cs.add_command(name, "s" fmt, [&cs](TvalRange args) { \
|
||||
auto val = args[1].gmeth(); \
|
||||
for (ListParser p(args[0].get_strr()); p.parse();) { \
|
||||
if (cmp) { \
|
||||
if (p.parse()) { \
|
||||
auto elem = p.element(); \
|
||||
auto er = elem.iter(); \
|
||||
elem.disown(); \
|
||||
cs.result->set_mstr(er); \
|
||||
} \
|
||||
return; \
|
||||
} \
|
||||
if (!p.parse()) \
|
||||
break; \
|
||||
} \
|
||||
});
|
||||
|
||||
CS_CMD_LIST_ASSOC("listassoc=", "i", get_int, cs_parse_int(p.item) == val);
|
||||
CS_CMD_LIST_ASSOC("listassoc=f", "f", get_float, cs_parse_float(p.item) == val);
|
||||
CS_CMD_LIST_ASSOC("listassoc=s", "s", get_strr, p.item == val);
|
||||
|
||||
#undef CS_CMD_LIST_ASSOC
|
||||
|
||||
cs.add_command("looplist", "rse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
auto body = args[2].get_code();
|
||||
if (id->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack;
|
||||
int n = 0;
|
||||
for (ListParser p(args[1].get_strr()); p.parse(); ++n) {
|
||||
cs_set_iter(*id, p.element().disown(), stack);
|
||||
cs.run_int(body);
|
||||
}
|
||||
if (n >= 0)
|
||||
id->pop_arg();
|
||||
});
|
||||
|
||||
cs.add_command("looplist2", "rrse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident(), *id2 = args[1].get_ident();
|
||||
auto body = args[3].get_code();
|
||||
if (id->type != ID_ALIAS || id2->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack, stack2;
|
||||
int n = 0;
|
||||
for (ListParser p(args[2].get_strr()); p.parse(); n += 2) {
|
||||
cs_set_iter(*id, p.element().disown(), stack);
|
||||
cs_set_iter(*id2, p.parse() ? p.element().disown()
|
||||
: cs_dup_ostr(""), stack2);
|
||||
cs.run_int(body);
|
||||
}
|
||||
if (n >= 0) {
|
||||
id->pop_arg();
|
||||
id2->pop_arg();
|
||||
}
|
||||
});
|
||||
|
||||
cs.add_command("looplist3", "rrrse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
Ident *id2 = args[1].get_ident();
|
||||
Ident *id3 = args[2].get_ident();
|
||||
auto body = args[4].get_code();
|
||||
if (id->type != ID_ALIAS)
|
||||
return;
|
||||
if (id2->type != ID_ALIAS || id3->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack, stack2, stack3;
|
||||
int n = 0;
|
||||
for (ListParser p(args[3].get_strr()); p.parse(); n += 3) {
|
||||
cs_set_iter(*id, p.element().disown(), stack);
|
||||
cs_set_iter(*id2, p.parse() ? p.element().disown()
|
||||
: cs_dup_ostr(""), stack2);
|
||||
cs_set_iter(*id3, p.parse() ? p.element().disown()
|
||||
: cs_dup_ostr(""), stack3);
|
||||
cs.run_int(body);
|
||||
}
|
||||
if (n >= 0) {
|
||||
id->pop_arg();
|
||||
id2->pop_arg();
|
||||
id3->pop_arg();
|
||||
}
|
||||
});
|
||||
|
||||
cs.add_command("looplistconcat", "rse", [&cs](TvalRange args) {
|
||||
cs_loop_list_conc(
|
||||
cs, args[0].get_ident(), args[1].get_strr(),
|
||||
args[2].get_code(), true
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("looplistconcatword", "rse", [&cs](TvalRange args) {
|
||||
cs_loop_list_conc(
|
||||
cs, args[0].get_ident(), args[1].get_strr(),
|
||||
args[2].get_code(), false
|
||||
);
|
||||
});
|
||||
|
||||
cs.add_command("listfilter", "rse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
auto body = args[2].get_code();
|
||||
if (id->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack;
|
||||
ostd::Vector<char> r;
|
||||
int n = 0;
|
||||
for (ListParser p(args[1].get_strr()); p.parse(); ++n) {
|
||||
char *val = cs_dup_ostr(p.item);
|
||||
cs_set_iter(*id, val, stack);
|
||||
if (cs.run_bool(body)) {
|
||||
if (r.size()) r.push(' ');
|
||||
r.push_n(p.quote.data(), p.quote.size());
|
||||
}
|
||||
}
|
||||
if (n >= 0)
|
||||
id->pop_arg();
|
||||
r.push('\0');
|
||||
ostd::Size len = r.size() - 1;
|
||||
cs.result->set_mstr(ostd::CharRange(r.disown(), len));
|
||||
});
|
||||
|
||||
cs.add_command("listcount", "rse", [&cs](TvalRange args) {
|
||||
Ident *id = args[0].get_ident();
|
||||
auto body = args[2].get_code();
|
||||
if (id->type != ID_ALIAS)
|
||||
return;
|
||||
IdentStack stack;
|
||||
int n = 0, r = 0;
|
||||
for (ListParser p(args[1].get_strr()); p.parse(); ++n) {
|
||||
char *val = cs_dup_ostr(p.item);
|
||||
cs_set_iter(*id, val, stack);
|
||||
if (cs.run_bool(body))
|
||||
r++;
|
||||
}
|
||||
if (n >= 0)
|
||||
id->pop_arg();
|
||||
cs.result->set_int(r);
|
||||
});
|
||||
|
||||
cs.add_command("prettylist", "ss", [&cs](TvalRange args) {
|
||||
ostd::Vector<char> buf;
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
ostd::ConstCharRange conj = args[1].get_strr();
|
||||
ostd::Size len = util::list_length(s);
|
||||
ostd::Size n = 0;
|
||||
for (ListParser p(s); p.parse(); ++n) {
|
||||
if (!p.quote.empty() && (p.quote.front() == '"')) {
|
||||
buf.reserve(buf.size() + p.item.size());
|
||||
auto writer = ostd::CharRange(&buf[buf.size()],
|
||||
buf.capacity() - buf.size());
|
||||
ostd::Size adv = util::unescape_string(writer, p.item);
|
||||
writer.put('\0');
|
||||
buf.advance(adv);
|
||||
} else {
|
||||
buf.push_n(p.item.data(), p.item.size());
|
||||
}
|
||||
if ((n + 1) < len) {
|
||||
if ((len > 2) || conj.empty())
|
||||
buf.push(',');
|
||||
if ((n + 2 == len) && !conj.empty()) {
|
||||
buf.push(' ');
|
||||
buf.push_n(conj.data(), conj.size());
|
||||
}
|
||||
buf.push(' ');
|
||||
}
|
||||
}
|
||||
buf.push('\0');
|
||||
ostd::Size slen = buf.size() - 1;
|
||||
cs.result->set_mstr(ostd::CharRange(buf.disown(), slen));
|
||||
});
|
||||
|
||||
cs.add_command("indexof", "ss", [&cs](TvalRange args) {
|
||||
cs.result->set_int(
|
||||
cs_list_includes(args[0].get_strr(), args[1].get_strr())
|
||||
);
|
||||
});
|
||||
|
||||
#define CS_CMD_LIST_MERGE(name, init, iter, filter, dir) \
|
||||
cs.add_command(name, "ss", [&cs](TvalRange args) { \
|
||||
ostd::ConstCharRange list = args[0].get_strr(); \
|
||||
ostd::ConstCharRange elems = args[1].get_strr(); \
|
||||
ostd::Vector<char> buf; \
|
||||
init; \
|
||||
for (ListParser p(iter); p.parse();) { \
|
||||
if (cs_list_includes(filter, p.item) dir 0) { \
|
||||
if (!buf.empty()) \
|
||||
buf.push(' '); \
|
||||
buf.push_n(p.quote.data(), p.quote.size()); \
|
||||
} \
|
||||
} \
|
||||
buf.push('\0'); \
|
||||
ostd::Size len = buf.size() - 1; \
|
||||
cs.result->set_mstr(ostd::CharRange(buf.disown(), len)); \
|
||||
});
|
||||
|
||||
CS_CMD_LIST_MERGE("listdel", {}, list, elems, <);
|
||||
CS_CMD_LIST_MERGE("listintersect", {}, list, elems, >=);
|
||||
CS_CMD_LIST_MERGE("listunion", buf.push_n(list.data(), list.size()), elems,
|
||||
list, <);
|
||||
|
||||
#undef CS_CMD_LIST_MERGE
|
||||
|
||||
cs.add_command("listsplice", "ssii", [&cs](TvalRange args) {
|
||||
int offset = ostd::max(args[2].get_int(), 0);
|
||||
int len = ostd::max(args[3].get_int(), 0);
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
ostd::ConstCharRange vals = args[1].get_strr();
|
||||
char const *list = s.data();
|
||||
ListParser p(s);
|
||||
for (int i = 0; i < offset; ++i)
|
||||
if (!p.parse())
|
||||
break;
|
||||
char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list;
|
||||
ostd::Vector<char> buf;
|
||||
if (qend > list)
|
||||
buf.push_n(list, qend - list);
|
||||
if (!vals.empty()) {
|
||||
if (!buf.empty())
|
||||
buf.push(' ');
|
||||
buf.push_n(vals.data(), vals.size());
|
||||
}
|
||||
for (int i = 0; i < len; ++i)
|
||||
if (!p.parse())
|
||||
break;
|
||||
p.skip();
|
||||
if (!p.input.empty()) switch (p.input.front()) {
|
||||
case ')':
|
||||
case ']':
|
||||
break;
|
||||
default:
|
||||
if (!buf.empty())
|
||||
buf.push(' ');
|
||||
buf.push_n(p.input.data(), p.input.size());
|
||||
break;
|
||||
}
|
||||
buf.push('\0');
|
||||
ostd::Size slen = buf.size() - 1;
|
||||
cs.result->set_mstr(ostd::CharRange(buf.disown(), slen));
|
||||
});
|
||||
|
||||
cs_init_lib_list_sort(cs);
|
||||
}
|
||||
|
||||
struct ListSortItem {
|
||||
char const *str;
|
||||
ostd::ConstCharRange quote;
|
||||
};
|
||||
|
||||
struct ListSortFun {
|
||||
CsState &cs;
|
||||
Ident *x, *y;
|
||||
ostd::Uint32 *body;
|
||||
|
||||
bool operator()(ListSortItem const &xval, ListSortItem const &yval) {
|
||||
x->clean_code();
|
||||
if (x->get_valtype() != VAL_CSTR) {
|
||||
x->valtype = VAL_CSTR;
|
||||
}
|
||||
x->val.cstr = xval.str;
|
||||
x->val.len = strlen(xval.str);
|
||||
y->clean_code();
|
||||
if (y->get_valtype() != VAL_CSTR) {
|
||||
y->valtype = VAL_CSTR;
|
||||
}
|
||||
y->val.cstr = yval.str;
|
||||
y->val.len = strlen(yval.str);
|
||||
return cs.run_bool(body);
|
||||
}
|
||||
};
|
||||
|
||||
static void cs_list_sort(
|
||||
CsState &cs, ostd::ConstCharRange list, Ident *x, Ident *y,
|
||||
ostd::Uint32 *body, ostd::Uint32 *unique
|
||||
) {
|
||||
if (x == y || x->type != ID_ALIAS || y->type != ID_ALIAS)
|
||||
return;
|
||||
|
||||
ostd::Vector<ListSortItem> items;
|
||||
ostd::Size clen = list.size();
|
||||
ostd::Size total = 0;
|
||||
|
||||
char *cstr = cs_dup_ostr(list);
|
||||
for (ListParser p(list); p.parse();) {
|
||||
cstr[&p.item[p.item.size()] - list.data()] = '\0';
|
||||
ListSortItem item = { &cstr[p.item.data() - list.data()], p.quote };
|
||||
items.push(item);
|
||||
total += item.quote.size();
|
||||
}
|
||||
|
||||
if (items.empty()) {
|
||||
cs.result->set_mstr(cstr);
|
||||
return;
|
||||
}
|
||||
|
||||
IdentStack xstack, ystack;
|
||||
x->push_arg(null_value, xstack);
|
||||
y->push_arg(null_value, ystack);
|
||||
|
||||
ostd::Size totaluniq = total;
|
||||
ostd::Size nuniq = items.size();
|
||||
if (body) {
|
||||
ListSortFun f = { cs, x, y, body };
|
||||
ostd::sort_cmp(items.iter(), f);
|
||||
if ((*unique & CODE_OP_MASK) != CODE_EXIT) {
|
||||
f.body = unique;
|
||||
totaluniq = items[0].quote.size();
|
||||
nuniq = 1;
|
||||
for (ostd::Size i = 1; i < items.size(); i++) {
|
||||
ListSortItem &item = items[i];
|
||||
if (f(items[i - 1], item))
|
||||
item.quote = nullptr;
|
||||
else {
|
||||
totaluniq += item.quote.size();
|
||||
++nuniq;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ListSortFun f = { cs, x, y, unique };
|
||||
totaluniq = items[0].quote.size();
|
||||
nuniq = 1;
|
||||
for (ostd::Size i = 1; i < items.size(); i++) {
|
||||
ListSortItem &item = items[i];
|
||||
for (ostd::Size j = 0; j < i; ++j) {
|
||||
ListSortItem &prev = items[j];
|
||||
if (!prev.quote.empty() && f(item, prev)) {
|
||||
item.quote = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!item.quote.empty()) {
|
||||
totaluniq += item.quote.size();
|
||||
++nuniq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x->pop_arg();
|
||||
y->pop_arg();
|
||||
|
||||
char *sorted = cstr;
|
||||
ostd::Size sortedlen = totaluniq + ostd::max(nuniq - 1, ostd::Size(0));
|
||||
if (clen < sortedlen) {
|
||||
delete[] cstr;
|
||||
sorted = new char[sortedlen + 1];
|
||||
}
|
||||
|
||||
ostd::Size offset = 0;
|
||||
for (ostd::Size i = 0; i < items.size(); ++i) {
|
||||
ListSortItem &item = items[i];
|
||||
if (item.quote.empty())
|
||||
continue;
|
||||
if (i)
|
||||
sorted[offset++] = ' ';
|
||||
memcpy(&sorted[offset], item.quote.data(), item.quote.size());
|
||||
offset += item.quote.size();
|
||||
}
|
||||
sorted[offset] = '\0';
|
||||
|
||||
cs.result->set_mstr(sorted);
|
||||
}
|
||||
|
||||
static void cs_init_lib_list_sort(CsState &cs) {
|
||||
cs.add_command("sortlist", "srree", [&cs](TvalRange args) {
|
||||
cs_list_sort(
|
||||
cs, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(),
|
||||
args[3].get_code(), args[4].get_code()
|
||||
);
|
||||
});
|
||||
cs.add_command("uniquelist", "srre", [&cs](TvalRange args) {
|
||||
cs_list_sort(
|
||||
cs, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(),
|
||||
nullptr, args[3].get_code()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
} /* namespace cscript */
|
|
@ -0,0 +1,129 @@
|
|||
#ifndef LIB_LIST_HH
|
||||
#define LIB_LIST_HH
|
||||
|
||||
#include "cubescript.hh"
|
||||
|
||||
namespace cscript {
|
||||
|
||||
ostd::ConstCharRange cs_parse_str(ostd::ConstCharRange str);
|
||||
char const *parseword(char const *p);
|
||||
|
||||
struct ListParser {
|
||||
ostd::ConstCharRange input;
|
||||
ostd::ConstCharRange quote = ostd::ConstCharRange();
|
||||
ostd::ConstCharRange item = ostd::ConstCharRange();
|
||||
|
||||
ListParser() = delete;
|
||||
ListParser(ostd::ConstCharRange src): input(src) {}
|
||||
|
||||
void skip() {
|
||||
for (;;) {
|
||||
while (!input.empty()) {
|
||||
char c = input.front();
|
||||
if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'))
|
||||
input.pop_front();
|
||||
else
|
||||
break;
|
||||
}
|
||||
if ((input.size() < 2) || (input[0] != '/') || (input[1] != '/'))
|
||||
break;
|
||||
input = ostd::find(input, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
bool parse() {
|
||||
skip();
|
||||
if (input.empty())
|
||||
return false;
|
||||
switch (input.front()) {
|
||||
case '"':
|
||||
quote = input;
|
||||
input.pop_front();
|
||||
item = input;
|
||||
input = cs_parse_str(input);
|
||||
item = ostd::slice_until(item, input);
|
||||
if (!input.empty() && (input.front() == '"'))
|
||||
input.pop_front();
|
||||
quote = ostd::slice_until(quote, input);
|
||||
break;
|
||||
case '(':
|
||||
case '[': {
|
||||
quote = input;
|
||||
input.pop_front();
|
||||
item = input;
|
||||
char btype = quote.front();
|
||||
int brak = 1;
|
||||
for (;;) {
|
||||
input = ostd::find_one_of(input,
|
||||
ostd::ConstCharRange("\"/;()[]"));
|
||||
if (input.empty())
|
||||
return true;
|
||||
char c = input.front();
|
||||
input.pop_front();
|
||||
switch (c) {
|
||||
case '"':
|
||||
input = cs_parse_str(input);
|
||||
if (!input.empty() && (input.front() == '"'))
|
||||
input.pop_front();
|
||||
break;
|
||||
case '/':
|
||||
if (!input.empty() && (input.front() == '/'))
|
||||
input = ostd::find(input, '\n');
|
||||
break;
|
||||
case '(':
|
||||
case '[':
|
||||
brak += (c == btype);
|
||||
break;
|
||||
case ')':
|
||||
if ((btype == '(') && (--brak <= 0))
|
||||
goto endblock;
|
||||
break;
|
||||
case ']':
|
||||
if ((btype == '[') && (--brak <= 0))
|
||||
goto endblock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endblock:
|
||||
item = ostd::slice_until(item, input);
|
||||
item.pop_back();
|
||||
quote = ostd::slice_until(quote, input);
|
||||
break;
|
||||
}
|
||||
case ')':
|
||||
case ']':
|
||||
return false;
|
||||
default: {
|
||||
char const *e = parseword(input.data());
|
||||
item = input;
|
||||
input.pop_front_n(e - input.data());
|
||||
item = ostd::slice_until(item, input);
|
||||
quote = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip();
|
||||
if (!input.empty() && (input.front() == ';'))
|
||||
input.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
ostd::String element() {
|
||||
ostd::String s;
|
||||
s.reserve(item.size());
|
||||
if (!quote.empty() && (quote.front() == '"')) {
|
||||
auto writer = s.iter_cap();
|
||||
util::unescape_string(writer, item);
|
||||
writer.put('\0');
|
||||
} else {
|
||||
memcpy(s.data(), item.data(), item.size());
|
||||
s[item.size()] = '\0';
|
||||
}
|
||||
s.advance(item.size());
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
} /*namespace cscript */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,196 @@
|
|||
#include "cubescript.hh"
|
||||
|
||||
namespace cscript {
|
||||
|
||||
static constexpr float PI = 3.14159265358979f;
|
||||
static constexpr float RAD = PI / 180.0f;
|
||||
|
||||
void cs_init_lib_math(CsState &cs) {
|
||||
cs.add_command("sin", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(sin(args[0].get_float() * RAD));
|
||||
});
|
||||
cs.add_command("cos", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(cos(args[0].get_float() * RAD));
|
||||
});
|
||||
cs.add_command("tan", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(tan(args[0].get_float() * RAD));
|
||||
});
|
||||
|
||||
cs.add_command("asin", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(asin(args[0].get_float()) / RAD);
|
||||
});
|
||||
cs.add_command("acos", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(acos(args[0].get_float()) / RAD);
|
||||
});
|
||||
cs.add_command("atan", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(atan(args[0].get_float()) / RAD);
|
||||
});
|
||||
cs.add_command("atan2", "ff", [&cs](TvalRange args) {
|
||||
cs.result->set_float(atan2(args[0].get_float(), args[1].get_float()) / RAD);
|
||||
});
|
||||
|
||||
cs.add_command("sqrt", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(sqrt(args[0].get_float()));
|
||||
});
|
||||
cs.add_command("loge", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(log(args[0].get_float()));
|
||||
});
|
||||
cs.add_command("log2", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(log(args[0].get_float()) / M_LN2);
|
||||
});
|
||||
cs.add_command("log10", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(log10(args[0].get_float()));
|
||||
});
|
||||
|
||||
cs.add_command("exp", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(exp(args[0].get_float()));
|
||||
});
|
||||
|
||||
#define CS_CMD_MIN_MAX(name, fmt, type, op) \
|
||||
cs.add_command(#name, #fmt "1V", [&cs](TvalRange args) { \
|
||||
type v = !args.empty() ? args[0].fmt : 0; \
|
||||
for (ostd::Size i = 1; i < args.size(); ++i) v = op(v, args[i].fmt); \
|
||||
cs.result->set_##type(v); \
|
||||
})
|
||||
|
||||
CS_CMD_MIN_MAX(min, i, int, ostd::min);
|
||||
CS_CMD_MIN_MAX(max, i, int, ostd::max);
|
||||
CS_CMD_MIN_MAX(minf, f, float, ostd::min);
|
||||
CS_CMD_MIN_MAX(maxf, f, float, ostd::max);
|
||||
|
||||
#undef CS_CMD_MIN_MAX
|
||||
|
||||
cs.add_command("abs", "i", [&cs](TvalRange args) {
|
||||
cs.result->set_int(abs(args[0].get_int()));
|
||||
});
|
||||
cs.add_command("absf", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(fabs(args[0].get_float()));
|
||||
});
|
||||
|
||||
cs.add_command("floor", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(floor(args[0].get_float()));
|
||||
});
|
||||
cs.add_command("ceil", "f", [&cs](TvalRange args) {
|
||||
cs.result->set_float(ceil(args[0].get_float()));
|
||||
});
|
||||
|
||||
cs.add_command("round", "ff", [&cs](TvalRange args) {
|
||||
double step = args[1].get_float();
|
||||
double r = args[0].get_float();
|
||||
if (step > 0) {
|
||||
r += step * ((r < 0) ? -0.5 : 0.5);
|
||||
r -= fmod(r, step);
|
||||
} else {
|
||||
r = (r < 0) ? ceil(r - 0.5) : floor(r + 0.5);
|
||||
}
|
||||
cs.result->set_float(float(r));
|
||||
});
|
||||
|
||||
#define CS_CMD_MATH(name, fmt, type, op, initval, unaryop) \
|
||||
cs.add_command(name, #fmt "1V", [&cs](TvalRange args) { \
|
||||
type val; \
|
||||
if (args.size() >= 2) { \
|
||||
val = args[0].fmt; \
|
||||
type val2 = args[1].fmt; \
|
||||
op; \
|
||||
for (ostd::Size i = 2; i < args.size(); ++i) { \
|
||||
val2 = args[i].fmt; \
|
||||
op; \
|
||||
} \
|
||||
} else { \
|
||||
val = (args.size() > 0) ? args[0].fmt : initval; \
|
||||
unaryop; \
|
||||
} \
|
||||
cs.result->set_##type(val); \
|
||||
});
|
||||
|
||||
#define CS_CMD_MATHIN(name, op, initval, unaryop) \
|
||||
CS_CMD_MATH(#name, i, int, val = val op val2, initval, unaryop)
|
||||
|
||||
#define CS_CMD_MATHI(name, initval, unaryop) \
|
||||
CS_CMD_MATHIN(name, name, initval, unaryop)
|
||||
|
||||
#define CS_CMD_MATHFN(name, op, initval, unaryop) \
|
||||
CS_CMD_MATH(#name "f", f, float, val = val op val2, initval, unaryop)
|
||||
|
||||
#define CS_CMD_MATHF(name, initval, unaryop) \
|
||||
CS_CMD_MATHFN(name, name, initval, unaryop)
|
||||
|
||||
CS_CMD_MATHI(+, 0, {});
|
||||
CS_CMD_MATHI(*, 1, {});
|
||||
CS_CMD_MATHI(-, 0, val = -val);
|
||||
|
||||
CS_CMD_MATHI(^, 0, val = ~val);
|
||||
CS_CMD_MATHIN(~, ^, 0, val = ~val);
|
||||
CS_CMD_MATHI(&, 0, {});
|
||||
CS_CMD_MATHI(|, 0, {});
|
||||
CS_CMD_MATHI(^~, 0, {});
|
||||
CS_CMD_MATHI(&~, 0, {});
|
||||
CS_CMD_MATHI(|~, 0, {});
|
||||
|
||||
CS_CMD_MATH("<<", i, int, {
|
||||
val = (val2 < 32) ? (val << ostd::max(val2, 0)) : 0;
|
||||
}, 0, {});
|
||||
CS_CMD_MATH(">>", i, int, val >>= ostd::clamp(val2, 0, 31), 0, {});
|
||||
|
||||
CS_CMD_MATHF(+, 0, {});
|
||||
CS_CMD_MATHF(*, 1, {});
|
||||
CS_CMD_MATHF(-, 0, val = -val);
|
||||
|
||||
#define CS_CMD_DIV(name, fmt, type, op) \
|
||||
CS_CMD_MATH(#name, fmt, type, { if (val2) op; else val = 0; }, 0, {})
|
||||
|
||||
CS_CMD_DIV(div, i, int, val /= val2);
|
||||
CS_CMD_DIV(mod, i, int, val %= val2);
|
||||
CS_CMD_DIV(divf, f, float, val /= val2);
|
||||
CS_CMD_DIV(modf, f, float, val = fmod(val, val2));
|
||||
|
||||
#undef CS_CMD_DIV
|
||||
|
||||
CS_CMD_MATH("pow", f, float, val = pow(val, val2), 0, {});
|
||||
|
||||
#undef CS_CMD_MATHF
|
||||
#undef CS_CMD_MATHFN
|
||||
#undef CS_CMD_MATHI
|
||||
#undef CS_CMD_MATHIN
|
||||
#undef CS_CMD_MATH
|
||||
|
||||
#define CS_CMD_CMP(name, fmt, type, op) \
|
||||
cs.add_command(name, #fmt "1V", [&cs](TvalRange args) { \
|
||||
bool val; \
|
||||
if (args.size() >= 2) { \
|
||||
val = args[0].fmt op args[1].fmt; \
|
||||
for (ostd::Size i = 2; i < args.size() && val; ++i) \
|
||||
val = args[i-1].fmt op args[i].fmt; \
|
||||
} else \
|
||||
val = ((args.size() > 0) ? args[0].fmt : 0) op 0; \
|
||||
cs.result->set_int(int(val)); \
|
||||
})
|
||||
|
||||
#define CS_CMD_CMPIN(name, op) CS_CMD_CMP(#name, i, int, op)
|
||||
#define CS_CMD_CMPI(name) CS_CMD_CMPIN(name, name)
|
||||
#define CS_CMD_CMPFN(name, op) CS_CMD_CMP(#name "f", f, float, op)
|
||||
#define CS_CMD_CMPF(name) CS_CMD_CMPFN(name, name)
|
||||
|
||||
CS_CMD_CMPIN(=, ==);
|
||||
CS_CMD_CMPI(!=);
|
||||
CS_CMD_CMPI(<);
|
||||
CS_CMD_CMPI(>);
|
||||
CS_CMD_CMPI(<=);
|
||||
CS_CMD_CMPI(>=);
|
||||
|
||||
CS_CMD_CMPFN(=, ==);
|
||||
CS_CMD_CMPF(!=);
|
||||
CS_CMD_CMPF(<);
|
||||
CS_CMD_CMPF(>);
|
||||
CS_CMD_CMPF(<=);
|
||||
CS_CMD_CMPF(>=);
|
||||
|
||||
#undef CS_CMD_CMPF
|
||||
#undef CS_CMD_CMPFN
|
||||
#undef CS_CMD_CMPI
|
||||
#undef CS_CMD_CMPIN
|
||||
#undef CS_CMD_CMP
|
||||
}
|
||||
|
||||
} /* namespace cscript */
|
|
@ -0,0 +1,216 @@
|
|||
#include "cubescript.hh"
|
||||
|
||||
namespace cscript {
|
||||
|
||||
char *conc(TvalRange v, bool space);
|
||||
|
||||
void cs_init_lib_string(CsState &cs) {
|
||||
cs.add_command("strstr", "ss", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange a = args[0].get_strr(), b = args[1].get_strr();
|
||||
ostd::ConstCharRange s = a;
|
||||
for (int i = 0; b.size() <= s.size(); ++i) {
|
||||
if (b == s.slice(0, b.size())) {
|
||||
cs.result->set_int(i);
|
||||
return;
|
||||
}
|
||||
s.pop_front();
|
||||
}
|
||||
cs.result->set_int(-1);
|
||||
});
|
||||
|
||||
cs.add_command("strlen", "s", [&cs](TvalRange args) {
|
||||
cs.result->set_int(int(args[0].get_strr().size()));
|
||||
});
|
||||
|
||||
cs.add_command("strcode", "si", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange str = args[0].get_strr();
|
||||
int i = args[1].get_int();
|
||||
if (i >= int(str.size())) {
|
||||
cs.result->set_int(0);
|
||||
} else {
|
||||
cs.result->set_int(ostd::byte(str[i]));
|
||||
}
|
||||
});
|
||||
|
||||
cs.add_command("codestr", "i", [&cs](TvalRange args) {
|
||||
char *s = new char[2];
|
||||
s[0] = char(args[0].get_int());
|
||||
s[1] = '\0';
|
||||
cs.result->set_mstr(s);
|
||||
});
|
||||
|
||||
cs.add_command("strlower", "s", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
char *buf = new char[s.size() + 1];
|
||||
for (auto i: ostd::range(s.size()))
|
||||
buf[i] = tolower(s[i]);
|
||||
buf[s.size()] = '\0';
|
||||
cs.result->set_mstr(ostd::CharRange(buf, s.size()));
|
||||
});
|
||||
|
||||
cs.add_command("strupper", "s", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
char *buf = new char[s.size() + 1];
|
||||
for (auto i: ostd::range(s.size()))
|
||||
buf[i] = toupper(s[i]);
|
||||
buf[s.size()] = '\0';
|
||||
cs.result->set_mstr(ostd::CharRange(buf, s.size()));
|
||||
});
|
||||
|
||||
cs.add_command("escape", "s", [&cs](TvalRange args) {
|
||||
auto x = ostd::appender<ostd::String>();
|
||||
util::escape_string(x, args[0].get_strr());
|
||||
ostd::Size len = x.size();
|
||||
cs.result->set_mstr(ostd::CharRange(x.get().disown(), len));
|
||||
});
|
||||
|
||||
cs.add_command("unescape", "s", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
char *buf = new char[s.size() + 1];
|
||||
auto writer = ostd::CharRange(buf, s.size() + 1);
|
||||
util::unescape_string(writer, s);
|
||||
writer.put('\0');
|
||||
cs.result->set_mstr(ostd::CharRange(buf, s.size()));
|
||||
});
|
||||
|
||||
cs.add_command("concat", "V", [&cs](TvalRange args) {
|
||||
cs.result->set_mstr(conc(args, true));
|
||||
});
|
||||
|
||||
cs.add_command("concatworld", "V", [&cs](TvalRange args) {
|
||||
cs.result->set_mstr(conc(args, false));
|
||||
});
|
||||
|
||||
cs.add_command("format", "V", [&cs](TvalRange args) {
|
||||
if (args.empty())
|
||||
return;
|
||||
ostd::Vector<char> s;
|
||||
ostd::String fs = ostd::move(args[0].get_str());
|
||||
ostd::ConstCharRange f = fs.iter();
|
||||
while (!f.empty()) {
|
||||
char c = f.front();
|
||||
f.pop_front();
|
||||
if ((c == '%') && !f.empty()) {
|
||||
char ic = f.front();
|
||||
f.pop_front();
|
||||
if (ic >= '1' && ic <= '9') {
|
||||
int i = ic - '0';
|
||||
ostd::String sub = ostd::move((i < int(args.size()))
|
||||
? args[i].get_str() : ostd::String(""));
|
||||
s.push_n(sub.data(), sub.size());
|
||||
} else s.push(ic);
|
||||
} else s.push(c);
|
||||
}
|
||||
s.push('\0');
|
||||
ostd::Size len = s.size() - 1;
|
||||
cs.result->set_mstr(ostd::CharRange(s.disown(), len));
|
||||
});
|
||||
|
||||
cs.add_command("tohex", "ii", [&cs](TvalRange args) {
|
||||
auto r = ostd::appender<ostd::Vector<char>>();
|
||||
ostd::format(r, "0x%.*X", ostd::max(args[1].get_int(), 1), args[0].get_int());
|
||||
r.put('\0');
|
||||
ostd::Size len = r.size() - 1;
|
||||
cs.result->set_mstr(ostd::CharRange(r.get().disown(), len));
|
||||
});
|
||||
|
||||
cs.add_command("substr", "siiN", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
int start = args[1].get_int(), count = args[2].get_int();
|
||||
int numargs = args[3].get_int();
|
||||
int len = int(s.size()), offset = ostd::clamp(start, 0, len);
|
||||
cs.result->set_str(ostd::ConstCharRange(
|
||||
&s[offset],
|
||||
(numargs >= 3) ? ostd::clamp(count, 0, len - offset)
|
||||
: (len - offset)
|
||||
));
|
||||
});
|
||||
|
||||
#define CS_CMD_CMPS(name, op) \
|
||||
cs.add_command(#name, "s1V", [&cs](TvalRange args) { \
|
||||
bool val; \
|
||||
if (args.size() >= 2) { \
|
||||
val = strcmp(args[0].s, args[1].s) op 0; \
|
||||
for (ostd::Size i = 2; i < args.size() && val; ++i) \
|
||||
val = strcmp(args[i-1].s, args[i].s) op 0; \
|
||||
} else \
|
||||
val = (!args.empty() ? args[0].s[0] : 0) op 0; \
|
||||
cs.result->set_int(int(val)); \
|
||||
})
|
||||
|
||||
CS_CMD_CMPS(strcmp, ==);
|
||||
CS_CMD_CMPS(=s, ==);
|
||||
CS_CMD_CMPS(!=s, !=);
|
||||
CS_CMD_CMPS(<s, <);
|
||||
CS_CMD_CMPS(>s, >);
|
||||
CS_CMD_CMPS(<=s, <=);
|
||||
CS_CMD_CMPS(>=s, >=);
|
||||
|
||||
#undef CS_CMD_CMPS
|
||||
|
||||
cs.add_command("strreplace", "ssss", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
ostd::ConstCharRange oldval = args[1].get_strr(),
|
||||
newval = args[2].get_strr(),
|
||||
newval2 = args[3].get_strr();
|
||||
if (newval2.empty()) {
|
||||
newval2 = newval;
|
||||
}
|
||||
ostd::Vector<char> buf;
|
||||
if (!oldval.size()) {
|
||||
cs.result->set_str(s);
|
||||
return;
|
||||
}
|
||||
for (ostd::Size i = 0;; ++i) {
|
||||
ostd::ConstCharRange found;
|
||||
ostd::ConstCharRange trys = s;
|
||||
for (; oldval.size() <= trys.size(); trys.pop_front()) {
|
||||
if (trys.slice(0, oldval.size()) == oldval) {
|
||||
found = trys;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found.empty()) {
|
||||
auto bef = ostd::slice_until(s, found);
|
||||
for (; !bef.empty(); bef.pop_front()) {
|
||||
buf.push(bef.front());
|
||||
}
|
||||
auto use = (i & 1) ? newval2 : newval;
|
||||
for (; !use.empty(); use.pop_front()) {
|
||||
buf.push(use.front());
|
||||
}
|
||||
s = found + oldval.size();
|
||||
} else {
|
||||
for (; !s.empty(); s.pop_front()) {
|
||||
buf.push(s.front());
|
||||
}
|
||||
buf.push('\0');
|
||||
ostd::Size len = buf.size() - 1;
|
||||
cs.result->set_mstr(ostd::CharRange(buf.disown(), len));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cs.add_command("strsplice", "ssii", [&cs](TvalRange args) {
|
||||
ostd::ConstCharRange s = args[0].get_strr();
|
||||
ostd::ConstCharRange vals = args[1].get_strr();
|
||||
int skip = args[2].get_int(),
|
||||
count = args[3].get_int();
|
||||
int slen = int(s.size()),
|
||||
vlen = int(vals.size());
|
||||
int offset = ostd::clamp(skip, 0, slen),
|
||||
len = ostd::clamp(count, 0, slen - offset);
|
||||
char *p = new char[slen - len + vlen + 1];
|
||||
if (offset)
|
||||
memcpy(p, s.data(), offset);
|
||||
if (vlen)
|
||||
memcpy(&p[offset], vals.data(), vlen);
|
||||
if (offset + len < slen)
|
||||
memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len));
|
||||
p[slen - len + vlen] = '\0';
|
||||
cs.result->set_mstr(ostd::CharRange(p, slen - len + vlen));
|
||||
});
|
||||
}
|
||||
|
||||
} /* namespace cscript */
|
Loading…
Reference in New Issue