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
#define LIBCUBESCRIPT_CUBESCRIPT_CALLABLE_HH
#include <cstring>
#include <cstddef>
#include <utility>
#include <type_traits>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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