start splitting into multiple source files

master
Daniel Kolesa 2016-08-02 01:21:36 +01:00
parent b584c7f857
commit 395615e2cb
9 changed files with 1595 additions and 1523 deletions

View File

@ -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

75
cs_private.hh 100644
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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();

366
lib_base.cc 100644
View File

@ -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 */

571
lib_list.cc 100644
View File

@ -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 */

129
lib_list.hh 100644
View File

@ -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

196
lib_math.cc 100644
View File

@ -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 */

216
lib_str.cc 100644
View File

@ -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 */