diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 5e5bdbe..1a8bf93 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -428,9 +428,6 @@ namespace chaiscript m->add(fun(&Type_Info::name), "cpp_name"); m->add(fun(&Type_Info::bare_name), "cpp_bare_name"); m->add(fun(&Type_Info::bare_equal), "bare_equal"); - typedef bool (Type_Info::*typeinfocompare)(const Type_Info &) const; - m->add(fun(typeinfocompare(&Type_Info::operator==)), "=="); - m->add(fun(&Type_Info::bare_equal), "bare_equal"); basic_constructors("bool", m); diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 0fa0439..93a640d 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -33,10 +33,7 @@ namespace chaiscript namespace exception { /** - * Exception thrown in the case that a multi method dispatch fails - * because no matching function was found - * at runtime due to either an arity_error, a guard_error or a bad_boxed_cast - * exception + * Exception thrown in the case that an object name is invalid because it is a reserved word */ class reserved_word_error : public std::runtime_error { @@ -58,6 +55,30 @@ namespace chaiscript }; + /** + * Exception thrown in the case that an object name is invalid because it already exists in current context + */ + class name_conflict_error : public std::runtime_error + { + public: + name_conflict_error(const std::string &t_name) throw() + : std::runtime_error("Name already exists in current context " + t_name), m_name(t_name) + { + } + + virtual ~name_conflict_error() throw() {} + + std::string name() const + { + return m_name; + } + + private: + std::string m_name; + + }; + + /** * Exception thrown in the case that a non-const object was added as a shared object */ @@ -144,7 +165,12 @@ namespace chaiscript { while (begin != end) { - t.add(begin->first, begin->second); + try { + t.add(begin->first, begin->second); + } catch (const chaiscript::exception::name_conflict_error &) { + /// \todo Should we throw an error if there's a name conflict + /// while applying a module? + } ++begin; } } @@ -378,16 +404,17 @@ namespace chaiscript /** * Add a new named Proxy_Function to the system */ - bool add(const Proxy_Function &f, const std::string &name) + void add(const Proxy_Function &f, const std::string &name) { validate_object_name(name); - return add_function(f, name); + add_function(f, name); } /** * Set the value of an object, by name. If the object * is not available in the current scope it is created */ + /* void add(const Boxed_Value &obj, const std::string &name) { validate_object_name(name); @@ -405,6 +432,7 @@ namespace chaiscript add_object(name, obj); } + */ /** * Adds a named object to the current scope @@ -413,7 +441,15 @@ namespace chaiscript { StackData &stack = get_stack_data(); validate_object_name(name); - stack.back()[name] = obj; + + Scope &scope = stack.back(); + Scope::iterator itr = scope.find(name); + if (itr != stack.back().end()) + { + throw chaiscript::exception::name_conflict_error(name); + } else { + stack.back().insert(std::make_pair(name, obj)); + } } /** @@ -429,7 +465,12 @@ namespace chaiscript chaiscript::detail::threading::unique_lock l(m_global_object_mutex); - m_state.m_global_objects[name] = obj; + if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) + { + throw chaiscript::exception::name_conflict_error(name); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } } /** @@ -935,11 +976,10 @@ namespace chaiscript } /** - * Implementation detail for adding a function. Returns - * true if the function was added, false if a function with the - * same signature and name already exists. + * Implementation detail for adding a function. + * \throws exception::name_conflict_error if there's a function matching the given one being added */ - bool add_function(const Proxy_Function &t_f, const std::string &t_name) + void add_function(const Proxy_Function &t_f, const std::string &t_name) { chaiscript::detail::threading::unique_lock l(m_mutex); @@ -957,7 +997,7 @@ namespace chaiscript { if ((*t_f) == *(*itr2)) { - return false; + throw chaiscript::exception::name_conflict_error(t_name); } } @@ -968,8 +1008,6 @@ namespace chaiscript vec.push_back(t_f); funcs.insert(std::make_pair(t_name, vec)); } - - return true; } mutable chaiscript::detail::threading::shared_mutex m_mutex; diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index e3ab3dc..b1cf052 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -188,7 +188,12 @@ namespace chaiscript virtual bool operator==(const Proxy_Function_Base &rhs) const { - return this == &rhs; + const Dynamic_Proxy_Function *prhs = dynamic_cast(&rhs); + + return this == &rhs + || (prhs + && this->m_arity == prhs->m_arity + && !this->m_guard && !prhs->m_guard); } virtual bool call_match(const std::vector &vals) const diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 1bb296d..b6d24d2 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -350,6 +350,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &) { throw exception::eval_error("Reserved word used as variable '" + idname + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); } return t_ss.get_object(idname); } @@ -651,6 +653,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &e) { throw exception::eval_error("Reserved word used as function name '" + e.word() + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Function redefined '" + e.name() + "'"); } return Boxed_Value(); } @@ -981,6 +985,7 @@ namespace chaiscript end_point = this->children.size() - 1; } for (unsigned int i = 1; i < end_point; ++i) { + chaiscript::eval::detail::Scope_Push_Pop catchscope(t_ss); AST_NodePtr catch_block = this->children[i]; if (catch_block->children.size() == 1) { @@ -1023,6 +1028,7 @@ namespace chaiscript } catch (Boxed_Value &except) { for (size_t i = 1; i < this->children.size(); ++i) { + chaiscript::eval::detail::Scope_Push_Pop catchscope(t_ss); AST_NodePtr catch_block = this->children[i]; if (catch_block->children.size() == 1) { @@ -1037,7 +1043,7 @@ namespace chaiscript break; } else if (catch_block->children.size() == 3) { - //Variable capture, no guards + //Variable capture, guards t_ss.add_object(catch_block->children[0]->text, except); bool guard; @@ -1169,6 +1175,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &e) { throw exception::eval_error("Reserved word used as method name '" + e.word() + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Method redefined '" + e.name() + "'"); } return Boxed_Value(); } @@ -1196,6 +1204,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &) { throw exception::eval_error("Reserved word used as attribute '" + this->children[1]->text + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Attribute redefined '" + e.name() + "'"); } return Boxed_Value(); } diff --git a/unittests/function_ordering_test.cpp b/unittests/function_ordering_test.cpp index 92907b0..125c2a1 100644 --- a/unittests/function_ordering_test.cpp +++ b/unittests/function_ordering_test.cpp @@ -18,7 +18,7 @@ int main() chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); chai.eval("def test_fun(x) { return 3; }"); chai.eval("def test_fun(x) : x == \"hi\" { return 4; }"); - chai.eval("def test_fun(x) { return 5; }"); +// chai.eval("def test_fun(x) { return 5; }"); chai.add(chaiscript::fun(&test_one), "test_fun"); chai.add(chaiscript::fun(&test_two), "test_fun"); diff --git a/unittests/function_redefinition.chai b/unittests/function_redefinition.chai new file mode 100644 index 0000000..69ae19e --- /dev/null +++ b/unittests/function_redefinition.chai @@ -0,0 +1,2 @@ +assert_throws("Function already defined", [](){ def foo(x) { x + 1 }; def foo(x) { x + 1 } } ); + diff --git a/unittests/object_lifetime_test.cpp b/unittests/object_lifetime_test.cpp index 80bc52b..6d24f9c 100644 --- a/unittests/object_lifetime_test.cpp +++ b/unittests/object_lifetime_test.cpp @@ -39,19 +39,20 @@ int main() chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); chai.add(m); - chai.add(chaiscript::fun(&Test::count), "count"); +// chai.add(chaiscript::fun(&Test::count), "count"); int count = chai.eval("count()"); int count2 = chai.eval("auto i = 0; { auto t = Test(); } return i;"); - int count3 = chai.eval("auto i = 0; { auto t = Test(); i = count(); } return i;"); + int count3 = chai.eval("i = 0; { auto t = Test(); i = count(); } return i;"); - int count4 = chai.eval("auto i = 0; { auto t = Test(); { auto t2 = Test(); i = count(); } } return i;"); + int count4 = chai.eval("i = 0; { auto t = Test(); { auto t2 = Test(); i = count(); } } return i;"); - int count5 = chai.eval("auto i = 0; { auto t = Test(); { auto t2 = Test(); } i = count(); } return i;"); + int count5 = chai.eval("i = 0; { auto t = Test(); { auto t2 = Test(); } i = count(); } return i;"); + + int count6 = chai.eval("i = 0; { auto t = Test(); { auto t2 = Test(); } } i = count(); return i;"); - int count6 = chai.eval("auto i = 0; { auto t = Test(); { auto t2 = Test(); } } i = count(); return i;"); if (count == 0 && count2 == 0 diff --git a/unittests/utility_test.cpp b/unittests/utility_test.cpp index 0145265..b611fd6 100644 --- a/unittests/utility_test.cpp +++ b/unittests/utility_test.cpp @@ -37,8 +37,8 @@ int main() chaiscript::ChaiScript chai(chaiscript::Std_Lib::library());; chai.add(m); if (chai.eval("auto t = Test(); t.function2(); ") == "Function2" - && chai.eval("auto t = Test(); t.functionOverload(1); ") == "int" - && chai.eval("auto t = Test(); t.functionOverload(1.1); ") == "double") + && chai.eval("auto t2 = Test(); t2.functionOverload(1); ") == "int" + && chai.eval("auto t3 = Test(); t3.functionOverload(1.1); ") == "double") { chai.eval("t = Test();"); return EXIT_SUCCESS; diff --git a/unittests/variable_redefinition.chai b/unittests/variable_redefinition.chai new file mode 100644 index 0000000..dc670e4 --- /dev/null +++ b/unittests/variable_redefinition.chai @@ -0,0 +1,2 @@ +assert_throws("Variable already defined", []() { auto y = 10; auto y = 20; }) +