#ifndef ERROR_H_INCLUDED_ #define ERROR_H_INCLUDED_ #include #include #include #include #include #include #include "kblib/containers.h" #include "kblib/convert.h" #include "kblib/stringops.h" enum class ec : unsigned { // section 0: placeholder errors unknown = 0, internal, no_feature, // section 1: system errors // section 2: YAML Format errors (load time) y_internal = 200, too_many_tnode_args, invalid_stage_type, invalid_normalization_form, bad_regex, // section 3: noderef syntax errors (expansion time) n_internal = 300, unterminated_noderef, extra_rb, lb_in_name, illegal_escape, // section 4: template string errors (load time) p_internal = 400, bad_format, blank_fname, invalid_fname, call_arg, eos = 405, extra_rp, extra_lp, extra_p, extra_ab, extra_h = 410, extra_c, early_close, escape_nostr, overmarked_arg, invalid_raw = 415, no_function, // section 5: template evaluation errors (expansion time) e_internal = 500, bad_arg_type, bad_arg_rank, bad_randspec, invoke_null, invoke_type = 505, node_bad_cvt, unpaired_args, too_many_args, not_enough_args, e_nonode = 510, range_error, bad_math, m_divzero, null_var, // section 6: transformation evaluation errors (transform time) t_internal = 600, }; const std::map err_strs{ // E0 {ec::unknown, "Unknown error."}, {ec::internal, "Internal error."}, {ec::no_feature, "Feature not implemented yet."}, // E200 {ec::y_internal, "Unknown load-time error."}, {ec::too_many_tnode_args, "Too many arguments specified for a node."}, {ec::invalid_stage_type, "Invalid replace stage type."}, {ec::invalid_normalization_form, "Invalid normalization form."}, {ec::bad_regex, "Invalid regular expression"}, // E205 // E300 {ec::n_internal, "Internal error in noderef parser."}, {ec::unterminated_noderef, "Unterminated reference."}, {ec::extra_rb, "Encountered '}' outside reference."}, {ec::lb_in_name, "Encountered '{' inside reference."}, {ec::illegal_escape, "Illegal escape sequence."}, // E400 {ec::p_internal, "Internal error in template parser."}, {ec::bad_format, "Invalid argref."}, {ec::blank_fname, "Function names cannot be blank."}, {ec::invalid_fname, "Invalid character in function name."}, {ec::call_arg, "Arguments cannot be used as functions."}, // E405 {ec::eos, "Unexpected end-of-input"}, {ec::extra_rp, "Unmatched )."}, {ec::extra_lp, "Unmatched (; or unexpected >."}, {ec::extra_p, "Unexpected top-level |."}, {ec::extra_ab, "unescaped < in template context."}, // E410 {ec::extra_h, "encountered unexpected # in argument context."}, {ec::extra_c, "encountered unexpected characters after )."}, {ec::early_close, "blank argref is invalid."}, {ec::escape_nostr, "escape sequence outside of string context."}, {ec::overmarked_arg, "Argrefs at topmost level do not need #."}, // E415 {ec::invalid_raw, "Invalid raw string syntax."}, {ec::no_function, "No function by this name exists."}, // E500 {ec::e_internal, "Internal error in template evaluator."}, {ec::bad_arg_type, "Template function received argument of incorrect type."}, {ec::bad_arg_rank, "Template function received a list when it expected a single element."}, {ec::bad_randspec, "Bad parameters for a random function."}, {ec::invoke_null, "Invoke requires arguments."}, // E505 {ec::invoke_type, "The first argument to invoke must be a string, not a list."}, {ec::node_bad_cvt, "A synthesized node cannot be converted to another type."}, {ec::unpaired_args, "Template function expected arguments in sets, but " "could not complete a set."}, {ec::too_many_args, "Template function received more arguments than it expected."}, {ec::not_enough_args, "Template function did not receive all expected arguments."}, // E510 {ec::e_nonode, "'node' function's argument does not name a node"}, {ec::range_error, "? or [] index out of range."}, {ec::bad_math, "Invalid mathematical expression."}, {ec::m_divzero, "Division by zero occurred."}, {ec::null_var, "get() of nonexistent variable."}, // E600 {ec::t_internal, "Internal error in transformation processor"}, }; inline auto format_err(ec err) -> std::string { std::ostringstream out; out << 'E' << std::setw(3) << std::setfill('0') << static_cast(err) << ": " << kblib::get_or(err_strs, err, err_strs.at(ec::unknown)) << '\n'; return out.str(); } class wordgen_error final { public: wordgen_error(std::string desc, std::string context, std::string loc, std::string info) : l1(std::make_shared(std::move(desc))), l2(std::make_shared(std::move(context))), l3(std::make_shared(std::move(loc))), l4(std::make_shared(std::move(info))), c(ec::unknown) {} wordgen_error(ec err, std::string context, std::string loc, std::string info) : l1(std::make_shared(format_err(err))), l2(std::make_shared(std::move(context))), l3(std::make_shared(std::move(loc))), l4(std::make_shared(std::move(info))), c(err) {} std::shared_ptr l1, l2, l3, l4; ec c; auto code() const -> ec { return c; } auto what() const -> std::string { return kblib::concat(*l1, *l2, *l3, *l4); } }; inline auto operator==(const wordgen_error& l, const wordgen_error& r) -> bool { return l.what() == r.what(); } inline auto operator!=(const wordgen_error& l, const wordgen_error& r) -> bool { return l.what() != r.what(); } [[noreturn]] inline auto parse_error(ec err, std::string_view str, const char* epos, std::string msg = "") -> void { auto printable = kblib::escapify(str); auto index = kblib::calculate_translated_index(str, epos); throw wordgen_error(err, kblib::concat("\"", printable, "\"\n"), kblib::concat(" ", kblib::repeat(' ', index), "^\n"), std::move(msg)); } [[noreturn]] inline auto parse_error(ec err, std::string_view str, long long epos, std::string msg = "") -> void { parse_error(err, str, str.data() + epos, std::move(msg)); } #endif // ERROR_H_INCLUDED_