From 805e7c0917c2623c67c84e60ba314927dc0d178a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 25 May 2018 14:33:17 -0600 Subject: [PATCH] Fix up some error handling --- include/chaiscript/dispatchkit/bootstrap.hpp | 4 +-- .../chaiscript/language/chaiscript_common.hpp | 2 -- .../chaiscript/language/chaiscript_eval.hpp | 25 ++++++++++++------- .../language/chaiscript_optimizer.hpp | 2 +- src/main.cpp | 8 +++--- unittests/3.x/assign_const.chai | 4 +-- unittests/3.x/function_introspection.chai | 6 ++--- .../3.x/invalid_function_assignment.chai | 2 +- .../3.x/invalid_function_reassignment.chai | 3 ++- unittests/3.x/malformed_inline_map.chai | 8 +++++- unittests/3.x/operators_float.chai | 2 +- unittests/assign_const.chai | 4 +-- ...dynamic_object_dynamic_attrs_explicit.chai | 4 ++- unittests/function_introspection.chai | 6 ++--- unittests/function_redefinition.chai | 2 +- unittests/invalid_function_assignment.chai | 2 +- unittests/invalid_function_reassignment.chai | 5 +++- unittests/malformed_inline_map.chai | 8 +++++- unittests/operators_float.chai | 4 ++- unittests/operators_int.chai | 10 ++++++++ unittests/return_value_assignment.chai | 14 +++++++++++ unittests/string_unicode_unicode.chai | 4 +-- unittests/unit_test.inc | 6 +++-- unittests/variable_redefinition.chai | 2 +- 24 files changed, 94 insertions(+), 43 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 671a723a..c74b86b9 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -466,7 +466,7 @@ namespace chaiscript m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); m.add(fun(&Boxed_Number::to_string), "to_string"); - + bootstrap_pod_type("double", m); bootstrap_pod_type("long_double", m); bootstrap_pod_type("float", m); @@ -495,7 +495,7 @@ namespace chaiscript opers_arithmetic_pod(m); - + m.add(fun(&Build_Info::version_major), "version_major"); m.add(fun(&Build_Info::version_minor), "version_minor"); m.add(fun(&Build_Info::version_patch), "version_patch"); diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index b1e16688..5d66bd94 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -647,8 +647,6 @@ namespace chaiscript /// Special type for returned values struct Return_Value { Boxed_Value retval; - - explicit Return_Value(Boxed_Value t_return_value) : retval(std::move(t_return_value)) { } }; diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index f6472933..ff900312 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -448,19 +448,22 @@ namespace chaiscript Boxed_Value rhs = this->children[1]->eval(t_ss); Boxed_Value lhs = this->children[0]->eval(t_ss); + if (lhs.is_return_value()) { + throw exception::eval_error("Error, cannot assign to temporary value."); + } else if (lhs.is_const()) { + throw exception::eval_error("Error, cannot assign to constant value."); + } + + if (m_oper != Operators::Opers::invalid && lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { try { return Boxed_Number::do_oper(m_oper, lhs, rhs); } catch (const std::exception &) { - throw exception::eval_error("Error with unsupported arithmetic assignment operation"); + throw exception::eval_error("Error with unsupported arithmetic assignment operation."); } } else if (m_oper == Operators::Opers::assign) { - if (lhs.is_return_value()) { - throw exception::eval_error("Error, cannot assign to temporary value."); - } - try { if (lhs.is_undef()) { @@ -470,7 +473,7 @@ namespace chaiscript && this->children[0]->children[0]->identifier == AST_Node_Type::Reference) ) ) - + { /// \todo This does not handle the case of an unassigned reference variable /// being assigned outside of its declaration @@ -1141,10 +1144,10 @@ namespace chaiscript Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ if (!this->children.empty()) { - throw detail::Return_Value(this->children[0]->eval(t_ss)); + throw detail::Return_Value{this->children[0]->eval(t_ss)}; } else { - throw detail::Return_Value(void_var()); + throw detail::Return_Value{void_var()}; } } }; @@ -1201,6 +1204,10 @@ namespace chaiscript // short circuit arithmetic operations if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic()) { + if ((m_oper == Operators::Opers::pre_increment || m_oper == Operators::Opers::pre_decrement) && bv.is_const()) + { + throw exception::eval_error("Error with prefix operator evaluation: cannot modify constant value."); + } return Boxed_Number::do_oper(m_oper, bv); } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); @@ -1409,7 +1416,7 @@ namespace chaiscript Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), - std::vector>(std::make_move_iterator(t_children.begin()), + std::vector>(std::make_move_iterator(t_children.begin()), std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node::has_guard(t_children, 1)?2:1))) ), m_body_node(Def_AST_Node::get_body_node(std::move(t_children))), diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index a8118fba..4bcae1a4 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -356,7 +356,7 @@ namespace chaiscript { const auto make_constant = [&node, &fun_name](auto val){ const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")"; - return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, const_var(val)); }; if (fun_name == "double") { diff --git a/src/main.cpp b/src/main.cpp index f3bd2d82..b5d55520 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -164,15 +164,15 @@ void help(int n) { } } -bool throws_exception(const std::function &f) +std::string throws_exception(const std::function &f) { try { f(); - } catch (...) { - return true; + } catch (const std::exception &e) { + return e.what(); } - return false; + return ""; } chaiscript::exception::eval_error get_eval_error(const std::function &f) diff --git a/unittests/3.x/assign_const.chai b/unittests/3.x/assign_const.chai index ff6a8c3d..9726ed50 100644 --- a/unittests/3.x/assign_const.chai +++ b/unittests/3.x/assign_const.chai @@ -1,2 +1,2 @@ -assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 = 2 } ); -assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 + 2 = 2 } ); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 = 2 } ); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 + 2 = 2 } ); diff --git a/unittests/3.x/function_introspection.chai b/unittests/3.x/function_introspection.chai index 96b2ec67..014cf45d 100644 --- a/unittests/3.x/function_introspection.chai +++ b/unittests/3.x/function_introspection.chai @@ -66,10 +66,10 @@ var group = group_guard.get_contained_functions(); assert_equal(true, group[0].has_guard()) assert_equal(false, group[1].has_guard()) -assert_throws("Function does not have a guard", fun() { group[0].get_guard(); } ); -assert_throws("Function does not have a guard", fun() { without_guard.get_guard(); } ); +assert_throws("Function does not have a guard", fun[group]() { group[1].get_guard(); } ); +assert_throws("Function does not have a guard", fun[without_guard]() { without_guard.get_guard(); } ); var guard = with_guard.get_guard(); assert_equal(false, guard.has_guard()); -assert_throws("Function does not have a guard", fun() { guard.get_guard(); } ); +assert_throws("Function does not have a guard", fun[guard]() { guard.get_guard(); } ); diff --git a/unittests/3.x/invalid_function_assignment.chai b/unittests/3.x/invalid_function_assignment.chai index 99b098db..3aa1bc2f 100644 --- a/unittests/3.x/invalid_function_assignment.chai +++ b/unittests/3.x/invalid_function_assignment.chai @@ -1 +1 @@ -assert_throws("Illegal const function assignment", fun() { clone = `-` } ); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } ); diff --git a/unittests/3.x/invalid_function_reassignment.chai b/unittests/3.x/invalid_function_reassignment.chai index cc7cb5af..77307b6f 100644 --- a/unittests/3.x/invalid_function_reassignment.chai +++ b/unittests/3.x/invalid_function_reassignment.chai @@ -1 +1,2 @@ -assert_throws("Invalid function reassignment", fun() { var x = 5; x = `-`; } ); +assert_throws("Error: \"Unable to find appropriate'=' operator.\" With parameters: (int, const Function)", fun() { auto x = 5; x = `-`; } ); + diff --git a/unittests/3.x/malformed_inline_map.chai b/unittests/3.x/malformed_inline_map.chai index d267bcfe..1488ded4 100644 --- a/unittests/3.x/malformed_inline_map.chai +++ b/unittests/3.x/malformed_inline_map.chai @@ -1,2 +1,8 @@ -assert_throws("Parse failure", fun() { eval("[\"hello\":5,\"j\",\"k\"]") } ); +try { + eval("[\"hello\":5,\"j\",\"k\"]"); + assert_true(false); +} catch (eval_error ee) { +} + + diff --git a/unittests/3.x/operators_float.chai b/unittests/3.x/operators_float.chai index 931606fb..847f5e58 100644 --- a/unittests/3.x/operators_float.chai +++ b/unittests/3.x/operators_float.chai @@ -13,4 +13,4 @@ assert_equal(0, i -= i) assert_equal(3, j *= 1.5) assert_equal(1.5, j /= 2) assert_equal(2.5, j += 1) -assert_throws("No modulus for float", fun() { k % 2 } ); +assert_throws("Error: \"Error with numeric operator calling: %\"", fun[k]() { k % 2 } ); diff --git a/unittests/assign_const.chai b/unittests/assign_const.chai index ff6a8c3d..9726ed50 100644 --- a/unittests/assign_const.chai +++ b/unittests/assign_const.chai @@ -1,2 +1,2 @@ -assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 = 2 } ); -assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 + 2 = 2 } ); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 = 2 } ); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 + 2 = 2 } ); diff --git a/unittests/dynamic_object_dynamic_attrs_explicit.chai b/unittests/dynamic_object_dynamic_attrs_explicit.chai index b37dc0fb..53ede571 100644 --- a/unittests/dynamic_object_dynamic_attrs_explicit.chai +++ b/unittests/dynamic_object_dynamic_attrs_explicit.chai @@ -10,5 +10,7 @@ var o = MyClass(); assert_true(o.is_explicit()); -assert_throws("error", fun[o](){o.x = 2}) +assert_throws("Error: \"'x' is not a function.\"", fun[o](){o.x = 2}) + + diff --git a/unittests/function_introspection.chai b/unittests/function_introspection.chai index fbfee5a4..2c511a73 100644 --- a/unittests/function_introspection.chai +++ b/unittests/function_introspection.chai @@ -66,10 +66,10 @@ auto group = group_guard.get_contained_functions(); assert_equal(true, group[0].has_guard()) assert_equal(false, group[1].has_guard()) -assert_throws("Function does not have a guard", fun() { group[0].get_guard(); } ); -assert_throws("Function does not have a guard", fun() { without_guard.get_guard(); } ); +assert_throws("Function does not have a guard", fun[group]() { group[1].get_guard(); } ); +assert_throws("Function does not have a guard", fun[without_guard]() { without_guard.get_guard(); } ); auto guard = with_guard.get_guard(); assert_equal(false, guard.has_guard()); -assert_throws("Function does not have a guard", fun() { guard.get_guard(); } ); +assert_throws("Function does not have a guard", fun[guard]() { guard.get_guard(); } ); diff --git a/unittests/function_redefinition.chai b/unittests/function_redefinition.chai index f82a414c..e1f51f5e 100644 --- a/unittests/function_redefinition.chai +++ b/unittests/function_redefinition.chai @@ -1,2 +1,2 @@ -assert_throws("Function already defined", fun(){ def foo(x) { x + 1 }; def foo(x) { x + 1 } } ); +assert_throws("Error: \"Function redefined 'foo'\"", fun(){ def foo(x) { x + 1 }; def foo(x) { x + 1 } } ); diff --git a/unittests/invalid_function_assignment.chai b/unittests/invalid_function_assignment.chai index 99b098db..3aa1bc2f 100644 --- a/unittests/invalid_function_assignment.chai +++ b/unittests/invalid_function_assignment.chai @@ -1 +1 @@ -assert_throws("Illegal const function assignment", fun() { clone = `-` } ); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } ); diff --git a/unittests/invalid_function_reassignment.chai b/unittests/invalid_function_reassignment.chai index 784372ac..76ff6e61 100644 --- a/unittests/invalid_function_reassignment.chai +++ b/unittests/invalid_function_reassignment.chai @@ -1 +1,4 @@ -assert_throws("Invalid function reassignment", fun() { auto x = 5; x = `-`; } ); +assert_throws("Error: \"Unable to find appropriate'=' operator.\" With parameters: (int, const Function)", fun() { auto x = 5; x = `-`; } ); + + + diff --git a/unittests/malformed_inline_map.chai b/unittests/malformed_inline_map.chai index d267bcfe..fb99fc06 100644 --- a/unittests/malformed_inline_map.chai +++ b/unittests/malformed_inline_map.chai @@ -1,2 +1,8 @@ -assert_throws("Parse failure", fun() { eval("[\"hello\":5,\"j\",\"k\"]") } ); +try { + eval("[\"hello\":5,\"j\",\"k\"]"); + assert_true(false); +} catch (eval_error ee) { +} + + diff --git a/unittests/operators_float.chai b/unittests/operators_float.chai index 823d4289..c032e037 100644 --- a/unittests/operators_float.chai +++ b/unittests/operators_float.chai @@ -13,4 +13,6 @@ assert_equal(0, i -= i) assert_equal(3, j *= 1.5) assert_equal(1.5, j /= 2) assert_equal(2.5, j += 1) -assert_throws("No modulus for float", fun() { k % 2 } ); +assert_throws("Error: \"Error with numeric operator calling: %\"", fun[k]() { k % 2 } ); + + diff --git a/unittests/operators_int.chai b/unittests/operators_int.chai index 395ce27d..e53dccd5 100644 --- a/unittests/operators_int.chai +++ b/unittests/operators_int.chai @@ -29,3 +29,13 @@ assert_equal(2, j += 1); assert_equal(1, --j); assert_equal(2, ++j); + +assert_throws("Error: \"Error with prefix operator evaluation: cannot modify constant value.\"", fun() { ++4; }); +assert_throws("Error: \"Error with prefix operator evaluation: cannot modify constant value.\"", fun() { --4; }); + +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 5 = 5; }); +assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { int(5) = 5; }); + + + + diff --git a/unittests/return_value_assignment.chai b/unittests/return_value_assignment.chai index e6906e61..9a901b9f 100644 --- a/unittests/return_value_assignment.chai +++ b/unittests/return_value_assignment.chai @@ -6,6 +6,20 @@ try { print("Caught Error: " + e.what()); } +def get_value() +{ + var i = 5; + return i; +} + +def get_value_2() +{ + return 5; +} + +// TODO these should be fixed somehow +//assert_throws("Cannot assign to temporary", fun[](){ get_value() = 3; }); +//assert_throws("Cannot assign to temporary", fun[](){ get_value_2() = 3; }); try { diff --git a/unittests/string_unicode_unicode.chai b/unittests/string_unicode_unicode.chai index 93364f05..00b3cce8 100644 --- a/unittests/string_unicode_unicode.chai +++ b/unittests/string_unicode_unicode.chai @@ -8,8 +8,8 @@ assert_equal("Test\u2022Me", "Test•Me") assert_equal("\xF0\x9F\x8D\x8C", "🍌") assert_equal("\U0001F34C", "🍌") -assert_throws("Invalid 16 bit universal character", fun(){ parse("\"\\uD83C\""); }); -assert_throws("Incomplete unicode escape sequence", fun(){ parse("\"\\uD83 \""); }); +assert_throws("Error: \"Invalid 16 bit universal character\"", fun(){ parse("\"\\uD83C\""); }); +assert_throws("Error: \"Incomplete unicode escape sequence\"", fun(){ parse("\"\\uD83 \""); }); assert_equal("\U00024B62", "𤭢") diff --git a/unittests/unit_test.inc b/unittests/unit_test.inc index 681c5197..1528dc4d 100644 --- a/unittests/unit_test.inc +++ b/unittests/unit_test.inc @@ -42,12 +42,14 @@ def assert_not_equal(x, y) def assert_throws(desc, x) { - if (throws_exception(x)) + auto result = trim(throws_exception(x)); + if (result == desc) { // Passes } else { // Fails - print("assert_throws failure, function did not throw exception: " + to_string(desc)); + print("assert_throws failed: got '${result}' expected '${desc}'"); exit(-1); } } + diff --git a/unittests/variable_redefinition.chai b/unittests/variable_redefinition.chai index c59107c5..21ed9614 100644 --- a/unittests/variable_redefinition.chai +++ b/unittests/variable_redefinition.chai @@ -1,2 +1,2 @@ -assert_throws("Variable already defined", fun() { auto y = 10; auto y = 20; }) +assert_throws("Error: \"Variable redefined 'y'\"", fun() { auto y = 10; auto y = 20; })