add assert in lib_base, remove formatting public error api
parent
dec7e844e7
commit
54a7b4b7f1
|
@ -10,6 +10,7 @@
|
|||
#ifndef LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH
|
||||
#define LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include <string_view>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
|
@ -108,6 +106,9 @@ struct LIBCUBESCRIPT_EXPORT error {
|
|||
p_stack{std::move(v.p_stack)}
|
||||
{}
|
||||
|
||||
/** @brief Construct an error using a string. */
|
||||
error(state &cs, std::string_view msg);
|
||||
|
||||
/** @brief Get a view of the error message. */
|
||||
std::string_view what() const {
|
||||
return std::string_view{p_errbeg, std::size_t(p_errend - p_errbeg)};
|
||||
|
@ -122,45 +123,10 @@ struct LIBCUBESCRIPT_EXPORT error {
|
|||
stack_state const &stack() const {
|
||||
return p_stack;
|
||||
}
|
||||
|
||||
/** @brief Construct an error using an unformatted string. */
|
||||
error(state &cs, std::string_view msg):
|
||||
p_errbeg{}, p_errend{}, p_stack{cs}
|
||||
{
|
||||
char *sp;
|
||||
char *buf = request_buf(cs, msg.size(), sp);
|
||||
std::memcpy(buf, msg.data(), msg.size());
|
||||
buf[msg.size()] = '\0';
|
||||
p_errbeg = sp;
|
||||
p_errend = buf + msg.size();
|
||||
p_stack = save_stack(cs);
|
||||
}
|
||||
|
||||
/** @brief Construct an error using a `printf`-style format string. */
|
||||
template<typename ...A>
|
||||
error(state &cs, std::string_view msg, A const &...args):
|
||||
p_errbeg{}, p_errend{}, p_stack{cs}
|
||||
{
|
||||
std::size_t sz = msg.size() + 64;
|
||||
char *buf, *sp;
|
||||
for (;;) {
|
||||
buf = request_buf(cs, sz, sp);
|
||||
int written = std::snprintf(buf, sz, msg.data(), args...);
|
||||
if (written <= 0) {
|
||||
throw error{cs, "malformed format string"};
|
||||
} else if (std::size_t(written) <= sz) {
|
||||
break;
|
||||
}
|
||||
sz = std::size_t(written);
|
||||
}
|
||||
p_errbeg = sp;
|
||||
p_errend = buf + sz;
|
||||
p_stack = save_stack(cs);
|
||||
}
|
||||
|
||||
private:
|
||||
stack_state save_stack(state &cs);
|
||||
char *request_buf(state &cs, std::size_t bufs, char *&sp);
|
||||
friend struct error_p;
|
||||
|
||||
error(state &cs, char const *errbeg, char const *errend);
|
||||
|
||||
char const *p_errbeg, *p_errend;
|
||||
stack_state p_stack;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "cs_thread.hh"
|
||||
#include "cs_error.hh"
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
|
@ -44,46 +45,7 @@ LIBCUBESCRIPT_EXPORT bool stack_state::gap() const {
|
|||
return p_gap;
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT char *error::request_buf(
|
||||
state &cs, std::size_t bufs, char *&sp
|
||||
) {
|
||||
auto &ts = state_p{cs}.ts();
|
||||
charbuf &cb = ts.errbuf;
|
||||
cb.clear();
|
||||
std::size_t sz = 0;
|
||||
if (ts.current_line) {
|
||||
/* we can attach line number */
|
||||
sz = ts.source.size() + 32;
|
||||
for (;;) {
|
||||
/* we are using so the buffer tracks the elements and therefore
|
||||
* does not wipe them when we attempt to reserve more capacity
|
||||
*/
|
||||
cb.resize(sz);
|
||||
int nsz;
|
||||
if (!ts.source.empty()) {
|
||||
nsz = std::snprintf(
|
||||
cb.data(), sz, "%.*s:%zu: ",
|
||||
int(ts.source.size()), ts.source.data(),
|
||||
*ts.current_line
|
||||
);
|
||||
} else {
|
||||
nsz = std::snprintf(cb.data(), sz, "%zu: ", *ts.current_line);
|
||||
}
|
||||
if (nsz <= 0) {
|
||||
abort(); /* should be unreachable */
|
||||
} else if (std::size_t(nsz) < sz) {
|
||||
sz = std::size_t(nsz);
|
||||
break;
|
||||
}
|
||||
sz = std::size_t(nsz + 1);
|
||||
}
|
||||
}
|
||||
cb.resize(sz + bufs + 1);
|
||||
sp = cb.data();
|
||||
return &cb[sz];
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT stack_state error::save_stack(state &cs) {
|
||||
static stack_state save_stack(state &cs) {
|
||||
auto &ts = state_p{cs}.ts();
|
||||
builtin_var *dalias = ts.istate->ivar_dbgalias;
|
||||
auto dval = std::clamp(
|
||||
|
@ -124,4 +86,22 @@ LIBCUBESCRIPT_EXPORT stack_state error::save_stack(state &cs) {
|
|||
return stack_state{cs, ret, total > dval};
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT error::error(state &cs, std::string_view msg):
|
||||
p_errbeg{}, p_errend{}, p_stack{cs}
|
||||
{
|
||||
char *sp;
|
||||
char *buf = state_p{cs}.ts().request_errbuf(msg.size(), sp);
|
||||
std::memcpy(buf, msg.data(), msg.size());
|
||||
buf[msg.size()] = '\0';
|
||||
p_errbeg = sp;
|
||||
p_errend = buf + msg.size();
|
||||
p_stack = save_stack(cs);
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT error::error(
|
||||
state &cs, char const *errbeg, char const *errend
|
||||
): p_errbeg{errbeg}, p_errend{errend}, p_stack{cs} {
|
||||
p_stack = save_stack(cs);
|
||||
}
|
||||
|
||||
} /* namespace cubescript */
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "cs_bcode.hh"
|
||||
#include "cs_thread.hh"
|
||||
#include "cs_vm.hh"
|
||||
#include "cs_error.hh"
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
|
@ -229,10 +230,10 @@ LIBCUBESCRIPT_EXPORT void builtin_var::save(state &cs) {
|
|||
auto &ts = state_p{cs}.ts();
|
||||
if ((ts.ident_flags & IDENT_FLAG_OVERRIDDEN) || is_overridable()) {
|
||||
if (p_impl->p_flags & IDENT_FLAG_PERSIST) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "cannot override persistent variable '%s'",
|
||||
name().data()
|
||||
};
|
||||
);
|
||||
}
|
||||
if (!(p_impl->p_flags & IDENT_FLAG_OVERRIDDEN)) {
|
||||
auto *imp = static_cast<var_impl *>(p_impl);
|
||||
|
@ -293,9 +294,9 @@ LIBCUBESCRIPT_EXPORT void builtin_var::set_value(
|
|||
state &cs, any_value val, bool do_write, bool trigger
|
||||
) {
|
||||
if (is_read_only()) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "variable '%s' is read only", name().data()
|
||||
};
|
||||
);
|
||||
}
|
||||
if (!do_write) {
|
||||
return;
|
||||
|
@ -375,7 +376,7 @@ LIBCUBESCRIPT_EXPORT any_value command::call(
|
|||
|
||||
LIBCUBESCRIPT_EXPORT alias_local::alias_local(state &cs, ident &a) {
|
||||
if (a.type() != ident_type::ALIAS) {
|
||||
throw error{cs, "ident '%s' is not an alias", a.name().data()};
|
||||
throw error_p::make(cs, "ident '%s' is not an alias", a.name().data());
|
||||
}
|
||||
auto &ts = state_p{cs}.ts();
|
||||
p_alias = static_cast<alias *>(&a);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <iterator>
|
||||
|
||||
#include "cs_parser.hh"
|
||||
#include "cs_error.hh"
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
|
@ -56,9 +57,9 @@ LIBCUBESCRIPT_EXPORT char const *parse_string(
|
|||
end:
|
||||
nlines = nl;
|
||||
if ((beg == end) || (*beg != '\"')) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "unfinished string '%.*s'", int(beg - orig), orig
|
||||
};
|
||||
);
|
||||
}
|
||||
return ++beg;
|
||||
}
|
||||
|
@ -1116,7 +1117,9 @@ static bool finish_statement(parser_state &ps, bool more, int term) {
|
|||
/* EOS */
|
||||
case '\0':
|
||||
if (ps.current() != term) {
|
||||
throw error{*ps.ts.pstate, "missing \"%c\"", char(term)};
|
||||
throw error_p::make(
|
||||
*ps.ts.pstate, "missing \"%c\"", char(term)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
/* terminating parens/brackets */
|
||||
|
@ -1127,7 +1130,9 @@ static bool finish_statement(parser_state &ps, bool more, int term) {
|
|||
ps.next_char();
|
||||
return false;
|
||||
}
|
||||
throw error{*ps.ts.pstate, "unexpected \"%c\"", ps.current()};
|
||||
throw error_p::make(
|
||||
*ps.ts.pstate, "unexpected \"%c\"", ps.current()
|
||||
);
|
||||
/* potential comment */
|
||||
case '/':
|
||||
ps.next_char();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "cs_strman.hh"
|
||||
#include "cs_vm.hh" // break/continue, call_with_args
|
||||
#include "cs_parser.hh"
|
||||
#include "cs_error.hh"
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
|
@ -58,9 +59,9 @@ ident &internal_state::new_ident(state &cs, std::string_view name, int flags) {
|
|||
ident *id = get_ident(name);
|
||||
if (!id) {
|
||||
if (!is_valid_name(name)) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "'%s' is not a valid identifier name", name.data()
|
||||
};
|
||||
);
|
||||
}
|
||||
auto *inst = create<alias_impl>(
|
||||
cs, string_ref{cs, name}, flags
|
||||
|
@ -392,14 +393,14 @@ static void var_name_check(
|
|||
state &cs, ident *id, std::string_view n
|
||||
) {
|
||||
if (id) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "redefinition of ident '%.*s'", int(n.size()), n.data()
|
||||
};
|
||||
);
|
||||
} else if (!is_valid_name(n)) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "'%.*s' is not a valid variable name",
|
||||
int(n.size()), n.data()
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,13 +473,15 @@ LIBCUBESCRIPT_EXPORT void state::assign_value(
|
|||
id->get().call(span_type<any_value>{&v, 1}, *this);
|
||||
break;
|
||||
default:
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
*this, "cannot redefine builtin %s with an alias",
|
||||
id->get().name().data()
|
||||
};
|
||||
);
|
||||
}
|
||||
} else if (!is_valid_name(name)) {
|
||||
throw error{*this, "cannot alias invalid name '%s'", name.data()};
|
||||
throw error_p::make(
|
||||
*this, "cannot alias invalid name '%s'", name.data()
|
||||
);
|
||||
} else {
|
||||
auto *a = p_tstate->istate->create<alias_impl>(
|
||||
*this, string_ref{*this, name}, std::move(v),
|
||||
|
@ -531,17 +534,21 @@ LIBCUBESCRIPT_EXPORT any_value state::lookup_value(std::string_view name) {
|
|||
return any_value{};
|
||||
}
|
||||
}
|
||||
throw error{*this, "unknown alias lookup: %s", name.data()};
|
||||
throw error_p::make(*this, "unknown alias lookup: %s", name.data());
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT void state::reset_value(std::string_view name) {
|
||||
auto id = get_ident(name);
|
||||
if (!id) {
|
||||
throw error{*this, "variable '%s' does not exist", name.data()};
|
||||
throw error_p::make(
|
||||
*this, "variable '%s' does not exist", name.data()
|
||||
);
|
||||
}
|
||||
if (id->get().type() == ident_type::VAR) {
|
||||
if (static_cast<builtin_var &>(id->get()).is_read_only()) {
|
||||
throw error{*this, "variable '%s' is read only", name.data()};
|
||||
throw error_p::make(
|
||||
*this, "variable '%s' is read only", name.data()
|
||||
);
|
||||
}
|
||||
}
|
||||
clear_override(id->get());
|
||||
|
@ -619,9 +626,9 @@ LIBCUBESCRIPT_EXPORT command &state::new_command(
|
|||
fmt += 2;
|
||||
break;
|
||||
default:
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
*this, "invalid argument type: %c", *fmt
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
auto &is = *p_tstate->istate;
|
||||
|
@ -653,18 +660,18 @@ LIBCUBESCRIPT_EXPORT command &state::new_command(
|
|||
}
|
||||
/* we haven't found one matching the list, so error */
|
||||
is.destroy(cmd);
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
*this, "forbidden builtin command: %.*s",
|
||||
int(name.size()), name.data()
|
||||
};
|
||||
);
|
||||
}
|
||||
valid:
|
||||
if (is.get_ident(name)) {
|
||||
is.destroy(cmd);
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
*this, "redefinition of ident '%.*s'",
|
||||
int(name.size()), name.data()
|
||||
};
|
||||
);
|
||||
}
|
||||
do_add:
|
||||
is.add_ident(cmd, cmd);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "cs_thread.hh"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
thread_state::thread_state(internal_state *cs):
|
||||
|
@ -27,4 +29,41 @@ alias_stack &thread_state::get_astack(alias const *a) {
|
|||
return it.first->second;
|
||||
}
|
||||
|
||||
char *thread_state::request_errbuf(std::size_t bufs, char *&sp) {
|
||||
errbuf.clear();
|
||||
std::size_t sz = 0;
|
||||
if (current_line) {
|
||||
/* we can attach line number */
|
||||
sz = source.size() + 32;
|
||||
for (;;) {
|
||||
/* we are using so the buffer tracks the elements and therefore
|
||||
* does not wipe them when we attempt to reserve more capacity
|
||||
*/
|
||||
errbuf.resize(sz);
|
||||
int nsz;
|
||||
if (!source.empty()) {
|
||||
nsz = std::snprintf(
|
||||
errbuf.data(), sz, "%.*s:%zu: ",
|
||||
int(source.size()), source.data(),
|
||||
*current_line
|
||||
);
|
||||
} else {
|
||||
nsz = std::snprintf(
|
||||
errbuf.data(), sz, "%zu: ", *current_line
|
||||
);
|
||||
}
|
||||
if (nsz <= 0) {
|
||||
abort(); /* should be unreachable */
|
||||
} else if (std::size_t(nsz) < sz) {
|
||||
sz = std::size_t(nsz);
|
||||
break;
|
||||
}
|
||||
sz = std::size_t(nsz + 1);
|
||||
}
|
||||
}
|
||||
errbuf.resize(sz + bufs + 1);
|
||||
sp = errbuf.data();
|
||||
return &errbuf[sz];
|
||||
}
|
||||
|
||||
} /* namespace cubescript */
|
||||
|
|
|
@ -53,6 +53,8 @@ struct thread_state {
|
|||
hook_func const &get_hook() const { return call_hook; }
|
||||
|
||||
alias_stack &get_astack(alias const *a);
|
||||
|
||||
char *request_errbuf(std::size_t bufs, char *&sp);
|
||||
};
|
||||
|
||||
} /* namespace cubescript */
|
||||
|
|
13
src/cs_vm.cc
13
src/cs_vm.cc
|
@ -2,6 +2,7 @@
|
|||
#include "cs_vm.hh"
|
||||
#include "cs_std.hh"
|
||||
#include "cs_parser.hh"
|
||||
#include "cs_error.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
@ -149,9 +150,9 @@ bool exec_alias(
|
|||
return false;
|
||||
}
|
||||
} else if (aast.flags & IDENT_FLAG_UNKNOWN) {
|
||||
throw error {
|
||||
throw error_p::make(
|
||||
*ts.pstate, "unknown command: %s", a->name().data()
|
||||
};
|
||||
);
|
||||
}
|
||||
/* excess arguments get ignored (make error maybe?) */
|
||||
callargs = std::min(callargs, MAX_ARGUMENTS);
|
||||
|
@ -237,9 +238,9 @@ static inline alias *get_lookup_id(
|
|||
} else {
|
||||
ast = &ts.get_astack(static_cast<alias *>(id));
|
||||
if (ast->flags & IDENT_FLAG_UNKNOWN) {
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
*ts.pstate, "unknown alias lookup: %s", id->name().data()
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
return static_cast<alias *>(id);
|
||||
|
@ -799,9 +800,9 @@ noid:
|
|||
result.force_none();
|
||||
force_arg(cs, result, op & BC_INST_RET_MASK);
|
||||
std::string_view ids{idn};
|
||||
throw error{
|
||||
throw error_p::make(
|
||||
cs, "unknown command: %s", ids.data()
|
||||
};
|
||||
);
|
||||
}
|
||||
result.force_none();
|
||||
switch (ident_p{id->get()}.impl().p_type) {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "cs_std.hh"
|
||||
#include "cs_ident.hh"
|
||||
#include "cs_thread.hh"
|
||||
#include "cs_error.hh"
|
||||
|
||||
namespace cubescript {
|
||||
|
||||
|
@ -72,10 +73,10 @@ LIBCUBESCRIPT_EXPORT void std_init_base(state &gcs) {
|
|||
auto &cret = args[1].get_ident(cs);
|
||||
auto &css = args[2].get_ident(cs);
|
||||
if (cret.type() != ident_type::ALIAS) {
|
||||
throw error{cs, "'%s' is not an alias", cret.name().data()};
|
||||
throw error_p::make(cs, "'%s' is not an alias", cret.name().data());
|
||||
}
|
||||
if (css.type() != ident_type::ALIAS) {
|
||||
throw error{cs, "'%s' is not an alias", css.name().data()};
|
||||
throw error_p::make(cs, "'%s' is not an alias", css.name().data());
|
||||
}
|
||||
any_value result{}, tback{};
|
||||
bool rc = true;
|
||||
|
@ -98,6 +99,25 @@ LIBCUBESCRIPT_EXPORT void std_init_base(state &gcs) {
|
|||
ts.get_astack(ssa).set_alias(ssa, ts, tback);
|
||||
});
|
||||
|
||||
new_cmd_quiet(gcs, "assert", "ss#", [](auto &s, auto args, auto &ret) {
|
||||
auto val = args[0];
|
||||
val.force_code(s);
|
||||
if (!val.get_code().call(s).get_bool()) {
|
||||
if (args[2].get_integer() > 1) {
|
||||
throw error_p::make(
|
||||
s, "assertion failed: [%s] (%s)",
|
||||
args[0].get_string(s).data(), args[1].get_string(s).data()
|
||||
);
|
||||
} else {
|
||||
throw error_p::make(
|
||||
s, "assertion failed: [%s]",
|
||||
args[0].get_string(s).data()
|
||||
);
|
||||
}
|
||||
}
|
||||
ret = std::move(args[0]);
|
||||
});
|
||||
|
||||
new_cmd_quiet(gcs, "?", "aaa", [](auto &, auto args, auto &res) {
|
||||
if (args[0].get_bool()) {
|
||||
res = std::move(args[1]);
|
||||
|
@ -343,7 +363,7 @@ end:
|
|||
new_cmd_quiet(gcs, "getalias", "s", [](auto &cs, auto args, auto &res) {
|
||||
auto &id = cs.new_ident(args[0].get_string(cs));
|
||||
if (id.type() != ident_type::ALIAS) {
|
||||
throw error{cs, "'%s' is not an alias", id.name().data()};
|
||||
throw error_p::make(cs, "'%s' is not an alias", id.name().data());
|
||||
}
|
||||
if (ident_p{id}.impl().p_flags & IDENT_FLAG_UNKNOWN) {
|
||||
return;
|
||||
|
|
|
@ -58,26 +58,6 @@ int main(int argc, char **argv) {
|
|||
throw skip_test{};
|
||||
});
|
||||
|
||||
/* takes a string so we can print it */
|
||||
gcs.new_command("assert", "ss#", [](auto &s, auto args, auto &ret) {
|
||||
auto val = args[0];
|
||||
val.force_code(s);
|
||||
if (!val.get_code().call(s).get_bool()) {
|
||||
if (args[2].get_integer() > 1) {
|
||||
throw cs::error{
|
||||
s, "assertion failed: [%s] (%s)",
|
||||
args[0].get_string(s).data(), args[1].get_string(s).data()
|
||||
};
|
||||
} else {
|
||||
throw cs::error{
|
||||
s, "assertion failed: [%s]",
|
||||
args[0].get_string(s).data()
|
||||
};
|
||||
}
|
||||
}
|
||||
ret = std::move(args[0]);
|
||||
});
|
||||
|
||||
try {
|
||||
do_exec_file(gcs, argv[1]);
|
||||
} catch (skip_test) {
|
||||
|
|
|
@ -360,9 +360,11 @@ int main(int argc, char **argv) {
|
|||
cs::any_value val{};
|
||||
bool ret = do_exec_file(css, file, val);
|
||||
if (!ret) {
|
||||
throw cs::error(
|
||||
css, "could not execute file \"%s\"", file.data()
|
||||
char buf[4096];
|
||||
std::snprintf(
|
||||
buf, sizeof(buf), "could not execute file \"%s\"", file.data()
|
||||
);
|
||||
throw cs::error(css, buf);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue