libcubescript/cubescript.cc

1551 lines
40 KiB
C++
Raw Normal View History

2015-08-08 18:13:19 +02:00
#include "cubescript.hh"
2016-08-12 18:38:43 +02:00
#include "cs_vm.hh"
#include "cs_util.hh"
2015-08-08 18:13:19 +02:00
2015-08-08 17:13:46 +02:00
namespace cscript {
2016-08-14 18:35:38 +02:00
ostd::String intstr(CsInt v) {
2016-07-13 20:24:26 +02:00
char buf[256];
2016-08-14 18:35:38 +02:00
snprintf(buf, sizeof(buf), IntFormat, v);
2016-07-13 20:24:26 +02:00
return buf;
2015-08-11 23:01:56 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::String floatstr(CsFloat v) {
2016-07-13 20:24:26 +02:00
char buf[256];
2016-08-14 18:35:38 +02:00
snprintf(buf, sizeof(buf), v == CsInt(v) ? RoundFloatFormat : FloatFormat, v);
2016-07-13 20:24:26 +02:00
return buf;
2015-08-11 23:01:56 +02:00
}
char *cs_dup_ostr(ostd::ConstCharRange s) {
2015-08-11 23:16:20 +02:00
char *r = new char[s.size() + 1];
memcpy(r, s.data(), s.size());
r[s.size()] = 0;
return r;
}
2016-08-12 18:38:43 +02:00
bool cs_check_num(ostd::ConstCharRange s) {
2016-08-17 18:53:38 +02:00
if (isdigit(s[0])) {
2015-08-07 00:16:02 +02:00
return true;
2016-08-17 18:53:38 +02:00
}
2015-08-07 00:16:02 +02:00
switch (s[0]) {
2016-08-17 18:53:38 +02:00
case '+':
case '-':
return isdigit(s[1]) || ((s[1] == '.') && isdigit(s[2]));
case '.':
return isdigit(s[1]) != 0;
default:
return false;
2015-08-07 00:16:02 +02:00
}
}
Ident::Ident(IdentType tp, ostd::ConstCharRange nm, int fl):
2016-08-18 03:53:51 +02:00
p_name(nm), p_type(int(tp)), p_flags(fl)
{}
2016-08-10 19:33:43 +02:00
Var::Var(IdentType tp, ostd::ConstCharRange name, VarCb f, int fl):
Ident(tp, name, fl), cb_var(ostd::move(f))
{}
2016-08-17 23:29:31 +02:00
2016-08-17 23:04:43 +02:00
Ivar::Ivar(
ostd::ConstCharRange name, CsInt m, CsInt x, CsInt *s, VarCb f, int fl
):
Var(IdentType::ivar, name, ostd::move(f), fl | ((m > x) ? IDF_READONLY : 0)),
2016-08-18 04:14:55 +02:00
p_storage(s), p_minval(m), p_maxval(x), p_overrideval(0)
{}
2015-08-11 23:16:20 +02:00
2016-08-17 23:04:43 +02:00
Fvar::Fvar(
ostd::ConstCharRange name, CsFloat m, CsFloat x, CsFloat *s, VarCb f, int fl
):
Var(IdentType::fvar, name, ostd::move(f), fl | ((m > x) ? IDF_READONLY : 0)),
2016-08-18 04:14:55 +02:00
p_storage(s), p_minval(m), p_maxval(x), p_overrideval(0)
{}
2015-08-11 23:16:20 +02:00
Svar::Svar(ostd::ConstCharRange name, char **s, VarCb f, int fl):
Var(IdentType::svar, name, ostd::move(f), fl),
2016-08-18 04:14:55 +02:00
p_storage(s), p_overrideval(nullptr)
{}
2015-08-11 23:16:20 +02:00
Alias::Alias(ostd::ConstCharRange name, char *a, int fl):
Ident(IdentType::alias, name, fl),
code(nullptr), stack(nullptr)
2016-08-18 00:18:36 +02:00
{
val_v.set_mstr(a);
2015-08-11 23:16:20 +02:00
}
Alias::Alias(ostd::ConstCharRange name, CsInt a, int fl):
Ident(IdentType::alias, name, fl),
code(nullptr), stack(nullptr)
2016-08-18 00:18:36 +02:00
{
val_v.set_int(a);
2015-08-11 23:16:20 +02:00
}
Alias::Alias(ostd::ConstCharRange name, CsFloat a, int fl):
Ident(IdentType::alias, name, fl),
code(nullptr), stack(nullptr)
2016-08-18 00:18:36 +02:00
{
val_v.set_float(a);
2015-08-11 23:16:20 +02:00
}
Alias::Alias(ostd::ConstCharRange name, int fl):
Ident(IdentType::alias, name, fl),
code(nullptr), stack(nullptr)
2016-08-18 00:18:36 +02:00
{
val_v.set_null();
2016-08-17 23:04:43 +02:00
}
Alias::Alias(ostd::ConstCharRange name, TaggedValue const &v, int fl):
Ident(IdentType::alias, name, fl),
code(nullptr), stack(nullptr), val_v(v)
{}
2015-08-11 23:16:20 +02:00
2016-08-17 23:04:43 +02:00
Command::Command(
int tp, ostd::ConstCharRange name, ostd::ConstCharRange args,
2016-08-17 23:04:43 +02:00
ostd::Uint32 amask, int nargs, CmdFunc f
2016-08-17 18:53:38 +02:00
):
2016-08-18 03:53:51 +02:00
Ident(IdentType::command, name, 0),
2016-08-17 18:53:38 +02:00
cargs(!args.empty() ? cs_dup_ostr(args) : nullptr),
2016-08-17 23:04:43 +02:00
argmask(amask), numargs(nargs), cb_cftv(ostd::move(f))
{
2016-08-18 03:53:51 +02:00
p_type = tp;
2016-08-17 23:04:43 +02:00
}
2015-08-11 23:16:20 +02:00
2016-08-18 00:54:57 +02:00
bool Ident::is_alias() const {
return get_type() == IdentType::alias;
}
Alias *Ident::get_alias() {
if (!is_alias()) {
return nullptr;
}
return static_cast<Alias *>(this);
}
Alias const *Ident::get_alias() const {
if (!is_alias()) {
return nullptr;
}
return static_cast<Alias const *>(this);
}
bool Ident::is_command() const {
return get_type() == IdentType::command;
}
2016-08-18 03:53:51 +02:00
bool Ident::is_special() const {
return get_type() == IdentType::special;
}
2016-08-18 00:54:57 +02:00
bool Ident::is_var() const {
IdentType tp = get_type();
return (tp >= IdentType::ivar) && (tp <= IdentType::svar);
}
Var *Ident::get_var() {
if (!is_var()) {
return nullptr;
}
return static_cast<Var *>(this);
}
Var const *Ident::get_var() const {
if (!is_var()) {
return nullptr;
}
return static_cast<Var const *>(this);
}
bool Ident::is_ivar() const {
return get_type() == IdentType::ivar;
}
Ivar *Ident::get_ivar() {
if (!is_ivar()) {
return nullptr;
}
return static_cast<Ivar *>(this);
}
Ivar const *Ident::get_ivar() const {
if (!is_ivar()) {
return nullptr;
}
return static_cast<Ivar const *>(this);
}
bool Ident::is_fvar() const {
return get_type() == IdentType::fvar;
}
Fvar *Ident::get_fvar() {
if (!is_fvar()) {
return nullptr;
}
return static_cast<Fvar *>(this);
}
Fvar const *Ident::get_fvar() const {
if (!is_fvar()) {
return nullptr;
}
return static_cast<Fvar const *>(this);
}
bool Ident::is_svar() const {
return get_type() == IdentType::svar;
}
Svar *Ident::get_svar() {
if (!is_svar()) {
return nullptr;
}
return static_cast<Svar *>(this);
}
Svar const *Ident::get_svar() const {
if (!is_svar()) {
return nullptr;
}
return static_cast<Svar const *>(this);
}
2016-08-18 04:14:55 +02:00
CsInt Ivar::get_val_min() const {
return p_minval;
}
CsInt Ivar::get_val_max() const {
return p_maxval;
}
CsInt Ivar::get_var_value() const {
return *p_storage;
}
CsFloat Fvar::get_val_min() const {
return p_minval;
}
CsFloat Fvar::get_val_max() const {
return p_maxval;
}
CsFloat Fvar::get_var_value() const {
return *p_storage;
}
ostd::ConstCharRange Svar::get_var_value() const {
return *p_storage;
}
void cs_init_lib_base(CsState &cs);
2016-08-11 19:21:18 +02:00
CsState::CsState() {
2016-07-27 19:52:01 +02:00
noalias.id = nullptr;
noalias.next = nullptr;
noalias.usedargs = (1 << MaxArguments) - 1;
noalias.argstack = nullptr;
2016-02-07 00:17:28 +01:00
for (int i = 0; i < MaxArguments; ++i) {
char buf[32];
snprintf(buf, sizeof(buf), "arg%d", i + 1);
2016-07-17 19:50:40 +02:00
new_ident(static_cast<char const *>(buf), IDF_ARG);
}
2015-08-13 20:51:15 +02:00
dummy = new_ident("//dummy");
2016-08-17 23:04:43 +02:00
add_ident(new Ivar("numargs", MaxArguments, 0, &numargs));
add_ident(new Ivar("dbgalias", 0, 1000, &dbgalias));
cs_init_lib_base(*this);
}
CsState::~CsState() {
2016-08-17 22:21:16 +02:00
for (auto &p: idents.iter()) {
Ident *i = p.second;
2016-08-18 03:53:51 +02:00
Alias *a = i->get_alias();
if (a) {
2016-08-18 00:06:39 +02:00
a->force_null();
delete[] reinterpret_cast<ostd::Uint32 *>(a->code);
2016-08-18 03:53:51 +02:00
} else if (i->is_command() || i->is_special()) {
2016-08-17 23:04:43 +02:00
delete[] static_cast<Command *>(i)->cargs;
}
2016-08-17 22:21:16 +02:00
delete i;
}
}
void CsState::clear_override(Ident &id) {
2016-08-18 03:53:51 +02:00
if (!(id.get_flags() & IDF_OVERRIDDEN)) {
2016-08-17 18:53:38 +02:00
return;
}
2016-08-18 03:53:51 +02:00
switch (id.get_type()) {
case IdentType::alias: {
2016-08-17 23:29:31 +02:00
Alias &a = static_cast<Alias &>(id);
a.val_v.cleanup();
2016-08-17 23:29:31 +02:00
a.clean_code();
a.val_v.set_str("");
2016-08-17 18:53:38 +02:00
break;
2016-08-17 23:29:31 +02:00
}
2016-08-18 03:53:51 +02:00
case IdentType::ivar: {
2016-08-17 23:29:31 +02:00
Ivar &iv = static_cast<Ivar &>(id);
2016-08-18 04:14:55 +02:00
*iv.p_storage = iv.p_overrideval;
2016-08-17 23:29:31 +02:00
iv.changed();
2016-08-17 18:53:38 +02:00
break;
2016-08-17 23:29:31 +02:00
}
2016-08-18 03:53:51 +02:00
case IdentType::fvar: {
Fvar &fv = static_cast<Fvar &>(id);
2016-08-18 04:14:55 +02:00
*fv.p_storage = fv.p_overrideval;
fv.changed();
2016-08-17 18:53:38 +02:00
break;
2016-08-17 23:29:31 +02:00
}
2016-08-18 03:53:51 +02:00
case IdentType::svar: {
Svar &sv = static_cast<Svar &>(id);
2016-08-18 04:14:55 +02:00
delete[] *sv.p_storage;
*sv.p_storage = sv.p_overrideval;
sv.changed();
2016-08-17 18:53:38 +02:00
break;
2016-08-17 23:29:31 +02:00
}
2016-08-18 03:53:51 +02:00
default:
break;
}
2016-08-18 03:53:51 +02:00
id.p_flags &= ~IDF_OVERRIDDEN;
}
void CsState::clear_overrides() {
2016-08-17 22:21:16 +02:00
for (auto &p: idents.iter()) {
clear_override(*(p.second));
2016-08-17 18:53:38 +02:00
}
}
2016-08-18 03:53:51 +02:00
Ident *CsState::add_ident(Ident *id) {
if (!id) {
return nullptr;
}
idents[id->get_name()] = id;
id->p_index = identmap.size();
return identmap.push(id);
}
2015-08-07 00:16:02 +02:00
Ident *CsState::new_ident(ostd::ConstCharRange name, int flags) {
Ident *id = get_ident(name);
2015-08-07 00:16:02 +02:00
if (!id) {
2015-08-11 22:41:12 +02:00
if (cs_check_num(name)) {
2016-08-17 18:53:38 +02:00
cs_debug_code(
*this, "number %s is not a valid identifier name", name
);
2015-08-07 00:16:02 +02:00
return dummy;
}
2016-08-17 23:04:43 +02:00
id = add_ident(new Alias(name, flags));
2015-08-07 00:16:02 +02:00
}
return id;
}
Ident *CsState::force_ident(TaggedValue &v) {
2015-08-13 22:48:03 +02:00
switch (v.get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_IDENT:
return v.id;
case VAL_MACRO:
case VAL_CSTR: {
Ident *id = new_ident(v.s);
v.set_ident(id);
return id;
}
case VAL_STR: {
Ident *id = new_ident(v.s);
delete[] v.s;
v.set_ident(id);
return id;
}
2015-08-07 00:16:02 +02:00
}
v.cleanup();
v.set_ident(dummy);
return dummy;
}
bool CsState::reset_var(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id) {
return false;
}
2016-08-18 03:53:51 +02:00
if (id->get_flags() & IDF_READONLY) {
cs_debug_code(*this, "variable %s is read only", id->get_name());
2015-08-07 00:16:02 +02:00
return false;
}
clear_override(*id);
return true;
}
void CsState::touch_var(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (id && id->is_var()) {
2016-08-17 23:29:31 +02:00
static_cast<Var *>(id)->changed();
2015-08-07 00:16:02 +02:00
}
}
2015-08-07 00:38:22 +02:00
void CsState::set_alias(ostd::ConstCharRange name, TaggedValue &v) {
Ident *id = get_ident(name);
2015-08-07 00:38:22 +02:00
if (id) {
2016-08-18 03:53:51 +02:00
switch (id->get_type()) {
case IdentType::alias: {
2016-08-17 23:29:31 +02:00
Alias *a = static_cast<Alias *>(id);
2016-08-18 03:53:51 +02:00
if (a->get_index() < MaxArguments) {
2016-08-17 23:29:31 +02:00
a->set_arg(*this, v);
2016-08-17 18:53:38 +02:00
} else {
2016-08-17 23:29:31 +02:00
a->set_alias(*this, v);
2016-08-17 18:53:38 +02:00
}
return;
2016-08-17 23:29:31 +02:00
}
2016-08-18 03:53:51 +02:00
case IdentType::ivar:
2016-08-17 23:29:31 +02:00
set_var_int_checked(static_cast<Ivar *>(id), v.get_int());
2016-08-17 18:53:38 +02:00
break;
2016-08-18 03:53:51 +02:00
case IdentType::fvar:
2016-08-17 23:29:31 +02:00
set_var_float_checked(static_cast<Fvar *>(id), v.get_float());
2016-08-17 18:53:38 +02:00
break;
2016-08-18 03:53:51 +02:00
case IdentType::svar:
2016-08-17 23:29:31 +02:00
set_var_str_checked(static_cast<Svar *>(id), v.get_str());
2016-08-17 18:53:38 +02:00
break;
default:
cs_debug_code(
2016-08-18 03:53:51 +02:00
*this, "cannot redefine builtin %s with an alias",
id->get_name()
2016-08-17 18:53:38 +02:00
);
break;
2015-08-07 00:38:22 +02:00
}
v.cleanup();
2015-08-11 22:41:12 +02:00
} else if (cs_check_num(name)) {
2015-08-12 19:50:02 +02:00
cs_debug_code(*this, "cannot alias number %s", name);
2015-08-07 00:38:22 +02:00
v.cleanup();
} else {
2016-08-17 23:04:43 +02:00
add_ident(new Alias(name, v, identflags));
2015-08-07 00:38:22 +02:00
}
}
2016-08-17 23:29:31 +02:00
void CsState::print_var_int(Ivar *iv, CsInt i) {
2015-08-07 22:38:57 +02:00
if (i < 0) {
2016-08-18 03:53:51 +02:00
writefln("%s = %d", iv->get_name(), i);
2015-08-07 22:38:57 +02:00
return;
}
2016-08-18 03:53:51 +02:00
if (iv->get_flags() & IDF_HEX) {
2016-08-18 04:14:55 +02:00
if (iv->get_val_max() == 0xFFFFFF) {
2016-08-17 18:53:38 +02:00
writefln(
2016-08-18 03:53:51 +02:00
"%s = 0x%.6X (%d, %d, %d)", iv->get_name(),
2016-08-17 18:53:38 +02:00
i, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF
);
} else {
2016-08-18 03:53:51 +02:00
writefln("%s = 0x%X", iv->get_name(), i);
2016-08-17 18:53:38 +02:00
}
} else {
2016-08-18 03:53:51 +02:00
writefln("%s = %d", iv->get_name(), i);
2015-08-07 22:38:57 +02:00
}
}
2016-08-17 23:29:31 +02:00
void CsState::print_var_float(Fvar *fv, CsFloat f) {
2016-08-18 03:53:51 +02:00
writefln("%s = %s", fv->get_name(), floatstr(f));
2015-08-07 22:38:57 +02:00
}
2016-08-17 23:29:31 +02:00
void CsState::print_var_str(Svar *sv, ostd::ConstCharRange s) {
2016-08-17 18:53:38 +02:00
if (ostd::find(s, '"').empty()) {
2016-08-18 03:53:51 +02:00
writefln("%s = \"%s\"", sv->get_name(), s);
2016-08-17 18:53:38 +02:00
} else {
2016-08-18 03:53:51 +02:00
writefln("%s = [%s]", sv->get_name(), s);
2016-08-17 18:53:38 +02:00
}
2015-08-07 22:38:57 +02:00
}
2016-08-17 23:29:31 +02:00
void CsState::print_var(Var *v) {
switch (v->get_type()) {
case IdentType::ivar: {
Ivar *iv = static_cast<Ivar *>(v);
2016-08-18 04:14:55 +02:00
print_var_int(iv, *iv->p_storage);
2016-08-17 23:29:31 +02:00
break;
}
case IdentType::fvar: {
Fvar *fv = static_cast<Fvar *>(v);
2016-08-18 04:14:55 +02:00
print_var_float(fv, *fv->p_storage);
2016-08-17 18:53:38 +02:00
break;
2016-08-17 23:29:31 +02:00
}
case IdentType::svar: {
Svar *sv = static_cast<Svar *>(v);
2016-08-18 04:14:55 +02:00
print_var_str(sv, *sv->p_storage);
2016-08-17 18:53:38 +02:00
break;
2016-08-17 23:29:31 +02:00
}
default:
2016-08-17 18:53:38 +02:00
break;
2015-08-07 22:38:57 +02:00
}
}
2015-10-14 03:02:38 +02:00
void TaggedValue::cleanup() {
2015-08-13 22:48:03 +02:00
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_STR:
delete[] s;
break;
case VAL_CODE:
ostd::Uint32 *bcode = const_cast<ostd::Uint32 *>(
reinterpret_cast<ostd::Uint32 const *>(code)
);
if (bcode[-1] == CODE_START) {
delete[] bcode;
}
break;
}
}
2015-10-14 03:02:38 +02:00
void TaggedValue::force_null() {
2016-08-17 18:53:38 +02:00
if (get_type() == VAL_NULL) {
return;
}
cleanup();
set_null();
}
2016-08-14 18:35:38 +02:00
CsFloat TaggedValue::force_float() {
CsFloat rf = 0.0f;
2015-08-13 22:48:03 +02:00
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_INT:
rf = i;
break;
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
rf = cs_parse_float(s);
break;
case VAL_FLOAT:
return f;
}
cleanup();
set_float(rf);
return rf;
}
2016-08-14 18:35:38 +02:00
CsInt TaggedValue::force_int() {
CsInt ri = 0;
2015-08-13 22:48:03 +02:00
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_FLOAT:
ri = f;
break;
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
ri = cs_parse_int(s);
break;
case VAL_INT:
return i;
}
cleanup();
set_int(ri);
return ri;
}
2015-10-14 03:02:38 +02:00
ostd::ConstCharRange TaggedValue::force_str() {
2016-07-13 20:24:26 +02:00
ostd::String rs;
2015-08-13 22:48:03 +02:00
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_FLOAT:
rs = ostd::move(floatstr(f));
break;
case VAL_INT:
rs = ostd::move(intstr(i));
break;
case VAL_MACRO:
case VAL_CSTR:
rs = ostd::ConstCharRange(s, len);
break;
case VAL_STR:
return s;
}
cleanup();
2016-07-13 20:24:26 +02:00
set_str(ostd::move(rs));
return s;
}
CsInt TaggedValue::get_int() const {
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_FLOAT:
return CsInt(f);
2016-08-17 18:53:38 +02:00
case VAL_INT:
return i;
2016-08-17 18:53:38 +02:00
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
return cs_parse_int(s);
2015-08-11 22:41:12 +02:00
}
return 0;
}
CsFloat TaggedValue::get_float() const {
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_FLOAT:
return f;
2016-08-17 18:53:38 +02:00
case VAL_INT:
return CsFloat(i);
2016-08-17 18:53:38 +02:00
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
return cs_parse_float(s);
2015-08-11 22:41:12 +02:00
}
return 0.0f;
}
2016-08-06 20:12:38 +02:00
Bytecode *TaggedValue::get_code() const {
2016-08-17 18:53:38 +02:00
if (get_type() != VAL_CODE) {
2016-07-30 22:42:49 +02:00
return nullptr;
2016-08-17 18:53:38 +02:00
}
2016-08-06 20:12:38 +02:00
return const_cast<Bytecode *>(code);
2016-07-30 22:42:49 +02:00
}
2016-08-01 22:35:42 +02:00
Ident *TaggedValue::get_ident() const {
2016-08-17 18:53:38 +02:00
if (get_type() != VAL_IDENT) {
2016-08-01 22:35:42 +02:00
return nullptr;
2016-08-17 18:53:38 +02:00
}
2016-08-01 22:35:42 +02:00
return id;
}
ostd::String TaggedValue::get_str() const {
switch (get_type()) {
2015-08-11 23:01:56 +02:00
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
return ostd::ConstCharRange(s, len);
2015-08-11 23:01:56 +02:00
case VAL_INT:
return intstr(i);
2015-08-11 23:01:56 +02:00
case VAL_FLOAT:
return floatstr(f);
2015-08-11 23:01:56 +02:00
}
2016-07-13 20:24:26 +02:00
return ostd::String("");
2015-08-11 23:01:56 +02:00
}
ostd::ConstCharRange TaggedValue::get_strr() const {
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
return ostd::ConstCharRange(s, len);
2016-08-17 18:53:38 +02:00
default:
break;
}
return ostd::ConstCharRange();
}
void TaggedValue::get_val(TaggedValue &r) const {
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR: {
r.set_str(ostd::ConstCharRange(s, len));
2016-08-17 18:53:38 +02:00
break;
}
case VAL_INT:
r.set_int(i);
2016-08-17 18:53:38 +02:00
break;
case VAL_FLOAT:
r.set_float(f);
2016-08-17 18:53:38 +02:00
break;
default:
r.set_null();
break;
2015-08-11 22:41:12 +02:00
}
}
2016-08-14 17:05:55 +02:00
OSTD_EXPORT bool code_is_empty(Bytecode const *code) {
if (!code) {
return true;
}
2016-08-17 18:53:38 +02:00
return (
*reinterpret_cast<ostd::Uint32 const *>(code) & CODE_OP_MASK
) == CODE_EXIT;
2016-08-14 17:05:55 +02:00
}
bool TaggedValue::code_is_empty() const {
if (get_type() != VAL_CODE) {
return true;
}
return cscript::code_is_empty(code);
}
static inline bool cs_get_bool(ostd::ConstCharRange s) {
if (s.empty()) {
2016-08-14 17:05:55 +02:00
return false;
}
ostd::ConstCharRange end = s;
2016-08-15 19:55:22 +02:00
CsInt ival = cs_parse_int(end, &end);
if (end.empty()) {
return !!ival;
}
end = s;
2016-08-15 19:55:22 +02:00
CsFloat fval = cs_parse_float(end, &end);
if (end.empty()) {
return !!fval;
2016-08-14 17:05:55 +02:00
}
return true;
}
bool TaggedValue::get_bool() const {
switch (get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_FLOAT:
return f != 0;
case VAL_INT:
return i != 0;
case VAL_STR:
case VAL_MACRO:
case VAL_CSTR:
return cs_get_bool(ostd::ConstCharRange(s, len));
default:
return false;
2016-08-14 17:05:55 +02:00
}
}
2016-08-18 00:18:36 +02:00
void Alias::get_cstr(TaggedValue &v) const {
switch (val_v.get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_MACRO:
v.set_macro(val_v.code, val_v.len);
2016-08-17 18:53:38 +02:00
break;
case VAL_STR:
case VAL_CSTR:
v.set_cstr(ostd::ConstCharRange(val_v.s, val_v.len));
2016-08-17 18:53:38 +02:00
break;
case VAL_INT:
v.set_str(ostd::move(intstr(val_v.i)));
2016-08-17 18:53:38 +02:00
break;
case VAL_FLOAT:
v.set_str(ostd::move(floatstr(val_v.f)));
2016-08-17 18:53:38 +02:00
break;
default:
v.set_cstr("");
break;
2015-08-11 22:41:12 +02:00
}
}
2016-08-18 00:18:36 +02:00
void Alias::get_cval(TaggedValue &v) const {
switch (val_v.get_type()) {
2016-08-17 18:53:38 +02:00
case VAL_MACRO:
v.set_macro(val_v.code, val_v.len);
2016-08-17 18:53:38 +02:00
break;
case VAL_STR:
case VAL_CSTR:
v.set_cstr(ostd::ConstCharRange(val_v.s, val_v.len));
2016-08-17 18:53:38 +02:00
break;
case VAL_INT:
v.set_int(val_v.i);
2016-08-17 18:53:38 +02:00
break;
case VAL_FLOAT:
v.set_float(val_v.f);
2016-08-17 18:53:38 +02:00
break;
default:
v.set_null();
break;
2015-08-11 22:41:12 +02:00
}
}
2016-08-18 00:06:39 +02:00
void Alias::clean_code() {
2016-08-06 20:12:38 +02:00
ostd::Uint32 *bcode = reinterpret_cast<ostd::Uint32 *>(code);
if (bcode) {
2016-08-13 01:26:16 +02:00
bcode_decr(bcode);
code = nullptr;
}
}
2016-08-18 00:06:39 +02:00
void Alias::push_arg(TaggedValue const &v, IdentStack &st, bool um) {
st.val_s = val_v;
2015-08-07 00:02:56 +02:00
st.next = stack;
stack = &st;
2015-08-11 22:41:12 +02:00
set_value(v);
2015-08-07 00:02:56 +02:00
clean_code();
2016-08-17 18:53:38 +02:00
if (um) {
2016-08-18 03:53:51 +02:00
p_flags &= ~IDF_UNKNOWN;
2016-08-17 18:53:38 +02:00
}
2015-08-07 00:02:56 +02:00
}
2016-08-18 00:06:39 +02:00
void Alias::pop_arg() {
2016-08-17 18:53:38 +02:00
if (!stack) {
return;
}
2015-08-07 00:02:56 +02:00
IdentStack *st = stack;
val_v.cleanup();
2015-08-11 22:41:12 +02:00
set_value(*stack);
2015-08-07 00:02:56 +02:00
clean_code();
stack = st->next;
}
2016-08-18 00:06:39 +02:00
void Alias::undo_arg(IdentStack &st) {
2015-08-07 00:02:56 +02:00
IdentStack *prev = stack;
st.val_s = val_v;
2015-08-07 00:02:56 +02:00
st.next = prev;
stack = prev->next;
2015-08-11 22:41:12 +02:00
set_value(*prev);
2015-08-07 00:02:56 +02:00
clean_code();
}
2016-08-18 00:06:39 +02:00
void Alias::redo_arg(IdentStack const &st) {
2015-08-07 00:02:56 +02:00
IdentStack *prev = st.next;
prev->val_s = val_v;
2015-08-07 00:02:56 +02:00
stack = prev;
2015-08-11 22:41:12 +02:00
set_value(st);
2015-08-07 00:02:56 +02:00
clean_code();
}
2016-08-18 00:06:39 +02:00
void Alias::set_arg(CsState &cs, TaggedValue &v) {
2016-08-18 03:53:51 +02:00
if (cs.stack->usedargs & (1 << get_index())) {
val_v.cleanup();
2015-08-11 22:41:12 +02:00
set_value(v);
2015-08-07 00:38:22 +02:00
clean_code();
} else {
2016-08-18 03:53:51 +02:00
push_arg(v, cs.stack->argstack[get_index()], false);
cs.stack->usedargs |= 1 << get_index();
2015-08-07 00:38:22 +02:00
}
}
2016-08-18 00:06:39 +02:00
void Alias::set_alias(CsState &cs, TaggedValue &v) {
val_v.cleanup();
2015-08-11 22:41:12 +02:00
set_value(v);
2015-08-07 00:38:22 +02:00
clean_code();
2016-08-18 03:53:51 +02:00
p_flags = (p_flags & cs.identflags) | cs.identflags;
2015-08-07 00:38:22 +02:00
}
2016-08-10 19:33:43 +02:00
IdentType Ident::get_type() const {
2016-08-18 03:53:51 +02:00
if (p_type > ID_ALIAS) {
return IdentType::special;
2016-08-10 19:33:43 +02:00
}
2016-08-18 03:53:51 +02:00
return IdentType(p_type);
}
ostd::ConstCharRange Ident::get_name() const {
return p_name;
}
int Ident::get_flags() const {
return p_flags;
}
int Ident::get_index() const {
return p_index;
2016-08-10 19:33:43 +02:00
}
2015-08-07 03:11:53 +02:00
template<typename SF, typename RF, typename CF>
2016-08-18 03:53:51 +02:00
static inline bool cs_override_var(
CsState &cs, Var *v, int &vflags, SF sf, RF rf, CF cf
) {
if ((cs.identflags & IDF_OVERRIDDEN) || (vflags & IDF_OVERRIDE)) {
if (vflags & IDF_PERSIST) {
2016-08-17 23:29:31 +02:00
cs_debug_code(
2016-08-18 03:53:51 +02:00
cs, "cannot override persistent variable '%s'", v->get_name()
2016-08-17 23:29:31 +02:00
);
2015-08-07 03:11:53 +02:00
return false;
}
2016-08-18 03:53:51 +02:00
if (!(vflags & IDF_OVERRIDDEN)) {
2015-08-07 03:11:53 +02:00
sf();
2016-08-18 03:53:51 +02:00
vflags |= IDF_OVERRIDDEN;
2016-08-17 18:53:38 +02:00
} else {
cf();
}
2015-08-07 03:11:53 +02:00
} else {
2016-08-18 03:53:51 +02:00
if (vflags & IDF_OVERRIDDEN) {
2015-08-07 03:11:53 +02:00
rf();
2016-08-18 03:53:51 +02:00
vflags &= ~IDF_OVERRIDDEN;
2015-08-07 03:11:53 +02:00
}
cf();
}
return true;
}
2016-08-17 18:53:38 +02:00
void CsState::set_var_int(
ostd::ConstCharRange name, CsInt v, bool dofunc, bool doclamp
) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_ivar()) {
2015-08-07 03:11:53 +02:00
return;
2016-08-17 18:53:38 +02:00
}
2016-08-17 23:29:31 +02:00
Ivar *iv = static_cast<Ivar *>(id);
2016-08-17 18:53:38 +02:00
bool success = cs_override_var(
2016-08-18 03:53:51 +02:00
*this, iv, iv->p_flags,
2016-08-18 04:14:55 +02:00
[&iv]() { iv->p_overrideval = *iv->p_storage; },
2016-08-17 18:53:38 +02:00
[]() {}, []() {}
);
if (!success) {
2015-08-07 03:11:53 +02:00
return;
2016-08-17 18:53:38 +02:00
}
if (doclamp) {
2016-08-18 04:14:55 +02:00
*iv->p_storage = ostd::clamp(v, iv->get_val_min(), iv->get_val_max());
2016-08-17 18:53:38 +02:00
} else {
2016-08-18 04:14:55 +02:00
*iv->p_storage = v;
2016-08-17 18:53:38 +02:00
}
if (dofunc) {
2016-08-17 23:29:31 +02:00
iv->changed();
2016-08-17 18:53:38 +02:00
}
2015-08-07 03:11:53 +02:00
}
2016-08-17 18:53:38 +02:00
void CsState::set_var_float(
ostd::ConstCharRange name, CsFloat v, bool dofunc, bool doclamp
) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_fvar()) {
2015-08-07 03:11:53 +02:00
return;
2016-08-17 18:53:38 +02:00
}
2016-08-17 23:29:31 +02:00
Fvar *fv = static_cast<Fvar *>(id);
2016-08-17 18:53:38 +02:00
bool success = cs_override_var(
2016-08-18 03:53:51 +02:00
*this, fv, fv->p_flags,
2016-08-18 04:14:55 +02:00
[&fv]() { fv->p_overrideval = *fv->p_storage; },
2016-08-17 18:53:38 +02:00
[]() {}, []() {}
);
if (!success) {
2015-08-07 03:11:53 +02:00
return;
2016-08-17 18:53:38 +02:00
}
if (doclamp) {
2016-08-18 04:14:55 +02:00
*fv->p_storage = ostd::clamp(v, fv->get_val_min(), fv->get_val_max());
2016-08-17 18:53:38 +02:00
} else {
2016-08-18 04:14:55 +02:00
*fv->p_storage = v;
2016-08-17 18:53:38 +02:00
}
if (dofunc) {
2016-08-17 23:29:31 +02:00
fv->changed();
2016-08-17 18:53:38 +02:00
}
2015-08-07 03:11:53 +02:00
}
2016-08-17 18:53:38 +02:00
void CsState::set_var_str(
ostd::ConstCharRange name, ostd::ConstCharRange v, bool dofunc
) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_svar()) {
2015-08-07 03:11:53 +02:00
return;
2016-08-17 18:53:38 +02:00
}
2016-08-17 23:29:31 +02:00
Svar *sv = static_cast<Svar *>(id);
2016-08-17 18:53:38 +02:00
bool success = cs_override_var(
2016-08-18 03:53:51 +02:00
*this, sv, sv->p_flags,
2016-08-18 04:14:55 +02:00
[&sv]() { sv->p_overrideval = *sv->p_storage; },
[&sv]() { delete[] sv->p_overrideval; },
[&sv]() { delete[] *sv->p_storage; }
2016-08-17 18:53:38 +02:00
);
if (!success) {
2015-08-07 03:11:53 +02:00
return;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
*sv->p_storage = cs_dup_ostr(v);
2016-08-17 18:53:38 +02:00
if (dofunc) {
2016-08-17 23:29:31 +02:00
sv->changed();
2016-08-17 18:53:38 +02:00
}
2015-08-07 03:11:53 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::Maybe<CsInt> CsState::get_var_int(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_ivar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return *static_cast<Ivar *>(id)->p_storage;
2015-08-07 03:11:53 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::Maybe<CsFloat> CsState::get_var_float(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_fvar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return *static_cast<Fvar *>(id)->p_storage;
2015-08-07 03:11:53 +02:00
}
ostd::Maybe<ostd::String> CsState::get_var_str(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_svar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return ostd::String(*static_cast<Svar *>(id)->p_storage);
2015-08-07 03:11:53 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::Maybe<CsInt> CsState::get_var_min_int(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_ivar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return static_cast<Ivar *>(id)->get_val_min();
2015-08-07 03:11:53 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::Maybe<CsInt> CsState::get_var_max_int(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_ivar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return static_cast<Ivar *>(id)->get_val_max();
2015-08-07 03:11:53 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::Maybe<CsFloat> CsState::get_var_min_float(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_fvar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return static_cast<Fvar *>(id)->get_val_min();
2015-08-07 03:11:53 +02:00
}
2016-08-14 18:35:38 +02:00
ostd::Maybe<CsFloat> CsState::get_var_max_float(ostd::ConstCharRange name) {
Ident *id = get_ident(name);
2016-08-17 18:53:38 +02:00
if (!id || id->is_fvar()) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
return static_cast<Fvar *>(id)->get_val_max();
2015-08-07 03:11:53 +02:00
}
2016-07-13 20:24:26 +02:00
ostd::Maybe<ostd::String>
2016-08-17 23:04:43 +02:00
CsState::get_alias_val(ostd::ConstCharRange name) {
2016-08-17 23:29:31 +02:00
Alias *a = get_alias(name);
if (!a) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
2016-08-18 03:53:51 +02:00
if (
(a->get_index() < MaxArguments) &&
!(stack->usedargs & (1 << a->get_index()))
) {
2015-08-07 03:11:53 +02:00
return ostd::nothing;
2016-08-17 18:53:38 +02:00
}
return ostd::move(a->val_v.get_str());
2015-08-07 03:11:53 +02:00
}
2016-08-17 23:29:31 +02:00
CsInt cs_clamp_var(CsState &cs, Ivar *iv, CsInt v) {
2016-08-18 04:14:55 +02:00
if (v < iv->get_val_min()) {
v = iv->get_val_min();
} else if (v > iv->get_val_max()) {
v = iv->get_val_max();
2016-08-17 18:53:38 +02:00
} else {
2015-08-07 03:44:51 +02:00
return v;
2016-08-17 18:53:38 +02:00
}
cs_debug_code(
cs,
2016-08-18 03:53:51 +02:00
(iv->get_flags() & IDF_HEX)
2016-08-17 18:53:38 +02:00
? (
2016-08-18 04:14:55 +02:00
(iv->get_val_min() <= 255)
2016-08-17 18:53:38 +02:00
? "valid range for '%s' is %d..0x%X"
: "valid range for '%s' is 0x%X..0x%X"
)
: "valid range for '%s' is %d..%d",
2016-08-18 04:14:55 +02:00
iv->get_name(), iv->get_val_min(), iv->get_val_max()
2016-08-17 18:53:38 +02:00
);
2015-08-07 03:44:51 +02:00
return v;
}
2016-08-17 23:29:31 +02:00
void CsState::set_var_int_checked(Ivar *iv, CsInt v) {
2016-08-18 03:53:51 +02:00
if (iv->get_flags() & IDF_READONLY) {
cs_debug_code(*this, "variable '%s' is read only", iv->get_name());
2015-08-07 03:44:51 +02:00
return;
}
2016-08-17 18:53:38 +02:00
bool success = cs_override_var(
2016-08-18 03:53:51 +02:00
*this, iv, iv->p_flags,
2016-08-18 04:14:55 +02:00
[&iv]() { iv->p_overrideval = *iv->p_storage; },
2016-08-17 18:53:38 +02:00
[]() {}, []() {}
);
if (!success) {
2015-08-07 03:44:51 +02:00
return;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
if ((v < iv->get_val_min()) || (v > iv->get_val_max())) {
2016-08-17 23:29:31 +02:00
v = cs_clamp_var(*this, iv, v);
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
*iv->p_storage = v;
2016-08-17 23:29:31 +02:00
iv->changed();
}
2016-08-17 23:29:31 +02:00
void CsState::set_var_int_checked(Ivar *iv, TvalRange args) {
2016-08-14 18:35:38 +02:00
CsInt v = args[0].force_int();
2016-08-18 03:53:51 +02:00
if ((iv->get_flags() & IDF_HEX) && (args.size() > 1)) {
2015-08-07 03:44:51 +02:00
v = (v << 16) | (args[1].force_int() << 8);
2016-08-17 18:53:38 +02:00
if (args.size() > 2) {
2015-08-07 03:44:51 +02:00
v |= args[2].force_int();
2016-08-17 18:53:38 +02:00
}
}
2016-08-17 23:29:31 +02:00
set_var_int_checked(iv, v);
}
2016-08-17 23:29:31 +02:00
CsFloat cs_clamp_fvar(CsState &cs, Fvar *fv, CsFloat v) {
2016-08-18 04:14:55 +02:00
if (v < fv->get_val_min()) {
v = fv->get_val_min();
} else if (v > fv->get_val_max()) {
v = fv->get_val_max();
2016-08-17 18:53:38 +02:00
} else {
2015-08-07 03:44:51 +02:00
return v;
2016-08-17 18:53:38 +02:00
}
cs_debug_code(
2016-08-18 04:14:55 +02:00
cs, "valid range for '%s' is %s..%s", floatstr(fv->get_val_min()),
floatstr(fv->get_val_max())
2016-08-17 18:53:38 +02:00
);
2015-08-07 03:44:51 +02:00
return v;
}
2016-08-17 23:29:31 +02:00
void CsState::set_var_float_checked(Fvar *fv, CsFloat v) {
2016-08-18 03:53:51 +02:00
if (fv->get_flags() & IDF_READONLY) {
cs_debug_code(*this, "variable '%s' is read only", fv->get_name());
2015-08-07 03:44:51 +02:00
return;
}
2016-08-17 18:53:38 +02:00
bool success = cs_override_var(
2016-08-18 03:53:51 +02:00
*this, fv, fv->p_flags,
2016-08-18 04:14:55 +02:00
[&fv]() { fv->p_overrideval = *fv->p_storage; },
2016-08-17 18:53:38 +02:00
[]() {}, []() {}
);
if (!success) {
2015-08-07 03:44:51 +02:00
return;
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
if ((v < fv->get_val_min()) || (v > fv->get_val_max())) {
2016-08-17 23:29:31 +02:00
v = cs_clamp_fvar(*this, fv, v);
2016-08-17 18:53:38 +02:00
}
2016-08-18 04:14:55 +02:00
*fv->p_storage = v;
2016-08-17 23:29:31 +02:00
fv->changed();
}
2016-08-17 23:29:31 +02:00
void CsState::set_var_str_checked(Svar *sv, ostd::ConstCharRange v) {
2016-08-18 03:53:51 +02:00
if (sv->get_flags() & IDF_READONLY) {
cs_debug_code(*this, "variable '%s' is read only", sv->get_name());
2015-08-07 03:44:51 +02:00
return;
}
2016-08-17 18:53:38 +02:00
bool success = cs_override_var(
2016-08-18 03:53:51 +02:00
*this, sv, sv->p_flags,
2016-08-18 04:14:55 +02:00
[&sv]() { sv->p_overrideval = *sv->p_storage; },
[&sv]() { delete[] sv->p_overrideval; },
[&sv]() { delete[] *sv->p_storage; }
2016-08-17 18:53:38 +02:00
);
if (!success) {
return;
}
2016-08-18 04:14:55 +02:00
*sv->p_storage = cs_dup_ostr(v);
2016-08-17 23:29:31 +02:00
sv->changed();
}
2016-08-10 19:33:43 +02:00
static bool cs_add_command(
CsState &cs, ostd::ConstCharRange name, ostd::ConstCharRange args,
CmdFunc func, int type = ID_COMMAND
) {
2015-08-08 00:37:54 +02:00
ostd::Uint32 argmask = 0;
int nargs = 0;
2016-08-17 18:53:38 +02:00
for (ostd::ConstCharRange fmt(args); !fmt.empty(); ++fmt) {
switch (*fmt) {
case 'i':
case 'b':
case 'f':
case 'F':
case 't':
case 'T':
case 'E':
case 'N':
case 'D':
if (nargs < MaxArguments) {
nargs++;
}
break;
case 'S':
case 's':
case 'e':
case 'r':
case '$':
if (nargs < MaxArguments) {
argmask |= 1 << nargs;
nargs++;
}
break;
case '1':
case '2':
case '3':
case '4':
if (nargs < MaxArguments) {
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-17 23:04:43 +02:00
cs.add_ident(new Command(type, name, args, argmask, nargs, ostd::move(func)));
return true;
}
2016-08-10 19:33:43 +02:00
bool CsState::add_command(
ostd::ConstCharRange name, ostd::ConstCharRange args, CmdFunc func
) {
return cs_add_command(*this, name, args, ostd::move(func));
}
void cs_init_lib_io(CsState &cs) {
cs_add_command(cs, "exec", "sb", [&cs](TvalRange args, TaggedValue &res) {
auto file = args[0].get_strr();
bool ret = cs.run_file(file);
if (!ret) {
2016-08-17 18:53:38 +02:00
if (args[1].get_int()) {
ostd::err.writefln("could not run file \"%s\"", file);
2016-08-17 18:53:38 +02:00
}
res.set_int(0);
2016-08-17 18:53:38 +02:00
} else {
res.set_int(1);
2016-08-17 18:53:38 +02:00
}
});
cs_add_command(cs, "echo", "C", [](TvalRange args, TaggedValue &) {
ostd::writeln(args[0].get_strr());
});
}
2016-08-18 00:06:39 +02:00
static inline void cs_set_iter(Alias &a, CsInt i, IdentStack &stack) {
if (a.stack == &stack) {
a.val_v.cleanup();
a.val_v.set_int(i);
return;
}
TaggedValue v;
v.set_int(i);
2016-08-18 00:06:39 +02:00
a.push_arg(v, stack);
}
2016-08-17 18:53:38 +02:00
static inline void cs_do_loop(
CsState &cs, Ident &id, CsInt offset, CsInt n, CsInt step,
Bytecode *cond, Bytecode *body
) {
if (n <= 0 || !id.is_alias()) {
return;
2016-08-17 18:53:38 +02:00
}
2016-08-18 00:06:39 +02:00
Alias &a = static_cast<Alias &>(id);
IdentStack stack;
2016-08-14 18:35:38 +02:00
for (CsInt i = 0; i < n; ++i) {
2016-08-18 00:06:39 +02:00
cs_set_iter(a, offset + i * step, stack);
2016-08-17 18:53:38 +02:00
if (cond && !cs.run_bool(cond)) {
break;
}
cs.run_int(body);
}
2016-08-18 00:06:39 +02:00
a.pop_arg();
}
static inline void cs_loop_conc(
2016-08-14 18:35:38 +02:00
CsState &cs, TaggedValue &res, Ident &id, CsInt offset, CsInt n,
CsInt step, Bytecode *body, bool space
) {
2016-08-17 18:53:38 +02:00
if (n <= 0 || !id.is_alias()) {
return;
2016-08-17 18:53:38 +02:00
}
2016-08-18 00:06:39 +02:00
Alias &a = static_cast<Alias &>(id);
IdentStack stack;
ostd::Vector<char> s;
2016-08-14 18:35:38 +02:00
for (CsInt i = 0; i < n; ++i) {
2016-08-18 00:06:39 +02:00
cs_set_iter(a, offset + i * step, stack);
TaggedValue v;
cs.run_ret(body, v);
ostd::String vstr = ostd::move(v.get_str());
2016-08-17 18:53:38 +02:00
if (space && i) {
s.push(' ');
}
s.push_n(vstr.data(), vstr.size());
v.cleanup();
}
2016-08-17 18:53:38 +02:00
if (n > 0) {
2016-08-18 00:06:39 +02:00
a.pop_arg();
2016-08-17 18:53:38 +02:00
}
s.push('\0');
ostd::Size len = s.size() - 1;
res.set_mstr(ostd::CharRange(s.disown(), len));
}
void cs_init_lib_base(CsState &cs) {
cs_add_command(cs, "do", "e", [&cs](TvalRange args, TaggedValue &res) {
cs.run_ret(args[0].get_code(), res);
}, ID_DO);
cs_add_command(cs, "doargs", "e", [&cs](TvalRange args, TaggedValue &res) {
2016-08-17 18:53:38 +02:00
if (cs.stack != &cs.noalias) {
cs_do_args(cs, [&]() { cs.run_ret(args[0].get_code(), res); });
2016-08-17 18:53:38 +02:00
} else {
cs.run_ret(args[0].get_code(), res);
2016-08-17 18:53:38 +02:00
}
}, ID_DOARGS);
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);
}, ID_IF);
cs_add_command(cs, "result", "T", [](TvalRange args, TaggedValue &res) {
TaggedValue &v = args[0];
res = v;
v.set_null();
}, ID_RESULT);
cs_add_command(cs, "!", "t", [](TvalRange args, TaggedValue &res) {
res.set_int(!args[0].get_bool());
}, ID_NOT);
cs_add_command(cs, "&&", "E1V", [&cs](TvalRange args, TaggedValue &res) {
2016-08-17 18:53:38 +02:00
if (args.empty()) {
res.set_int(1);
2016-08-17 18:53:38 +02:00
} else {
for (ostd::Size i = 0; i < args.size(); ++i) {
if (i) {
res.cleanup();
}
if (args[i].get_type() == VAL_CODE) {
cs.run_ret(args[i].code, res);
} else {
res = args[i];
}
if (!res.get_bool()) {
break;
}
}
}
}, ID_AND);
cs_add_command(cs, "||", "E1V", [&cs](TvalRange args, TaggedValue &res) {
2016-08-17 18:53:38 +02:00
if (args.empty()) {
res.set_int(0);
2016-08-17 18:53:38 +02:00
} else {
for (ostd::Size i = 0; i < args.size(); ++i) {
if (i) {
res.cleanup();
}
if (args[i].get_type() == VAL_CODE) {
cs.run_ret(args[i].code, res);
} else {
res = args[i];
}
if (res.get_bool()) {
break;
}
}
}
}, ID_OR);
cs_add_command(cs, "?", "tTT", [](TvalRange args, TaggedValue &res) {
res.set(args[0].get_bool() ? args[1] : args[2]);
});
cs_add_command(cs, "cond", "ee2V", [&cs](TvalRange args, TaggedValue &res) {
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, res);
break;
}
} else {
cs.run_ret(args[i].code, res);
break;
}
}
});
2016-08-17 18:53:38 +02:00
cs_add_command(cs, "case", "ite2V", [&cs](TvalRange args, TaggedValue &res) {
CsInt val = args[0].get_int();
for (ostd::Size i = 1; (i + 1) < args.size(); i += 2) {
if ((args[i].get_type() == VAL_NULL) || (args[i].get_int() == val)) {
cs.run_ret(args[i + 1].code, res);
return;
}
}
});
2016-08-17 18:53:38 +02:00
cs_add_command(cs, "casef", "fte2V", [&cs](TvalRange args, TaggedValue &res) {
CsFloat val = args[0].get_float();
for (ostd::Size i = 1; (i + 1) < args.size(); i += 2) {
if ((args[i].get_type() == VAL_NULL) || (args[i].get_float() == val)) {
cs.run_ret(args[i + 1].code, res);
return;
}
}
});
2016-08-17 18:53:38 +02:00
cs_add_command(cs, "cases", "ste2V", [&cs](TvalRange args, TaggedValue &res) {
ostd::String val = args[0].get_str();
for (ostd::Size i = 1; (i + 1) < args.size(); i += 2) {
if ((args[i].get_type() == VAL_NULL) || (args[i].get_str() == val)) {
cs.run_ret(args[i + 1].code, res);
return;
}
}
});
cs_add_command(cs, "pushif", "rTe", [&cs](TvalRange args, TaggedValue &res) {
Ident *id = args[0].get_ident();
TaggedValue &v = args[1];
2016-08-06 20:12:38 +02:00
Bytecode *code = args[2].get_code();
2016-08-18 03:53:51 +02:00
if (!id->is_alias() || (id->get_index() < MaxArguments)) {
return;
2016-08-17 18:53:38 +02:00
}
2016-08-18 00:06:39 +02:00
Alias *a = static_cast<Alias *>(id);
if (v.get_bool()) {
IdentStack stack;
2016-08-18 00:06:39 +02:00
a->push_arg(v, stack);
v.set_null();
cs.run_ret(code, res);
2016-08-18 00:06:39 +02:00
a->pop_arg();
}
});
cs_add_command(cs, "loop", "rie", [&cs](TvalRange args, TaggedValue &) {
cs_do_loop(
cs, *args[0].get_ident(), 0, args[1].get_int(), 1, nullptr,
args[2].get_code()
);
});
cs_add_command(cs, "loop+", "riie", [&cs](TvalRange args, TaggedValue &) {
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(cs, "loop*", "riie", [&cs](TvalRange args, TaggedValue &) {
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(cs, "loop+*", "riiie", [&cs](TvalRange args, TaggedValue &) {
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(cs, "loopwhile", "riee", [&cs](TvalRange args, TaggedValue &) {
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(cs, "loopwhile+", "riiee", [&cs](TvalRange args, TaggedValue &) {
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(cs, "loopwhile*", "riiee", [&cs](TvalRange args, TaggedValue &) {
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(cs, "loopwhile+*", "riiiee", [&cs](TvalRange args, TaggedValue &) {
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(cs, "while", "ee", [&cs](TvalRange args, TaggedValue &) {
2016-08-06 20:12:38 +02:00
Bytecode *cond = args[0].get_code(), *body = args[1].get_code();
while (cs.run_bool(cond)) {
cs.run_int(body);
}
});
cs_add_command(cs, "loopconcat", "rie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), 0, args[1].get_int(), 1,
args[2].get_code(), true
);
});
cs_add_command(cs, "loopconcat+", "riie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
args[3].get_code(), true
);
});
cs_add_command(cs, "loopconcat*", "riie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
args[3].get_code(), true
);
});
cs_add_command(cs, "loopconcat+*", "riiie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
args[2].get_int(), args[4].get_code(), true
);
});
cs_add_command(cs, "loopconcatword", "rie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), 0, args[1].get_int(), 1,
args[2].get_code(), false
);
});
cs_add_command(cs, "loopconcatword+", "riie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1,
args[3].get_code(), false
);
});
cs_add_command(cs, "loopconcatword*", "riie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(),
args[3].get_code(), false
);
});
cs_add_command(cs, "loopconcatword+*", "riiie", [&cs](TvalRange args, TaggedValue &res) {
cs_loop_conc(
cs, res, *args[0].get_ident(), args[1].get_int(), args[3].get_int(),
args[2].get_int(), args[4].get_code(), false
);
});
cs_add_command(cs, "nodebug", "e", [&cs](TvalRange args, TaggedValue &res) {
++cs.nodebug;
cs.run_ret(args[0].get_code(), res);
--cs.nodebug;
});
cs_add_command(cs, "push", "rTe", [&cs](TvalRange args, TaggedValue &res) {
Ident *id = args[0].get_ident();
2016-08-18 03:53:51 +02:00
if (!id->is_alias() || (id->get_index() < MaxArguments)) {
2016-08-17 18:53:38 +02:00
return;
}
2016-08-18 00:06:39 +02:00
Alias *a = static_cast<Alias *>(id);
IdentStack stack;
TaggedValue &v = args[1];
2016-08-18 00:06:39 +02:00
a->push_arg(v, stack);
v.set_null();
cs.run_ret(args[2].get_code(), res);
2016-08-18 00:06:39 +02:00
a->pop_arg();
});
2016-08-10 19:33:43 +02:00
cs_add_command(cs, "local", nullptr, nullptr, ID_LOCAL);
cs_add_command(cs, "resetvar", "s", [&cs](TvalRange args, TaggedValue &res) {
res.set_int(cs.reset_var(args[0].get_strr()));
});
cs_add_command(cs, "alias", "sT", [&cs](TvalRange args, TaggedValue &) {
TaggedValue &v = args[1];
cs.set_alias(args[0].get_strr(), v);
v.set_null();
});
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));
});
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));
});
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));
});
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));
});
cs_add_command(cs, "identexists", "s", [&cs](TvalRange args, TaggedValue &res) {
res.set_int(cs.have_ident(args[0].get_strr()));
});
cs_add_command(cs, "getalias", "s", [&cs](TvalRange args, TaggedValue &res) {
2016-08-17 23:04:43 +02:00
res.set_str(ostd::move(cs.get_alias_val(args[0].get_strr()).value_or("")));
});
}
void cs_init_lib_math(CsState &cs);
void cs_init_lib_string(CsState &cs);
void cs_init_lib_list(CsState &cs);
2015-08-08 02:40:29 +02:00
2016-03-17 22:21:45 +01:00
OSTD_EXPORT void init_libs(CsState &cs, int libs) {
2016-08-17 18:53:38 +02: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-28 23:21:28 +01:00
}
2016-02-07 22:22:39 +01:00
} /* namespace cscript */