add assert in lib_base, remove formatting public error api

master
Daniel Kolesa 2021-05-08 17:20:56 +02:00
parent dec7e844e7
commit 54a7b4b7f1
12 changed files with 142 additions and 138 deletions

View File

@ -10,6 +10,7 @@
#ifndef LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH #ifndef LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH
#define LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH #define LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH
#include <cstring>
#include <cstddef> #include <cstddef>
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>

View File

@ -13,8 +13,6 @@
#include <string_view> #include <string_view>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include <cstdlib>
#include <cstring>
namespace cubescript { namespace cubescript {
@ -108,6 +106,9 @@ struct LIBCUBESCRIPT_EXPORT error {
p_stack{std::move(v.p_stack)} 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. */ /** @brief Get a view of the error message. */
std::string_view what() const { std::string_view what() const {
return std::string_view{p_errbeg, std::size_t(p_errend - p_errbeg)}; 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 { stack_state const &stack() const {
return p_stack; 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: private:
stack_state save_stack(state &cs); friend struct error_p;
char *request_buf(state &cs, std::size_t bufs, char *&sp);
error(state &cs, char const *errbeg, char const *errend);
char const *p_errbeg, *p_errend; char const *p_errbeg, *p_errend;
stack_state p_stack; stack_state p_stack;

View File

@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include "cs_thread.hh" #include "cs_thread.hh"
#include "cs_error.hh"
namespace cubescript { namespace cubescript {
@ -44,46 +45,7 @@ LIBCUBESCRIPT_EXPORT bool stack_state::gap() const {
return p_gap; return p_gap;
} }
LIBCUBESCRIPT_EXPORT char *error::request_buf( static stack_state save_stack(state &cs) {
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) {
auto &ts = state_p{cs}.ts(); auto &ts = state_p{cs}.ts();
builtin_var *dalias = ts.istate->ivar_dbgalias; builtin_var *dalias = ts.istate->ivar_dbgalias;
auto dval = std::clamp( auto dval = std::clamp(
@ -124,4 +86,22 @@ LIBCUBESCRIPT_EXPORT stack_state error::save_stack(state &cs) {
return stack_state{cs, ret, total > dval}; 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 */ } /* namespace cubescript */

View File

@ -3,6 +3,7 @@
#include "cs_bcode.hh" #include "cs_bcode.hh"
#include "cs_thread.hh" #include "cs_thread.hh"
#include "cs_vm.hh" #include "cs_vm.hh"
#include "cs_error.hh"
namespace cubescript { namespace cubescript {
@ -229,10 +230,10 @@ LIBCUBESCRIPT_EXPORT void builtin_var::save(state &cs) {
auto &ts = state_p{cs}.ts(); auto &ts = state_p{cs}.ts();
if ((ts.ident_flags & IDENT_FLAG_OVERRIDDEN) || is_overridable()) { if ((ts.ident_flags & IDENT_FLAG_OVERRIDDEN) || is_overridable()) {
if (p_impl->p_flags & IDENT_FLAG_PERSIST) { if (p_impl->p_flags & IDENT_FLAG_PERSIST) {
throw error{ throw error_p::make(
cs, "cannot override persistent variable '%s'", cs, "cannot override persistent variable '%s'",
name().data() name().data()
}; );
} }
if (!(p_impl->p_flags & IDENT_FLAG_OVERRIDDEN)) { if (!(p_impl->p_flags & IDENT_FLAG_OVERRIDDEN)) {
auto *imp = static_cast<var_impl *>(p_impl); 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 state &cs, any_value val, bool do_write, bool trigger
) { ) {
if (is_read_only()) { if (is_read_only()) {
throw error{ throw error_p::make(
cs, "variable '%s' is read only", name().data() cs, "variable '%s' is read only", name().data()
}; );
} }
if (!do_write) { if (!do_write) {
return; return;
@ -375,7 +376,7 @@ LIBCUBESCRIPT_EXPORT any_value command::call(
LIBCUBESCRIPT_EXPORT alias_local::alias_local(state &cs, ident &a) { LIBCUBESCRIPT_EXPORT alias_local::alias_local(state &cs, ident &a) {
if (a.type() != ident_type::ALIAS) { 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(); auto &ts = state_p{cs}.ts();
p_alias = static_cast<alias *>(&a); p_alias = static_cast<alias *>(&a);

View File

@ -6,6 +6,7 @@
#include <iterator> #include <iterator>
#include "cs_parser.hh" #include "cs_parser.hh"
#include "cs_error.hh"
namespace cubescript { namespace cubescript {
@ -56,9 +57,9 @@ LIBCUBESCRIPT_EXPORT char const *parse_string(
end: end:
nlines = nl; nlines = nl;
if ((beg == end) || (*beg != '\"')) { if ((beg == end) || (*beg != '\"')) {
throw error{ throw error_p::make(
cs, "unfinished string '%.*s'", int(beg - orig), orig cs, "unfinished string '%.*s'", int(beg - orig), orig
}; );
} }
return ++beg; return ++beg;
} }
@ -1116,7 +1117,9 @@ static bool finish_statement(parser_state &ps, bool more, int term) {
/* EOS */ /* EOS */
case '\0': case '\0':
if (ps.current() != term) { 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; return false;
/* terminating parens/brackets */ /* terminating parens/brackets */
@ -1127,7 +1130,9 @@ static bool finish_statement(parser_state &ps, bool more, int term) {
ps.next_char(); ps.next_char();
return false; return false;
} }
throw error{*ps.ts.pstate, "unexpected \"%c\"", ps.current()}; throw error_p::make(
*ps.ts.pstate, "unexpected \"%c\"", ps.current()
);
/* potential comment */ /* potential comment */
case '/': case '/':
ps.next_char(); ps.next_char();

View File

@ -8,6 +8,7 @@
#include "cs_strman.hh" #include "cs_strman.hh"
#include "cs_vm.hh" // break/continue, call_with_args #include "cs_vm.hh" // break/continue, call_with_args
#include "cs_parser.hh" #include "cs_parser.hh"
#include "cs_error.hh"
namespace cubescript { namespace cubescript {
@ -58,9 +59,9 @@ ident &internal_state::new_ident(state &cs, std::string_view name, int flags) {
ident *id = get_ident(name); ident *id = get_ident(name);
if (!id) { if (!id) {
if (!is_valid_name(name)) { if (!is_valid_name(name)) {
throw error{ throw error_p::make(
cs, "'%s' is not a valid identifier name", name.data() cs, "'%s' is not a valid identifier name", name.data()
}; );
} }
auto *inst = create<alias_impl>( auto *inst = create<alias_impl>(
cs, string_ref{cs, name}, flags cs, string_ref{cs, name}, flags
@ -392,14 +393,14 @@ static void var_name_check(
state &cs, ident *id, std::string_view n state &cs, ident *id, std::string_view n
) { ) {
if (id) { if (id) {
throw error{ throw error_p::make(
cs, "redefinition of ident '%.*s'", int(n.size()), n.data() cs, "redefinition of ident '%.*s'", int(n.size()), n.data()
}; );
} else if (!is_valid_name(n)) { } else if (!is_valid_name(n)) {
throw error{ throw error_p::make(
cs, "'%.*s' is not a valid variable name", cs, "'%.*s' is not a valid variable name",
int(n.size()), n.data() 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); id->get().call(span_type<any_value>{&v, 1}, *this);
break; break;
default: default:
throw error{ throw error_p::make(
*this, "cannot redefine builtin %s with an alias", *this, "cannot redefine builtin %s with an alias",
id->get().name().data() id->get().name().data()
}; );
} }
} else if (!is_valid_name(name)) { } 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 { } else {
auto *a = p_tstate->istate->create<alias_impl>( auto *a = p_tstate->istate->create<alias_impl>(
*this, string_ref{*this, name}, std::move(v), *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{}; 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) { LIBCUBESCRIPT_EXPORT void state::reset_value(std::string_view name) {
auto id = get_ident(name); auto id = get_ident(name);
if (!id) { 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 (id->get().type() == ident_type::VAR) {
if (static_cast<builtin_var &>(id->get()).is_read_only()) { 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()); clear_override(id->get());
@ -619,9 +626,9 @@ LIBCUBESCRIPT_EXPORT command &state::new_command(
fmt += 2; fmt += 2;
break; break;
default: default:
throw error{ throw error_p::make(
*this, "invalid argument type: %c", *fmt *this, "invalid argument type: %c", *fmt
}; );
} }
} }
auto &is = *p_tstate->istate; 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 */ /* we haven't found one matching the list, so error */
is.destroy(cmd); is.destroy(cmd);
throw error{ throw error_p::make(
*this, "forbidden builtin command: %.*s", *this, "forbidden builtin command: %.*s",
int(name.size()), name.data() int(name.size()), name.data()
}; );
} }
valid: valid:
if (is.get_ident(name)) { if (is.get_ident(name)) {
is.destroy(cmd); is.destroy(cmd);
throw error{ throw error_p::make(
*this, "redefinition of ident '%.*s'", *this, "redefinition of ident '%.*s'",
int(name.size()), name.data() int(name.size()), name.data()
}; );
} }
do_add: do_add:
is.add_ident(cmd, cmd); is.add_ident(cmd, cmd);

View File

@ -1,5 +1,7 @@
#include "cs_thread.hh" #include "cs_thread.hh"
#include <cstdio>
namespace cubescript { namespace cubescript {
thread_state::thread_state(internal_state *cs): thread_state::thread_state(internal_state *cs):
@ -27,4 +29,41 @@ alias_stack &thread_state::get_astack(alias const *a) {
return it.first->second; 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 */ } /* namespace cubescript */

View File

@ -53,6 +53,8 @@ struct thread_state {
hook_func const &get_hook() const { return call_hook; } hook_func const &get_hook() const { return call_hook; }
alias_stack &get_astack(alias const *a); alias_stack &get_astack(alias const *a);
char *request_errbuf(std::size_t bufs, char *&sp);
}; };
} /* namespace cubescript */ } /* namespace cubescript */

View File

@ -2,6 +2,7 @@
#include "cs_vm.hh" #include "cs_vm.hh"
#include "cs_std.hh" #include "cs_std.hh"
#include "cs_parser.hh" #include "cs_parser.hh"
#include "cs_error.hh"
#include <cstdio> #include <cstdio>
#include <cmath> #include <cmath>
@ -149,9 +150,9 @@ bool exec_alias(
return false; return false;
} }
} else if (aast.flags & IDENT_FLAG_UNKNOWN) { } else if (aast.flags & IDENT_FLAG_UNKNOWN) {
throw error { throw error_p::make(
*ts.pstate, "unknown command: %s", a->name().data() *ts.pstate, "unknown command: %s", a->name().data()
}; );
} }
/* excess arguments get ignored (make error maybe?) */ /* excess arguments get ignored (make error maybe?) */
callargs = std::min(callargs, MAX_ARGUMENTS); callargs = std::min(callargs, MAX_ARGUMENTS);
@ -237,9 +238,9 @@ static inline alias *get_lookup_id(
} else { } else {
ast = &ts.get_astack(static_cast<alias *>(id)); ast = &ts.get_astack(static_cast<alias *>(id));
if (ast->flags & IDENT_FLAG_UNKNOWN) { if (ast->flags & IDENT_FLAG_UNKNOWN) {
throw error{ throw error_p::make(
*ts.pstate, "unknown alias lookup: %s", id->name().data() *ts.pstate, "unknown alias lookup: %s", id->name().data()
}; );
} }
} }
return static_cast<alias *>(id); return static_cast<alias *>(id);
@ -799,9 +800,9 @@ noid:
result.force_none(); result.force_none();
force_arg(cs, result, op & BC_INST_RET_MASK); force_arg(cs, result, op & BC_INST_RET_MASK);
std::string_view ids{idn}; std::string_view ids{idn};
throw error{ throw error_p::make(
cs, "unknown command: %s", ids.data() cs, "unknown command: %s", ids.data()
}; );
} }
result.force_none(); result.force_none();
switch (ident_p{id->get()}.impl().p_type) { switch (ident_p{id->get()}.impl().p_type) {

View File

@ -5,6 +5,7 @@
#include "cs_std.hh" #include "cs_std.hh"
#include "cs_ident.hh" #include "cs_ident.hh"
#include "cs_thread.hh" #include "cs_thread.hh"
#include "cs_error.hh"
namespace cubescript { namespace cubescript {
@ -72,10 +73,10 @@ LIBCUBESCRIPT_EXPORT void std_init_base(state &gcs) {
auto &cret = args[1].get_ident(cs); auto &cret = args[1].get_ident(cs);
auto &css = args[2].get_ident(cs); auto &css = args[2].get_ident(cs);
if (cret.type() != ident_type::ALIAS) { 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) { 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{}; any_value result{}, tback{};
bool rc = true; bool rc = true;
@ -98,6 +99,25 @@ LIBCUBESCRIPT_EXPORT void std_init_base(state &gcs) {
ts.get_astack(ssa).set_alias(ssa, ts, tback); 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) { new_cmd_quiet(gcs, "?", "aaa", [](auto &, auto args, auto &res) {
if (args[0].get_bool()) { if (args[0].get_bool()) {
res = std::move(args[1]); res = std::move(args[1]);
@ -343,7 +363,7 @@ end:
new_cmd_quiet(gcs, "getalias", "s", [](auto &cs, auto args, auto &res) { new_cmd_quiet(gcs, "getalias", "s", [](auto &cs, auto args, auto &res) {
auto &id = cs.new_ident(args[0].get_string(cs)); auto &id = cs.new_ident(args[0].get_string(cs));
if (id.type() != ident_type::ALIAS) { 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) { if (ident_p{id}.impl().p_flags & IDENT_FLAG_UNKNOWN) {
return; return;

View File

@ -58,26 +58,6 @@ int main(int argc, char **argv) {
throw skip_test{}; 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 { try {
do_exec_file(gcs, argv[1]); do_exec_file(gcs, argv[1]);
} catch (skip_test) { } catch (skip_test) {

View File

@ -360,9 +360,11 @@ int main(int argc, char **argv) {
cs::any_value val{}; cs::any_value val{};
bool ret = do_exec_file(css, file, val); bool ret = do_exec_file(css, file, val);
if (!ret) { if (!ret) {
throw cs::error( char buf[4096];
css, "could not execute file \"%s\"", file.data() std::snprintf(
buf, sizeof(buf), "could not execute file \"%s\"", file.data()
); );
throw cs::error(css, buf);
} }
}); });