From e21c8f87b49688e33bd29eaa1177e1b3a82829f5 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 28 Aug 2015 10:33:26 -0600 Subject: [PATCH 01/45] Add profile test for cpp call perf --- contrib/codeanalysis/profile_cpp_calls.chai | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 contrib/codeanalysis/profile_cpp_calls.chai diff --git a/contrib/codeanalysis/profile_cpp_calls.chai b/contrib/codeanalysis/profile_cpp_calls.chai new file mode 100644 index 0000000..8b7d484 --- /dev/null +++ b/contrib/codeanalysis/profile_cpp_calls.chai @@ -0,0 +1,21 @@ + +var test_str = "bob was a string"; + +for( var i = 0; i < 200000; ++i) +{ + test_str.size(); +// test_str.find("a", i); + test_str.c_str(); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str = "bob was a string"; +} From 9f362608b7324533d3e0de575f9c03b60e70de5a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 28 Aug 2015 21:19:00 -0600 Subject: [PATCH 02/45] Eliminate extra unneeded scope --- contrib/codeanalysis/profile_cpp_calls.chai | 15 ++++++++++----- include/chaiscript/language/chaiscript_eval.hpp | 5 +++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/contrib/codeanalysis/profile_cpp_calls.chai b/contrib/codeanalysis/profile_cpp_calls.chai index 8b7d484..011f9a1 100644 --- a/contrib/codeanalysis/profile_cpp_calls.chai +++ b/contrib/codeanalysis/profile_cpp_calls.chai @@ -12,10 +12,15 @@ for( var i = 0; i < 200000; ++i) test_str.erase_at(1); test_str.erase_at(1); test_str.erase_at(1); - test_str.erase_at(1); - test_str.erase_at(1); - test_str.erase_at(1); - test_str.erase_at(1); - test_str.erase_at(1); + + size(test_str); +// test_str.find("a", i); + c_str(test_str); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); test_str = "bob was a string"; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 53d8e41..6f28008 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -60,7 +60,6 @@ namespace chaiscript chaiscript::eval::detail::Stack_Push_Pop tpp(state); if (thisobj) state.add_object("this", *thisobj); - chaiscript::eval::detail::Scope_Push_Pop spp(state); if (t_locals) { for (const auto &local : *t_locals) { @@ -69,7 +68,9 @@ namespace chaiscript } for (size_t i = 0; i < t_param_names.size(); ++i) { - state.add_object(t_param_names[i], t_vals[i]); + if (t_param_names[i] != "this") { + state.add_object(t_param_names[i], t_vals[i]); + } } try { From 15eb78bd8f9fd85b235506412c17975d73bccbff Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 31 Aug 2015 08:41:47 -0600 Subject: [PATCH 03/45] Move to indexed function storage --- .../chaiscript/dispatchkit/dispatchkit.hpp | 141 +++++++++++------- 1 file changed, 87 insertions(+), 54 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 4557870..3ded2df 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -417,9 +417,9 @@ namespace chaiscript struct State { - std::map > m_functions; - std::map m_function_objects; - std::map m_boxed_functions; + std::vector>>> m_functions; + std::vector> m_function_objects; + std::vector> m_boxed_functions; std::map m_global_objects; Type_Name_Map m_types; std::set m_reserved_words; @@ -518,7 +518,7 @@ namespace chaiscript throw chaiscript::exception::global_non_const(); } - chaiscript::detail::threading::unique_lock l(m_global_object_mutex); + chaiscript::detail::threading::unique_lock l(m_mutex); if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) { @@ -533,7 +533,7 @@ namespace chaiscript { validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_global_object_mutex); + chaiscript::detail::threading::unique_lock l(m_mutex); const auto itr = m_state.m_global_objects.find(name); if (itr == m_state.m_global_objects.end()) @@ -551,7 +551,7 @@ namespace chaiscript { validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_global_object_mutex); + chaiscript::detail::threading::unique_lock l(m_mutex); if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) { @@ -648,19 +648,18 @@ namespace chaiscript return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)][loc & static_cast(Loc::loc_mask)].second; } - // Is the value we are looking for a global? - { - chaiscript::detail::threading::shared_lock l(m_global_object_mutex); + // Is the value we are looking for a global or function? + chaiscript::detail::threading::shared_lock l(m_mutex); - const auto itr = m_state.m_global_objects.find(name); - if (itr != m_state.m_global_objects.end()) - { - return itr->second; - } + const auto itr = m_state.m_global_objects.find(name); + if (itr != m_state.m_global_objects.end()) + { + return itr->second; } - // If all that failed, then check to see if it's a function - return get_function_object(name); + // no? is it a function object? + return get_function_object_int(name); + } /// Registers a new named type @@ -719,19 +718,19 @@ namespace chaiscript } /// Return a function by name - std::vector< Proxy_Function > get_function(const std::string &t_name) const + std::shared_ptr> get_function(const std::string &t_name) const { chaiscript::detail::threading::shared_lock l(m_mutex); const auto &funs = get_functions_int(); - auto itr = funs.find(t_name); + auto itr = find_keyed_value(funs, t_name); if (itr != funs.end()) { return itr->second; } else { - return std::vector(); + return std::make_shared>(); } } @@ -739,12 +738,19 @@ namespace chaiscript /// \throws std::range_error if it does not Boxed_Value get_function_object(const std::string &t_name) const { -// std::cout << "Getting function object: " << t_name << '\n'; chaiscript::detail::threading::shared_lock l(m_mutex); + return get_function_object_int(t_name); + } + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + /// \warn does not obtain a mutex lock. \sa get_function_object for public version + Boxed_Value get_function_object_int(const std::string &t_name) const + { const auto &funs = get_boxed_functions_int(); - auto itr = funs.find(t_name); + auto itr = find_keyed_value(funs, t_name); if (itr != funs.end()) { @@ -754,13 +760,14 @@ namespace chaiscript } } + /// Return true if a function exists bool function_exists(const std::string &name) const { chaiscript::detail::threading::shared_lock l(m_mutex); const auto &functions = get_functions_int(); - return functions.find(name) != functions.end(); + return find_keyed_value(functions, name) != functions.end(); } /// \returns All values in the local thread state in the parent scope, or if it doesn't exist, @@ -817,11 +824,8 @@ namespace chaiscript } // add the global values - { - chaiscript::detail::threading::shared_lock l(m_global_object_mutex); - - retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); - } + chaiscript::detail::threading::shared_lock l(m_mutex); + retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); return retval; } @@ -858,7 +862,7 @@ namespace chaiscript for (const auto & function : functions) { - for (const auto & internal_func : function.second) + for (const auto & internal_func : *function.second) { rets.emplace_back(function.first, internal_func); } @@ -941,14 +945,14 @@ namespace chaiscript } }; - if (is_attribute_call(funs, params, t_has_params)) { - return do_attribute_call(1, params, funs, m_conversions); + if (is_attribute_call(*funs, params, t_has_params)) { + return do_attribute_call(1, params, *funs, m_conversions); } else { std::exception_ptr except; - if (!funs.empty()) { + if (!funs->empty()) { try { - return dispatch::dispatch(funs, params, m_conversions); + return dispatch::dispatch(*funs, params, m_conversions); } catch(chaiscript::exception::dispatch_error&) { except = std::current_exception(); } @@ -960,7 +964,8 @@ namespace chaiscript const auto functions = [&]()->std::vector { std::vector fs; - for (const auto &f : get_function("method_missing")) + const auto method_missing_funs = get_function("method_missing"); + for (const auto &f : *method_missing_funs) { if(f->compare_first_type(params[0], m_conversions)) { fs.push_back(f); @@ -996,7 +1001,7 @@ namespace chaiscript if (except) { std::rethrow_exception(except); } else { - throw chaiscript::exception::dispatch_error(params, std::vector(funs.begin(), funs.end())); + throw chaiscript::exception::dispatch_error(params, std::vector(funs->begin(), funs->end())); } } } @@ -1008,7 +1013,8 @@ namespace chaiscript Boxed_Value call_function(const std::string &t_name, const std::vector ¶ms) const { - Boxed_Value bv = dispatch::dispatch(get_function(t_name), params, m_conversions); + const auto funs = get_function(t_name); + Boxed_Value bv = dispatch::dispatch(*funs, params, m_conversions); // the result of a clone is never to be marked as a return_value if (t_name == "clone") { bv.reset_return_value(); @@ -1140,7 +1146,6 @@ namespace chaiscript State get_state() const { chaiscript::detail::threading::shared_lock l(m_mutex); - chaiscript::detail::threading::shared_lock l2(m_global_object_mutex); return m_state; } @@ -1148,7 +1153,6 @@ namespace chaiscript void set_state(const State &t_state) { chaiscript::detail::threading::unique_lock l(m_mutex); - chaiscript::detail::threading::unique_lock l2(m_global_object_mutex); m_state = t_state; } @@ -1245,32 +1249,32 @@ namespace chaiscript private: - const std::map &get_boxed_functions_int() const + const std::vector> &get_boxed_functions_int() const { return m_state.m_boxed_functions; } - std::map &get_boxed_functions_int() + std::vector> &get_boxed_functions_int() { return m_state.m_boxed_functions; } - const std::map &get_function_objects_int() const + const std::vector> &get_function_objects_int() const { return m_state.m_function_objects; } - std::map &get_function_objects_int() + std::vector> &get_function_objects_int() { return m_state.m_function_objects; } - const std::map > &get_functions_int() const + const std::vector>>> &get_functions_int() const { return m_state.m_functions; } - std::map > &get_functions_int() + std::vector>>> &get_functions_int() { return m_state.m_functions; } @@ -1384,6 +1388,38 @@ namespace chaiscript } } + template + static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value) + { + auto itr = find_keyed_value(t_c, t_key); + + if (itr == t_c.end()) { + t_c.emplace_back(t_key, std::forward(t_value)); + } else { + typedef typename Container::value_type value_type; + *itr = value_type(t_key, std::forward(t_value)); + } + } + + template + static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key) + { + return std::find_if(t_c.begin(), t_c.end(), + [&t_key](const typename Container::value_type &o) { + return o.first == t_key; + }); + } + + template + static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key) + { + return std::find_if(t_c.begin(), t_c.end(), + [&t_key](const typename Container::value_type &o) { + return o.first == t_key; + }); + } + + /// Implementation detail for adding a function. /// \throws exception::name_conflict_error if there's a function matching the given one being added void add_function(const Proxy_Function &t_f, const std::string &t_name) @@ -1392,16 +1428,13 @@ namespace chaiscript auto &funcs = get_functions_int(); - auto itr = funcs.find(t_name); - - auto &func_objs = get_function_objects_int(); - auto &boxed_funcs = get_boxed_functions_int(); + auto itr = find_keyed_value(funcs, t_name); Proxy_Function new_func = [&]() -> Proxy_Function { if (itr != funcs.end()) { - auto &vec = itr->second; + auto vec = *itr->second; for (const auto &func : vec) { if ((*t_f) == *(func)) @@ -1412,26 +1445,26 @@ namespace chaiscript vec.push_back(t_f); std::stable_sort(vec.begin(), vec.end(), &function_less_than); - return std::make_shared(vec); + itr->second = std::make_shared>(vec); + return std::make_shared(std::move(vec)); } else if (t_f->has_arithmetic_param()) { // if the function is the only function but it also contains // arithmetic operators, we must wrap it in a dispatch function // to allow for automatic arithmetic type conversions std::vector vec({t_f}); - funcs.insert(std::make_pair(t_name, vec)); + funcs.emplace_back(t_name, std::make_shared>(vec)); return std::make_shared(std::move(vec)); } else { - funcs.insert(std::make_pair(t_name, std::vector{t_f})); + funcs.emplace_back(t_name, std::make_shared>(std::initializer_list({t_f}))); return t_f; } }(); - boxed_funcs[t_name] = const_var(new_func); - func_objs[t_name] = std::move(new_func); + add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func)); + add_keyed_value(get_function_objects_int(), t_name, std::move(new_func)); } mutable chaiscript::detail::threading::shared_mutex m_mutex; - mutable chaiscript::detail::threading::shared_mutex m_global_object_mutex; Type_Conversions m_conversions; From f06e5cdcd6b044dc8b7e0571ff3d58c278abfc4c Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 31 Aug 2015 09:44:47 -0600 Subject: [PATCH 04/45] Cache function lookups --- .../chaiscript/dispatchkit/dispatchkit.hpp | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 3ded2df..14571cf 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -658,7 +658,10 @@ namespace chaiscript } // no? is it a function object? - return get_function_object_int(name); + auto obj = get_function_object_int(name, loc); + if (obj.first != loc) t_loc.store(obj.first); + return obj.second; + } @@ -740,21 +743,21 @@ namespace chaiscript { chaiscript::detail::threading::shared_lock l(m_mutex); - return get_function_object_int(t_name); + return get_function_object_int(t_name, 0).second; } /// \returns a function object (Boxed_Value wrapper) if it exists /// \throws std::range_error if it does not /// \warn does not obtain a mutex lock. \sa get_function_object for public version - Boxed_Value get_function_object_int(const std::string &t_name) const + std::pair get_function_object_int(const std::string &t_name, const size_t t_hint) const { const auto &funs = get_boxed_functions_int(); - auto itr = find_keyed_value(funs, t_name); + auto itr = find_keyed_value(funs, t_name, t_hint); if (itr != funs.end()) { - return itr->second; + return std::make_pair(std::distance(funs.begin(), itr), itr->second); } else { throw std::range_error("Object not found: " + t_name); } @@ -1419,6 +1422,16 @@ namespace chaiscript }); } + template + static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint) + { + if (t_c.size() > t_hint && t_c[t_hint].first == t_key) { + return t_c.begin() + t_hint; + } else { + return find_keyed_value(t_c, t_key); + } + } + /// Implementation detail for adding a function. /// \throws exception::name_conflict_error if there's a function matching the given one being added From 52e11bf0015b5835dde38a6863702b06364c53a6 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 31 Aug 2015 11:00:56 -0600 Subject: [PATCH 05/45] Fun location caching phase2 This shows ~25% performance over develop --- .../chaiscript/dispatchkit/dispatchkit.hpp | 61 ++++++++++--------- .../chaiscript/language/chaiscript_eval.hpp | 39 ++++++++---- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 14571cf..b1cef9a 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -659,7 +659,7 @@ namespace chaiscript // no? is it a function object? auto obj = get_function_object_int(name, loc); - if (obj.first != loc) t_loc.store(obj.first); + if (obj.first != loc) t_loc.store(obj.first, std::memory_order_relaxed); return obj.second; @@ -720,20 +720,29 @@ namespace chaiscript return std::vector >(m_state.m_types.begin(), m_state.m_types.end()); } + std::shared_ptr> get_method_missing_functions() const + { + uint_fast32_t method_missing_loc = m_method_missing_loc.load(std::memory_order_relaxed); + auto method_missing_funs = get_function("method_missing", method_missing_loc); + if (method_missing_funs.first != method_missing_loc) m_method_missing_loc.store(method_missing_funs.first, std::memory_order_relaxed); + return std::move(method_missing_funs.second); + } + + /// Return a function by name - std::shared_ptr> get_function(const std::string &t_name) const + std::pair>> get_function(const std::string &t_name, const size_t t_hint) const { chaiscript::detail::threading::shared_lock l(m_mutex); const auto &funs = get_functions_int(); - auto itr = find_keyed_value(funs, t_name); + auto itr = find_keyed_value(funs, t_name, t_hint); if (itr != funs.end()) { - return itr->second; + return std::make_pair(std::distance(funs.begin(), itr), itr->second); } else { - return std::make_shared>(); + return std::make_pair(size_t(0), std::make_shared>()); } } @@ -910,9 +919,11 @@ namespace chaiscript #pragma warning(push) #pragma warning(disable : 4715) #endif - Boxed_Value call_member(const std::string &t_name, const std::vector ¶ms, bool t_has_params) + Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, bool t_has_params) { - const auto funs = get_function(t_name); + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + const auto funs = get_function(t_name, loc); + if (funs.first != loc) t_loc.store(funs.first, std::memory_order_relaxed); const auto do_attribute_call = [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions &l_conversions)->Boxed_Value @@ -948,14 +959,14 @@ namespace chaiscript } }; - if (is_attribute_call(*funs, params, t_has_params)) { - return do_attribute_call(1, params, *funs, m_conversions); + if (is_attribute_call(*funs.second, params, t_has_params)) { + return do_attribute_call(1, params, *funs.second, m_conversions); } else { std::exception_ptr except; - if (!funs->empty()) { + if (!funs.second->empty()) { try { - return dispatch::dispatch(*funs, params, m_conversions); + return dispatch::dispatch(*funs.second, params, m_conversions); } catch(chaiscript::exception::dispatch_error&) { except = std::current_exception(); } @@ -967,7 +978,8 @@ namespace chaiscript const auto functions = [&]()->std::vector { std::vector fs; - const auto method_missing_funs = get_function("method_missing"); + const auto method_missing_funs = get_method_missing_functions(); + for (const auto &f : *method_missing_funs) { if(f->compare_first_type(params[0], m_conversions)) { @@ -1004,7 +1016,7 @@ namespace chaiscript if (except) { std::rethrow_exception(except); } else { - throw chaiscript::exception::dispatch_error(params, std::vector(funs->begin(), funs->end())); + throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end())); } } } @@ -1014,10 +1026,12 @@ namespace chaiscript - Boxed_Value call_function(const std::string &t_name, const std::vector ¶ms) const + Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms) const { - const auto funs = get_function(t_name); - Boxed_Value bv = dispatch::dispatch(*funs, params, m_conversions); + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + const auto funs = get_function(t_name, loc); + if (funs.first != loc) t_loc.store(funs.first, std::memory_order_relaxed); + Boxed_Value bv = dispatch::dispatch(*funs.second, params, m_conversions); // the result of a clone is never to be marked as a return_value if (t_name == "clone") { bv.reset_return_value(); @@ -1025,20 +1039,6 @@ namespace chaiscript return bv; } - Boxed_Value call_function(const std::string &t_name) const - { - return call_function(t_name, std::vector()); - } - - Boxed_Value call_function(const std::string &t_name, Boxed_Value p1) const - { - return call_function(t_name, std::vector({std::move(p1)})); - } - - Boxed_Value call_function(const std::string &t_name, Boxed_Value p1, Boxed_Value p2) const - { - return call_function(t_name, std::vector({std::move(p1), std::move(p2)})); - } /// Dump object info to stdout void dump_object(const Boxed_Value &o) const @@ -1483,6 +1483,7 @@ namespace chaiscript Type_Conversions m_conversions; chaiscript::detail::threading::Thread_Storage m_stack_holder; + mutable std::atomic_uint_fast32_t m_method_missing_loc; State m_state; }; diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 6f28008..f6fde40 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -118,7 +118,7 @@ namespace chaiscript } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); fpp.save_params({t_lhs, t_rhs}); - return t_ss->call_function(t_oper_string, t_lhs, t_rhs); + return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs}); } } catch(const exception::dispatch_error &e){ @@ -128,6 +128,7 @@ namespace chaiscript private: Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; }; struct Int_AST_Node : public AST_Node { @@ -396,6 +397,8 @@ namespace chaiscript { assert(children.size() == 3); } Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_clone_loc; virtual ~Equation_AST_Node() {} virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { @@ -431,14 +434,14 @@ namespace chaiscript } else { if (!rhs.is_return_value()) { - rhs = t_ss->call_function("clone", rhs); + rhs = t_ss->call_function("clone", m_clone_loc, {rhs}); } rhs.reset_return_value(); } } try { - return t_ss->call_function(this->children[1]->text, std::move(lhs), rhs); + return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs}); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss); @@ -458,7 +461,7 @@ namespace chaiscript } else { try { - return t_ss->call_function(this->children[1]->text, std::move(lhs), rhs); + return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs}); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss); } @@ -541,7 +544,7 @@ namespace chaiscript try { fpp.save_params(params); - return t_ss->call_function("[]", params); + return t_ss->call_function("[]", m_loc, params); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss ); @@ -563,6 +566,8 @@ namespace chaiscript return oss.str(); } + + mutable std::atomic_uint_fast32_t m_loc; }; struct Dot_Access_AST_Node : public AST_Node { @@ -592,7 +597,7 @@ namespace chaiscript fpp.save_params(params); try { - retval = t_ss->call_member(m_fun_name, std::move(params), has_function_params); + retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params); } catch(const exception::dispatch_error &e){ if (e.functions.empty()) @@ -608,7 +613,7 @@ namespace chaiscript if (this->children[2]->identifier == AST_Node_Type::Array_Call) { try { - retval = t_ss->call_function("[]", retval, this->children[2]->children[1]->eval(t_ss)); + retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[2]->children[1]->eval(t_ss)}); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss); @@ -619,6 +624,8 @@ namespace chaiscript } private: + mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_array_loc; std::string m_fun_name; }; @@ -932,7 +939,7 @@ namespace chaiscript if (this->children[currentCase]->identifier == AST_Node_Type::Case) { //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here. try { - if (hasMatched || boxed_cast(t_ss->call_function("==", match_value, this->children[currentCase]->children[0]->eval(t_ss)))) { + if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}))) { this->children[currentCase]->eval(t_ss); hasMatched = true; } @@ -953,6 +960,8 @@ namespace chaiscript } return Boxed_Value(); } + + mutable std::atomic_uint_fast32_t m_loc; }; struct Case_AST_Node : public AST_Node { @@ -999,7 +1008,7 @@ namespace chaiscript for (const auto &child : children[0]->children) { auto obj = child->eval(t_ss); if (!obj.is_return_value()) { - vec.push_back(t_ss->call_function("clone", obj)); + vec.push_back(t_ss->call_function("clone", m_loc, {obj})); } else { vec.push_back(std::move(obj)); } @@ -1016,6 +1025,8 @@ namespace chaiscript { return "[" + AST_Node::pretty_print() + "]"; } + + mutable std::atomic_uint_fast32_t m_loc; }; struct Inline_Map_AST_Node : public AST_Node { @@ -1030,7 +1041,7 @@ namespace chaiscript for (const auto &child : children[0]->children) { auto obj = child->children[1]->eval(t_ss); if (!obj.is_return_value()) { - obj = t_ss->call_function("clone", obj); + obj = t_ss->call_function("clone", m_loc, {obj}); } retval[t_ss->boxed_cast(child->children[0]->eval(t_ss))] = std::move(obj); @@ -1043,6 +1054,7 @@ namespace chaiscript } } + mutable std::atomic_uint_fast32_t m_loc; }; struct Return_AST_Node : public AST_Node { @@ -1125,7 +1137,7 @@ namespace chaiscript } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); fpp.save_params({bv}); - return t_ss->call_function(children[0]->text, std::move(bv)); + return t_ss->call_function(children[0]->text, m_loc, {std::move(bv)}); } } catch (const exception::dispatch_error &e) { throw exception::eval_error("Error with prefix operator evaluation: '" + children[0]->text + "'", e.parameters, e.functions, false, *t_ss); @@ -1134,6 +1146,7 @@ namespace chaiscript private: Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; }; struct Break_AST_Node : public AST_Node { @@ -1196,14 +1209,14 @@ namespace chaiscript try { auto oper1 = children[0]->children[0]->children[0]->eval(t_ss); auto oper2 = children[0]->children[0]->children[1]->eval(t_ss); - return t_ss->call_function("generate_range", - oper1, oper2); + return t_ss->call_function("generate_range", m_loc, {oper1, oper2}); } catch (const exception::dispatch_error &e) { throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss); } } + mutable std::atomic_uint_fast32_t m_loc; }; struct Annotation_AST_Node : public AST_Node { From f3dbb7ed8738b592e64f8c4d64fd49a3305e450f Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 31 Aug 2015 11:09:03 -0600 Subject: [PATCH 06/45] Control how fast global vectors grow --- include/chaiscript/dispatchkit/dispatchkit.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index b1cef9a..4798876 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1397,6 +1397,7 @@ namespace chaiscript auto itr = find_keyed_value(t_c, t_key); if (itr == t_c.end()) { + t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here t_c.emplace_back(t_key, std::forward(t_value)); } else { typedef typename Container::value_type value_type; @@ -1456,6 +1457,7 @@ namespace chaiscript } } + vec.reserve(vec.size() + 1); // tightly control vec growth vec.push_back(t_f); std::stable_sort(vec.begin(), vec.end(), &function_less_than); itr->second = std::make_shared>(vec); From e1a80fb5ceec23af39cdee27c568469a2579175d Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 16 Sep 2015 10:28:05 -0600 Subject: [PATCH 07/45] A couple of MSVC fixes --- include/chaiscript/chaiscript_defines.hpp | 2 +- include/chaiscript/dispatchkit/dispatchkit.hpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index c28e9a5..5c54051 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -52,7 +52,7 @@ #define CHAISCRIPT_MODULE_EXPORT extern "C" #endif -#ifdef CHAISCRIPT_MSVC +#ifdef CHAISCRIPT_MSVC_12 #define CHAISCRIPT_NOEXCEPT throw() #define CHAISCRIPT_CONSTEXPR #else diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 4798876..de2caa0 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -659,7 +659,7 @@ namespace chaiscript // no? is it a function object? auto obj = get_function_object_int(name, loc); - if (obj.first != loc) t_loc.store(obj.first, std::memory_order_relaxed); + if (obj.first != loc) t_loc.store(uint_fast32_t(obj.first), std::memory_order_relaxed); return obj.second; @@ -724,7 +724,7 @@ namespace chaiscript { uint_fast32_t method_missing_loc = m_method_missing_loc.load(std::memory_order_relaxed); auto method_missing_funs = get_function("method_missing", method_missing_loc); - if (method_missing_funs.first != method_missing_loc) m_method_missing_loc.store(method_missing_funs.first, std::memory_order_relaxed); + if (method_missing_funs.first != method_missing_loc) m_method_missing_loc.store(uint_fast32_t(method_missing_funs.first), std::memory_order_relaxed); return std::move(method_missing_funs.second); } @@ -923,7 +923,7 @@ namespace chaiscript { uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); const auto funs = get_function(t_name, loc); - if (funs.first != loc) t_loc.store(funs.first, std::memory_order_relaxed); + if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); const auto do_attribute_call = [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions &l_conversions)->Boxed_Value @@ -1030,7 +1030,7 @@ namespace chaiscript { uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); const auto funs = get_function(t_name, loc); - if (funs.first != loc) t_loc.store(funs.first, std::memory_order_relaxed); + if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); Boxed_Value bv = dispatch::dispatch(*funs.second, params, m_conversions); // the result of a clone is never to be marked as a return_value if (t_name == "clone") { From f9f1d5807a296f723bdd8b01592d54872bb85be7 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sun, 20 Sep 2015 15:35:53 -0600 Subject: [PATCH 08/45] Basic support for parsing of JSON objects --- include/chaiscript/chaiscript_stdlib.hpp | 3 + .../chaiscript/dispatchkit/dynamic_object.hpp | 10 + include/chaiscript/utility/json.hpp | 652 ++++++++++++++++++ include/chaiscript/utility/json_wrap.hpp | 74 ++ 4 files changed, 739 insertions(+) create mode 100644 include/chaiscript/utility/json.hpp create mode 100644 include/chaiscript/utility/json_wrap.hpp diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index c07e64c..4b38f2c 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -19,6 +19,7 @@ #include "dispatchkit/bootstrap_stl.hpp" #include "dispatchkit/boxed_value.hpp" #include "language/chaiscript_prelude.chai" +#include "utility/json_wrap.hpp" #ifndef CHAISCRIPT_NO_THREADS #include @@ -51,6 +52,8 @@ namespace chaiscript lib->add(chaiscript::fun([](const std::function &t_func){ return std::async(std::launch::async, t_func);}), "async"); #endif + lib->add(json_wrap::library()); + lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ ); return lib; diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 5a9ccaa..9ea827c 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -41,6 +41,16 @@ namespace chaiscript return m_type_name; } + const Boxed_Value &operator[](const std::string &t_attr_name) const + { + return get_attr(t_attr_name); + } + + Boxed_Value &operator[](const std::string &t_attr_name) + { + return get_attr(t_attr_name); + } + const Boxed_Value &get_attr(const std::string &t_attr_name) const { auto a = m_attrs.find(t_attr_name); diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp new file mode 100644 index 0000000..dbdf886 --- /dev/null +++ b/include/chaiscript/utility/json.hpp @@ -0,0 +1,652 @@ +// From github.com/nbsdx/SimpleJSON. +// Released under the DWTFYW PL +// + + +#pragma once + +#ifndef SIMPLEJSON_HPP +#define SIMPLEJSON_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace json { + +using std::map; +using std::deque; +using std::string; +using std::enable_if; +using std::initializer_list; +using std::is_same; +using std::is_convertible; +using std::is_integral; +using std::is_floating_point; + +namespace { + string json_escape( const string &str ) { + string output; + for( unsigned i = 0; i < str.length(); ++i ) + switch( str[i] ) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default : output += str[i]; break; + } + return output; + } +} + +class JSON +{ + union BackingData { + BackingData( double d ) : Float( d ){} + BackingData( long l ) : Int( l ){} + BackingData( bool b ) : Bool( b ){} + BackingData( string s ) : String( new string( s ) ){} + BackingData() : Int( 0 ){} + + deque *List; + map *Map; + string *String; + double Float; + long Int; + bool Bool; + } Internal; + + public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template + class JSONWrapper { + Container *object; + + public: + JSONWrapper( Container *val ) : object( val ) {} + JSONWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template + class JSONConstWrapper { + const Container *object; + + public: + JSONConstWrapper( const Container *val ) : object( val ) {} + JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON() : Internal(), Type( Class::Null ){} + + explicit JSON(Class type) + : JSON() + { + SetType( type ); + } + + JSON( initializer_list list ) + : JSON() + { + SetType( Class::Object ); + for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) + operator[]( i->ToString() ) = *std::next( i ); + } + + JSON( JSON&& other ) + : Internal( other.Internal ) + , Type( other.Type ) + { other.Type = Class::Null; other.Internal.Map = nullptr; } + + JSON& operator=( JSON&& other ) { + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; + } + + JSON( const JSON &other ) { + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + } + + JSON& operator=( const JSON &other ) { + if (&other == this) return *this; + + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; + } + + ~JSON() { + switch( Type ) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + template + JSON( T b, typename enable_if::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){} + + template + JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( (long)i ), Type( Class::Integral ){} + + template + JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( (double)f ), Type( Class::Floating ){} + + template + JSON( T s, typename enable_if::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){} + + JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} + + static JSON Make( Class type ) { + return JSON(type); + } + + static JSON Load( const string & ); + + template + void append( T arg ) { + SetType( Class::Array ); Internal.List->emplace_back( arg ); + } + + template + void append( T arg, U... args ) { + append( arg ); append( args... ); + } + + template + typename enable_if::value, JSON&>::type operator=( T b ) { + SetType( Class::Boolean ); Internal.Bool = b; return *this; + } + + template + typename enable_if::value && !is_same::value, JSON&>::type operator=( T i ) { + SetType( Class::Integral ); Internal.Int = i; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T f ) { + SetType( Class::Floating ); Internal.Float = f; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T s ) { + SetType( Class::String ); *Internal.String = string( s ); return *this; + } + + JSON& operator[]( const string &key ) { + SetType( Class::Object ); return Internal.Map->operator[]( key ); + } + + JSON& operator[]( unsigned index ) { + SetType( Class::Array ); + if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); + return Internal.List->operator[]( index ); + } + + JSON &at( const string &key ) { + return operator[]( key ); + } + + const JSON &at( const string &key ) const { + return Internal.Map->at( key ); + } + + JSON &at( unsigned index ) { + return operator[]( index ); + } + + const JSON &at( unsigned index ) const { + return Internal.List->at( index ); + } + + int length() const { + if( Type == Class::Array ) + return static_cast(Internal.List->size()); + else + return -1; + } + + bool hasKey( const string &key ) const { + if( Type == Class::Object ) + return Internal.Map->find( key ) != Internal.Map->end(); + return false; + } + + int size() const { + if( Type == Class::Object ) + return static_cast(Internal.Map->size()); + else if( Type == Class::Array ) + return static_cast(Internal.List->size()); + else + return -1; + } + + Class JSONType() const { return Type; } + + /// Functions for getting primitives from the JSON object. + bool IsNull() const { return Type == Class::Null; } + + string ToString() const { bool b; return ToString( b ); } + string ToString( bool &ok ) const { + ok = (Type == Class::String); + return ok ? std::move( json_escape( *Internal.String ) ): string(""); + } + + double ToFloat() const { bool b; return ToFloat( b ); } + double ToFloat( bool &ok ) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; + } + + long ToInt() const { bool b; return ToInt( b ); } + long ToInt( bool &ok ) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; + } + + bool ToBool() const { bool b; return ToBool( b ); } + bool ToBool( bool &ok ) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; + } + + JSONWrapper> ObjectRange() { + if( Type == Class::Object ) + return JSONWrapper>( Internal.Map ); + return JSONWrapper>( nullptr ); + } + + JSONWrapper> ArrayRange() { + if( Type == Class::Array ) + return JSONWrapper>( Internal.List ); + return JSONWrapper>( nullptr ); + } + + JSONConstWrapper> ObjectRange() const { + if( Type == Class::Object ) + return JSONConstWrapper>( Internal.Map ); + return JSONConstWrapper>( nullptr ); + } + + + JSONConstWrapper> ArrayRange() const { + if( Type == Class::Array ) + return JSONConstWrapper>( Internal.List ); + return JSONConstWrapper>( nullptr ); + } + + string dump( int depth = 1, string tab = " ") const { + switch( Type ) { + case Class::Null: + return "null"; + case Class::Object: { + string pad = ""; + for( int i = 0; i < depth; ++i, pad += tab ); + + string s = "{\n"; + bool skip = true; + for( auto &p : *Internal.Map ) { + if( !skip ) s += ",\n"; + s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + skip = false; + } + s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; + return s; + } + case Class::Array: { + string s = "["; + bool skip = true; + for( auto &p : *Internal.List ) { + if( !skip ) s += ", "; + s += p.dump( depth + 1, tab ); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape( *Internal.String ) + "\""; + case Class::Floating: + return std::to_string( Internal.Float ); + case Class::Integral: + return std::to_string( Internal.Int ); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + default: + return ""; + } + } + + friend std::ostream& operator<<( std::ostream&, const JSON & ); + + private: + void SetType( Class type ) { + if( type == Type ) + return; + + switch( Type ) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } + + switch( type ) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new map(); break; + case Class::Array: Internal.List = new deque(); break; + case Class::String: Internal.String = new string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; + } + + private: + + Class Type = Class::Null; +}; + +JSON Array() { + return JSON::Make( JSON::Class::Array ); +} + +template +JSON Array( T... args ) { + JSON arr = JSON::Make( JSON::Class::Array ); + arr.append( args... ); + return arr; +} + +JSON Object() { + return JSON::Make( JSON::Class::Object ); +} + +std::ostream& operator<<( std::ostream &os, const JSON &json ) { + os << json.dump(); + return os; +} + +namespace { + JSON parse_next( const string &, size_t & ); + + void consume_ws( const string &str, size_t &offset ) { + while( isspace( str[offset] ) ) ++offset; + } + + JSON parse_object( const string &str, size_t &offset ) { + JSON Object = JSON::Make( JSON::Class::Object ); + + ++offset; + consume_ws( str, offset ); + if( str[offset] == '}' ) { + ++offset; return Object; + } + + for (;;) { + JSON Key = parse_next( str, offset ); + consume_ws( str, offset ); + if( str[offset] != ':' ) { + std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n"; + break; + } + consume_ws( str, ++offset ); + JSON Value = parse_next( str, offset ); + Object[Key.ToString()] = Value; + + consume_ws( str, offset ); + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == '}' ) { + ++offset; break; + } + else { + std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n"; + break; + } + } + + return Object; + } + + JSON parse_array( const string &str, size_t &offset ) { + JSON Array = JSON::Make( JSON::Class::Array ); + unsigned index = 0; + + ++offset; + consume_ws( str, offset ); + if( str[offset] == ']' ) { + ++offset; return Array; + } + + for (;;) { + Array[index++] = parse_next( str, offset ); + consume_ws( str, offset ); + + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == ']' ) { + ++offset; break; + } + else { + std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n"; + return JSON::Make( JSON::Class::Array ); + } + } + + return Array; + } + + JSON parse_string( const string &str, size_t &offset ) { + string val; + for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) { + if( c == '\\' ) { + switch( str[ ++offset ] ) { + case '\"': val += '\"'; break; + case '\\': val += '\\'; break; + case '/' : val += '/' ; break; + case 'b' : val += '\b'; break; + case 'f' : val += '\f'; break; + case 'n' : val += '\n'; break; + case 'r' : val += '\r'; break; + case 't' : val += '\t'; break; + case 'u' : { + val += "\\u" ; + for( unsigned i = 1; i <= 4; ++i ) { + c = str[offset+i]; + if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + val += c; + else { + std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n"; + return JSON::Make( JSON::Class::String ); + } + } + offset += 4; + } break; + default : val += '\\'; break; + } + } + else + val += c; + } + ++offset; + return JSON(val); + } + + JSON parse_number( const string &str, size_t &offset ) { + JSON Number; + string val, exp_str; + char c; + bool isDouble = false; + long exp = 0; + for (;;) { + c = str[offset++]; + if( (c == '-') || (c >= '0' && c <= '9') ) + val += c; + else if( c == '.' ) { + val += c; + isDouble = true; + } + else + break; + } + if( c == 'E' || c == 'e' ) { + c = str[ offset++ ]; + if( c == '-' ){ ++offset; exp_str += '-';} + for (;;) { + c = str[ offset++ ]; + if( c >= '0' && c <= '9' ) + exp_str += c; + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n"; + return JSON::Make( JSON::Class::Null ); + } + else + break; + } + exp = std::stol( exp_str ); + } + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + std::cerr << "ERROR: Number: unexpected character '" << c << "'\n"; + return JSON::Make( JSON::Class::Null ); + } + --offset; + + if( isDouble ) + Number = std::stod( val ) * std::pow( 10, exp ); + else { + if( !exp_str.empty() ) + Number = std::stol( val ) * std::pow( 10, exp ); + else + Number = std::stol( val ); + } + return Number; + } + + JSON parse_bool( const string &str, size_t &offset ) { + JSON Bool; + if( str.substr( offset, 4 ) == "true" ) + Bool = true; + else if( str.substr( offset, 5 ) == "false" ) + Bool = false; + else { + std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n"; + return JSON::Make( JSON::Class::Null ); + } + offset += (Bool.ToBool() ? 4 : 5); + return Bool; + } + + JSON parse_null( const string &str, size_t &offset ) { + if( str.substr( offset, 4 ) != "null" ) { + std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr( offset, 4 ) << "'\n"; + return JSON::Make( JSON::Class::Null ); + } + offset += 4; + return JSON(); + } + + JSON parse_next( const string &str, size_t &offset ) { + char value; + consume_ws( str, offset ); + value = str[offset]; + switch( value ) { + case '[' : return parse_array( str, offset ); + case '{' : return parse_object( str, offset ); + case '\"': return parse_string( str, offset ); + case 't' : + case 'f' : return parse_bool( str, offset ); + case 'n' : return parse_null( str, offset ); + default : if( ( value <= '9' && value >= '0' ) || value == '-' ) + return parse_number( str, offset ); + } + std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n"; + return JSON(); + } +} + +JSON JSON::Load( const string &str ) { + size_t offset = 0; + return parse_next( str, offset ); +} + +} // End Namespace json + + +#endif diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp new file mode 100644 index 0000000..55c32fe --- /dev/null +++ b/include/chaiscript/utility/json_wrap.hpp @@ -0,0 +1,74 @@ +#ifndef CHAISCRIPT_SIMPLEJSON_WRAP_HPP +#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP + +#include "json.hpp" + +namespace chaiscript +{ + class json_wrap + { + public: + + static ModulePtr library(ModulePtr m = std::make_shared()) + { + + m->add(chaiscript::fun([](const std::string &t_str) { return json_wrap::from_json(t_str); }), "from_json"); +// m->add(chaiscript::fun(&json_wrap::to_json), "to_json"); + + return m; + + } + + private: + + static Boxed_Value from_json(const json::JSON &t_json) + { + switch( t_json.JSONType() ) { + case json::JSON::Class::Null: + return Boxed_Value(); + case json::JSON::Class::Object: + { + std::map m; + + for (const auto &p : t_json.ObjectRange()) + { + m.emplace(p.first, from_json(p.second)); + } + + return Boxed_Value(m); + } + case json::JSON::Class::Array: + { + std::vector vec; + + for (const auto &p : t_json.ArrayRange()) + { + vec.emplace_back(from_json(p)); + } + + return Boxed_Value(vec); + } + case json::JSON::Class::String: + return Boxed_Value(t_json.ToString()); + case json::JSON::Class::Floating: + return Boxed_Value(t_json.ToFloat()); + case json::JSON::Class::Integral: + return Boxed_Value(t_json.ToInt()); + case json::JSON::Class::Boolean: + return Boxed_Value(t_json.ToBool()); + } + + throw std::runtime_error("Unknown JSON type"); + } + + static Boxed_Value from_json(const std::string &t_json) + { + return from_json( json::JSON::Load(t_json) ); + } + + }; + + +} + +#endif From 8024edeadf751fde53d7e75192f027a7e949867d Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sun, 20 Sep 2015 15:46:05 -0600 Subject: [PATCH 09/45] Fix some JSON parsing bug with short strings --- include/chaiscript/utility/json.hpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index dbdf886..9b1c659 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -457,7 +457,7 @@ namespace { ++offset; return Object; } - for (;;) { + for (;offset= '0' && c <= '9') ) val += c; @@ -566,7 +566,7 @@ namespace { else break; } - if( c == 'E' || c == 'e' ) { + if( offset < str.size() && (c == 'E' || c == 'e' )) { c = str[ offset++ ]; if( c == '-' ){ ++offset; exp_str += '-';} for (;;) { @@ -582,7 +582,7 @@ namespace { } exp = std::stol( exp_str ); } - else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { std::cerr << "ERROR: Number: unexpected character '" << c << "'\n"; return JSON::Make( JSON::Class::Null ); } @@ -601,15 +601,16 @@ namespace { JSON parse_bool( const string &str, size_t &offset ) { JSON Bool; - if( str.substr( offset, 4 ) == "true" ) + if( str.substr( offset, 4 ) == "true" ) { + offset += 4; Bool = true; - else if( str.substr( offset, 5 ) == "false" ) + } else if( str.substr( offset, 5 ) == "false" ) { + offset += 5; Bool = false; - else { + } else { std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n"; return JSON::Make( JSON::Class::Null ); } - offset += (Bool.ToBool() ? 4 : 5); return Bool; } From 85ac1052dd1e457c790b495ba90449b7ac739287 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sun, 20 Sep 2015 16:19:11 -0600 Subject: [PATCH 10/45] Initial support for export to JSON --- .../chaiscript/dispatchkit/boxed_number.hpp | 16 ++++ include/chaiscript/utility/json.hpp | 4 +- include/chaiscript/utility/json_wrap.hpp | 78 +++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index c953886..07a1103 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -100,6 +100,7 @@ namespace chaiscript :(Common_Types::t_uint64); } + static Common_Types get_common_type(const Boxed_Value &t_bv) { const Type_Info &inp_ = t_bv.get_type_info(); @@ -513,6 +514,21 @@ namespace chaiscript validate_boxed_number(bv); } + static bool is_floating_point(const Boxed_Value &t_bv) + { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == typeid(double)) { + return true; + } else if (inp_ == typeid(long double)) { + return true; + } else if (inp_ == typeid(float)) { + return true; + } else { + return false; + } + } + Boxed_Number get_as(const Type_Info &inp_) const { if (inp_.bare_equal_type_info(typeid(int))) { diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 9b1c659..ef3fd34 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -199,10 +199,10 @@ class JSON JSON( T b, typename enable_if::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){} template - JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( (long)i ), Type( Class::Integral ){} + JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( long(i) ), Type( Class::Integral ){} template - JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( (double)f ), Type( Class::Floating ){} + JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( double(f) ), Type( Class::Floating ){} template JSON( T s, typename enable_if::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){} diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 55c32fe..cf843f0 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -66,6 +66,84 @@ namespace chaiscript return from_json( json::JSON::Load(t_json) ); } + static json::JSON to_json_object(const Boxed_Value &t_bv) + { + try { + const std::map m = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj; + for (const auto &o : m) + { + obj[o.first] = to_json_object(o.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a map + } + + try { + const std::vector v = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj; + for (size_t i = 0; i < v.size(); ++i) + { + obj[i] = to_json_object(v[i]); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a vector + } + + + try { + Boxed_Number bn(t_bv); + json::JSON obj; + if (Boxed_Number::is_floating_point(t_bv)) + { + obj = bn.get_as(); + } else { + obj = bn.get_as(); + } + } catch (const chaiscript::detail::exception::bad_any_cast &) { + // not a number + } + + try { + bool b = boxed_cast(t_bv); + json::JSON obj; + obj = b; + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a bool + } + + try { + std::string s = boxed_cast(t_bv); + json::JSON obj; + obj = s; + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a string + } + + + try { + const chaiscript::dispatch::Dynamic_Object &o = boxed_cast(t_bv); + + json::JSON obj; + for (const auto &attr : o.get_attrs()) + { + obj[attr.first] = to_json_object(attr.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a dynamic object + } + + throw std::runtime_error("Unknown object type to convert to JSON"); + } + + }; From e62a38b39f1ccc8a246b765aca9ecc034ccc16a9 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 21 Sep 2015 09:27:23 -0600 Subject: [PATCH 11/45] JSON output working --- include/chaiscript/utility/json_wrap.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index cf843f0..b7e3ca1 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -13,7 +13,7 @@ namespace chaiscript { m->add(chaiscript::fun([](const std::string &t_str) { return json_wrap::from_json(t_str); }), "from_json"); -// m->add(chaiscript::fun(&json_wrap::to_json), "to_json"); + m->add(chaiscript::fun(&json_wrap::to_json), "to_json"); return m; @@ -66,6 +66,11 @@ namespace chaiscript return from_json( json::JSON::Load(t_json) ); } + static std::string to_json(const Boxed_Value &t_bv) + { + return to_json_object(t_bv).dump(); + } + static json::JSON to_json_object(const Boxed_Value &t_bv) { try { @@ -104,6 +109,7 @@ namespace chaiscript } else { obj = bn.get_as(); } + return obj; } catch (const chaiscript::detail::exception::bad_any_cast &) { // not a number } From 681f18ee620da6919f63ddb4735fd60cb6f22a5b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 21 Sep 2015 12:27:33 -0600 Subject: [PATCH 12/45] backport JSON for G++4.6 --- include/chaiscript/dispatchkit/function_call.hpp | 2 +- include/chaiscript/utility/json.hpp | 6 +++--- include/chaiscript/utility/json_wrap.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/chaiscript/dispatchkit/function_call.hpp b/include/chaiscript/dispatchkit/function_call.hpp index 5692e34..c5b950a 100644 --- a/include/chaiscript/dispatchkit/function_call.hpp +++ b/include/chaiscript/dispatchkit/function_call.hpp @@ -40,7 +40,7 @@ namespace chaiscript { const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), [](const Const_Proxy_Function &f) { - return f->get_arity() == -1 || f->get_arity() == chaiscript::dispatch::detail::Arity::arity; + return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity::arity; }); if (!has_arity_match) { diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index ef3fd34..82a1dac 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -107,13 +107,13 @@ class JSON JSON() : Internal(), Type( Class::Null ){} explicit JSON(Class type) - : JSON() + : Internal(), Type(Class::Null) { SetType( type ); } JSON( initializer_list list ) - : JSON() + : Internal(), Type(Class::Null) { SetType( Class::Object ); for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) @@ -418,7 +418,7 @@ class JSON private: - Class Type = Class::Null; + Class Type; }; JSON Array() { diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index b7e3ca1..1b0631d 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -32,7 +32,7 @@ namespace chaiscript for (const auto &p : t_json.ObjectRange()) { - m.emplace(p.first, from_json(p.second)); + m.insert(std::make_pair(p.first, from_json(p.second))); } return Boxed_Value(m); From ca35128503062f9e5163eb347ef58295bb266702 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 30 Sep 2015 06:32:34 -0600 Subject: [PATCH 13/45] Add failing test for long long conversions --- unittests/compiled_tests.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 9d1df58..da7eb7c 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -784,4 +784,21 @@ TEST_CASE("Variable Scope When Calling From C++ 2") CHECK_THROWS(func()); } +void ulonglong(unsigned long long i) { + std::cout << i << '\n'; +} + + +void longlong(long long i) { + std::cout << i << '\n'; +} + +TEST_CASE("Test long long dispatch") +{ + chaiscript::ChaiScript chai; + chai.add(chaiscript::fun(&longlong), "longlong"); + chai.add(chaiscript::fun(&ulonglong), "ulonglong"); + chai.eval("longlong(15)"); + chai.eval("ulonglong(15)"); +} From b4ffcd594d80896ccf56e0b442869a26304849f9 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 30 Sep 2015 06:49:03 -0600 Subject: [PATCH 14/45] Fix long long type usage Closes #208 --- include/chaiscript/dispatchkit/boxed_number.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index c953886..8c5440e 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -120,8 +120,12 @@ namespace chaiscript return get_common_type(sizeof(unsigned int), false); } else if (inp_ == typeid(long)) { return get_common_type(sizeof(long), true); + } else if (inp_ == typeid(long long)) { + return get_common_type(sizeof(long long), true); } else if (inp_ == typeid(unsigned long)) { return get_common_type(sizeof(unsigned long), false); + } else if (inp_ == typeid(unsigned long long)) { + return get_common_type(sizeof(unsigned long long), false); } else if (inp_ == typeid(std::int8_t)) { return Common_Types::t_int8; } else if (inp_ == typeid(std::int16_t)) { @@ -537,8 +541,12 @@ namespace chaiscript return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(long))) { return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(long long))) { + return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(unsigned long))) { return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(unsigned long long))) { + return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(int8_t))) { return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(int16_t))) { From ba30d4f48388adaf4691c24e8688cfc1adeb1bde Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 30 Sep 2015 08:57:36 -0600 Subject: [PATCH 15/45] Add support for == for Map --- .../chaiscript/dispatchkit/bootstrap_stl.hpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 2504f00..186ccba 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -468,6 +468,30 @@ namespace chaiscript m->add(fun(static_cast(&MapType::at)), "at"); m->add(fun(static_cast(&MapType::at)), "at"); + if (typeid(MapType) == typeid(std::map)) + { + m->eval(R"( + def Map::`==`(Map rhs) { + if ( rhs.size() != this.size() ) { + return false; + } else { + auto r1 = range(this); + auto r2 = range(rhs); + while (!r1.empty()) + { + if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second)) + { + return false; + } + r1.pop_front(); + r2.pop_front(); + } + true; + } + } )" + ); + } + container_type(type, m); default_constructible_type(type, m); assignable_type(type, m); @@ -523,7 +547,7 @@ namespace chaiscript if (typeid(VectorType) == typeid(std::vector)) { m->eval(R"( - def Vector::`==`(rhs) : type_match(rhs, this) { + def Vector::`==`(Vector rhs) { if ( rhs.size() != this.size() ) { return false; } else { From b434d26a5d2c9a9e1b8d12e410e94832c384e230 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 30 Sep 2015 14:24:56 -0600 Subject: [PATCH 16/45] Add json tests --- include/chaiscript/utility/json.hpp | 36 ++++++++++++----------------- unittests/json_1.chai | 3 +++ unittests/json_10.chai | 1 + unittests/json_11.chai | 14 +++++++++++ unittests/json_12.chai | 1 + unittests/json_13.chai | 1 + unittests/json_2.chai | 3 +++ unittests/json_3.chai | 1 + unittests/json_4.chai | 1 + unittests/json_5.chai | 1 + unittests/json_6.chai | 1 + unittests/json_7.chai | 4 ++++ unittests/json_8.chai | 1 + unittests/json_9.chai | 2 ++ 14 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 unittests/json_1.chai create mode 100644 unittests/json_10.chai create mode 100644 unittests/json_11.chai create mode 100644 unittests/json_12.chai create mode 100644 unittests/json_13.chai create mode 100644 unittests/json_2.chai create mode 100644 unittests/json_3.chai create mode 100644 unittests/json_4.chai create mode 100644 unittests/json_5.chai create mode 100644 unittests/json_6.chai create mode 100644 unittests/json_7.chai create mode 100644 unittests/json_8.chai create mode 100644 unittests/json_9.chai diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 82a1dac..e8cae19 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -301,7 +301,7 @@ class JSON string ToString() const { bool b; return ToString( b ); } string ToString( bool &ok ) const { ok = (Type == Class::String); - return ok ? std::move( json_escape( *Internal.String ) ): string(""); + return ok ? *Internal.String : string(""); } double ToFloat() const { bool b; return ToFloat( b ); } @@ -461,8 +461,7 @@ namespace { JSON Key = parse_next( str, offset ); consume_ws( str, offset ); if( str[offset] != ':' ) { - std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n"; - break; + throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str[offset] + "'\n"); } consume_ws( str, ++offset ); JSON Value = parse_next( str, offset ); @@ -476,8 +475,7 @@ namespace { ++offset; break; } else { - std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n"; - break; + throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str[offset] + "'\n"); } } @@ -505,8 +503,7 @@ namespace { ++offset; break; } else { - std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n"; - return JSON::Make( JSON::Class::Array ); + throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'\n"); } } @@ -533,8 +530,7 @@ namespace { if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) val += c; else { - std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n"; - return JSON::Make( JSON::Class::String ); + throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); } } offset += 4; @@ -568,14 +564,16 @@ namespace { } if( offset < str.size() && (c == 'E' || c == 'e' )) { c = str[ offset++ ]; - if( c == '-' ){ ++offset; exp_str += '-';} - for (;;) { + if( c == '-' ) { exp_str += '-';} + else if( c == '+' ) { } + else --offset; + + for (; offset < str.size() ;) { c = str[ offset++ ]; if( c >= '0' && c <= '9' ) exp_str += c; else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { - std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n"; - return JSON::Make( JSON::Class::Null ); + throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'"); } else break; @@ -583,8 +581,7 @@ namespace { exp = std::stol( exp_str ); } else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { - std::cerr << "ERROR: Number: unexpected character '" << c << "'\n"; - return JSON::Make( JSON::Class::Null ); + throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); } --offset; @@ -608,16 +605,14 @@ namespace { offset += 5; Bool = false; } else { - std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n"; - return JSON::Make( JSON::Class::Null ); + throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'"); } return Bool; } JSON parse_null( const string &str, size_t &offset ) { if( str.substr( offset, 4 ) != "null" ) { - std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr( offset, 4 ) << "'\n"; - return JSON::Make( JSON::Class::Null ); + throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'"); } offset += 4; return JSON(); @@ -637,8 +632,7 @@ namespace { default : if( ( value <= '9' && value >= '0' ) || value == '-' ) return parse_number( str, offset ); } - std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n"; - return JSON(); + throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'"); } } diff --git a/unittests/json_1.chai b/unittests/json_1.chai new file mode 100644 index 0000000..63277b3 --- /dev/null +++ b/unittests/json_1.chai @@ -0,0 +1,3 @@ + + +assert_true(from_json("null").is_var_null()) diff --git a/unittests/json_10.chai b/unittests/json_10.chai new file mode 100644 index 0000000..f19c7cf --- /dev/null +++ b/unittests/json_10.chai @@ -0,0 +1 @@ +assert_equal(from_json("\"This is a\\n\\nMultiline string\""), "This is a\n\nMultiline string") diff --git a/unittests/json_11.chai b/unittests/json_11.chai new file mode 100644 index 0000000..f11db38 --- /dev/null +++ b/unittests/json_11.chai @@ -0,0 +1,14 @@ +assert_equal(from_json( +"{\n" + +" \"T1\" : \"Value With a Quote : \\\"\",\n" + +" \"T2\" : \"Value With a Rev Solidus : \\/\",\n" + +" \"T3\" : \"Value with a Solidus : \\\\\",\n" + +" \"T4\" : \"Value with a Backspace : \\b\",\n" + +" \"T5\" : \"Value with a Formfeed : \\f\",\n" + +" \"T6\" : \"Value with a Newline : \\n\",\n" + +" \"T7\" : \"Value with a Carriage Return : \\r\",\n" + +" \"T8\" : \"Value with a Horizontal Tab : \\t\"\n" + +"}"), [ "T1" : "Value With a Quote : \"", "T2" : "Value With a Rev Solidus : /", "T3" : "Value with a Solidus : \\", "T4" : "Value with a Backspace : \b", "T5" : "Value with a Formfeed : \f", "T6" : "Value with a Newline : \n", "T7" : "Value with a Carriage Return : \r", "T8" : "Value with a Horizontal Tab : \t" ]); + + + diff --git a/unittests/json_12.chai b/unittests/json_12.chai new file mode 100644 index 0000000..a93211d --- /dev/null +++ b/unittests/json_12.chai @@ -0,0 +1 @@ +assert_equal(from_json("\"\""), "") diff --git a/unittests/json_13.chai b/unittests/json_13.chai new file mode 100644 index 0000000..3f9fa00 --- /dev/null +++ b/unittests/json_13.chai @@ -0,0 +1 @@ +assert_equal(from_json("1.20E+2"), 1.20e2) diff --git a/unittests/json_2.chai b/unittests/json_2.chai new file mode 100644 index 0000000..0f779bd --- /dev/null +++ b/unittests/json_2.chai @@ -0,0 +1,3 @@ + +assert_true(from_json("true")) + diff --git a/unittests/json_3.chai b/unittests/json_3.chai new file mode 100644 index 0000000..d3f222e --- /dev/null +++ b/unittests/json_3.chai @@ -0,0 +1 @@ +assert_equal(from_json("100"), 100) diff --git a/unittests/json_4.chai b/unittests/json_4.chai new file mode 100644 index 0000000..22d388c --- /dev/null +++ b/unittests/json_4.chai @@ -0,0 +1 @@ +assert_equal(from_json("1.234"), 1.234) diff --git a/unittests/json_5.chai b/unittests/json_5.chai new file mode 100644 index 0000000..9ad2ca2 --- /dev/null +++ b/unittests/json_5.chai @@ -0,0 +1 @@ +assert_equal(from_json("\"StringTest\""), "StringTest") diff --git a/unittests/json_6.chai b/unittests/json_6.chai new file mode 100644 index 0000000..8c33bff --- /dev/null +++ b/unittests/json_6.chai @@ -0,0 +1 @@ +assert_equal(from_json("{}"), Map()) diff --git a/unittests/json_7.chai b/unittests/json_7.chai new file mode 100644 index 0000000..b2c6efa --- /dev/null +++ b/unittests/json_7.chai @@ -0,0 +1,4 @@ +assert_equal(from_json("\n" + +"{\n" + +" \"Key\" : \"Value\"\n" + +"}\n"), ["Key":"Value"]) diff --git a/unittests/json_8.chai b/unittests/json_8.chai new file mode 100644 index 0000000..104c8bc --- /dev/null +++ b/unittests/json_8.chai @@ -0,0 +1 @@ +assert_equal(from_json("[]"), []) diff --git a/unittests/json_9.chai b/unittests/json_9.chai new file mode 100644 index 0000000..1512738 --- /dev/null +++ b/unittests/json_9.chai @@ -0,0 +1,2 @@ +assert_equal(from_json("[1,2,3]"), [1,2,3]) + From d9fa5605ac98f9ab724199efef8be33680116264 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 1 Oct 2015 09:39:03 -0600 Subject: [PATCH 17/45] Add operator overload tests --- unittests/operator_overload3.chai | 7 +++++++ unittests/operator_overload4.chai | 6 ++++++ 2 files changed, 13 insertions(+) create mode 100644 unittests/operator_overload3.chai create mode 100644 unittests/operator_overload4.chai diff --git a/unittests/operator_overload3.chai b/unittests/operator_overload3.chai new file mode 100644 index 0000000..90b81cd --- /dev/null +++ b/unittests/operator_overload3.chai @@ -0,0 +1,7 @@ + + +def string::`/=`(double d) { this = "${this}/=${d}"; return this; } + +var s = "Hello World" +s /= 2 + diff --git a/unittests/operator_overload4.chai b/unittests/operator_overload4.chai new file mode 100644 index 0000000..832d984 --- /dev/null +++ b/unittests/operator_overload4.chai @@ -0,0 +1,6 @@ + + +def string::`*`(double d) { return "${this} * ${d}"; } + +"Hello World" * 2 + From 5a651e2b8a0d5000e4c994ae2fa0a3fe6bf1fdc6 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 1 Oct 2015 09:56:53 -0600 Subject: [PATCH 18/45] Fix numeric overload resolution Closes #209 --- include/chaiscript/dispatchkit/dispatchkit.hpp | 1 + include/chaiscript/dispatchkit/proxy_functions.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index de2caa0..806abae 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1033,6 +1033,7 @@ namespace chaiscript if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); Boxed_Value bv = dispatch::dispatch(*funs.second, params, m_conversions); // the result of a clone is never to be marked as a return_value + // \todo see if we can eliminate this comparison if (t_name == "clone") { bv.reset_return_value(); } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 156831f..d9da2e6 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -211,7 +211,7 @@ namespace chaiscript if (ti.is_undef() || ti.bare_equal(user_type()) || (!bv.get_type_info().is_undef() - && (ti.bare_equal(user_type()) + && ( (ti.bare_equal(user_type()) && bv.get_type_info().is_arithmetic()) || ti.bare_equal(bv.get_type_info()) || bv.get_type_info().bare_equal(user_type >()) || t_conversions.converts(ti, bv.get_type_info()) From 6a4647af43040db97b33aa16f4b2dbb5be11566d Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Oct 2015 08:12:50 -0600 Subject: [PATCH 19/45] Add last test for json support Closes #207 --- unittests/json_roundtrip.chai | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 unittests/json_roundtrip.chai diff --git a/unittests/json_roundtrip.chai b/unittests/json_roundtrip.chai new file mode 100644 index 0000000..94682e5 --- /dev/null +++ b/unittests/json_roundtrip.chai @@ -0,0 +1,6 @@ + +var m = ["a" : 1, "b" : [ 1, 2, 3 ], "c" : [1, "a string", ["d" : 15.4]]] + +assert_equal(from_json(to_json(m)), m) + + From 8d9dc2b0a3637bb3eafe0eb522ec364b427e4676 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Oct 2015 10:35:37 -0600 Subject: [PATCH 20/45] Reduce redundant escape code parsing #211 --- .../chaiscript/language/chaiscript_parser.hpp | 124 +++++++++--------- 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 6aef050..9e075ee 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -949,6 +949,57 @@ namespace chaiscript return false; } + struct Char_Parser + { + bool is_escaped; + bool is_interpolated; + bool saw_interpolation_marker; + const bool interpolation_allowed; + + Char_Parser(const bool t_interpolation_allowed) + : is_escaped(false), + is_interpolated(false), + saw_interpolation_marker(false), + interpolation_allowed(t_interpolation_allowed) + { + } + + void parse(std::string &t_match, const char t_char, const int line, const int col, const std::string &filename) { + if (t_char == '\\') { + if (is_escaped) { + t_match.push_back('\\'); + is_escaped = false; + } else { + is_escaped = true; + } + } else { + if (is_escaped) { + switch (t_char) { + case ('\'') : t_match.push_back('\''); break; + case ('\"') : t_match.push_back('\"'); break; + case ('?') : t_match.push_back('?'); break; + case ('a') : t_match.push_back('\a'); break; + case ('b') : t_match.push_back('\b'); break; + case ('f') : t_match.push_back('\f'); break; + case ('n') : t_match.push_back('\n'); break; + case ('r') : t_match.push_back('\r'); break; + case ('t') : t_match.push_back('\t'); break; + case ('v') : t_match.push_back('\v'); break; + case ('$') : t_match.push_back('$'); break; + default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); + } + } else if (interpolation_allowed && t_char == '$') { + saw_interpolation_marker = true; + } else { + t_match.push_back(t_char); + } + is_escaped = false; + } + } + + }; + + /// Reads (and potentially captures) a quoted string from input. Translates escaped sequences. bool Quoted_String(const bool t_capture = false) { SkipWS(); @@ -960,19 +1011,19 @@ namespace chaiscript if (Quoted_String_()) { std::string match; - bool is_escaped = false; - bool is_interpolated = false; - bool saw_interpolation_marker = false; + + Char_Parser cparser(true); + const auto prev_stack_top = m_match_stack.size(); auto s = start + 1, end = m_position - 1; while (s != end) { - if (saw_interpolation_marker) { + if (cparser.saw_interpolation_marker) { if (*s == '{') { //We've found an interpolation point - if (is_interpolated) { + if (cparser.is_interpolated) { //If we've seen previous interpolation, add on instead of making a new one m_match_stack.push_back(make_node(match, start.line, start.col)); @@ -993,7 +1044,7 @@ namespace chaiscript } if (*s == '}') { - is_interpolated = true; + cparser.is_interpolated = true; ++s; const auto tostr_stack_top = m_match_stack.size(); @@ -1019,40 +1070,14 @@ namespace chaiscript } else { match.push_back('$'); } - saw_interpolation_marker = false; + cparser.saw_interpolation_marker = false; } else { - if (*s == '\\') { - if (is_escaped) { - match.push_back('\\'); - is_escaped = false; - } else { - is_escaped = true; - } - } else { - if (is_escaped) { - switch (*s) { - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - case ('$') : match.push_back('$'); break; - default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(start.line, start.col), *m_filename); - } - } else if (*s == '$') { - saw_interpolation_marker = true; - } else { - match.push_back(*s); - } - is_escaped = false; - } + cparser.parse(match, *s, start.line, start.col, *m_filename); ++s; } } - if (is_interpolated) { + if (cparser.is_interpolated) { m_match_stack.push_back(make_node(match, start.line, start.col)); build_match(prev_stack_top, "+"); @@ -1104,33 +1129,12 @@ namespace chaiscript const auto start = m_position; if (Single_Quoted_String_()) { std::string match; - bool is_escaped = false; + Char_Parser cparser(false); + for (auto s = start + 1, end = m_position - 1; s != end; ++s) { - if (*s == '\\') { - if (is_escaped) { - match.push_back('\\'); - is_escaped = false; - } else { - is_escaped = true; - } - } else { - if (is_escaped) { - switch (*s) { - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(start.line, start.col), *m_filename); - } - } else { - match.push_back(*s); - } - is_escaped = false; - } + cparser.parse(match, *s, start.line, start.col, *m_filename); } + m_match_stack.push_back(make_node(match, start.line, start.col)); return true; } From 41e9027d9a8a0e8944900529b7ca2b44c9f79212 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Oct 2015 11:45:28 -0600 Subject: [PATCH 21/45] Octal escape codes supported #211 --- CMakeLists.txt | 2 +- .../chaiscript/language/chaiscript_parser.hpp | 205 +++++++++++------- include/chaiscript/utility/json.hpp | 4 +- 3 files changed, 128 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2380e06..07ae809 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,7 +174,7 @@ else() add_definitions(-Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_definitions(-Weverything -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors) + add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors) else() add_definitions(-Wnoexcept) endif() diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 9e075ee..ebb8bfc 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -951,49 +951,88 @@ namespace chaiscript struct Char_Parser { + std::string &match; bool is_escaped; bool is_interpolated; bool saw_interpolation_marker; + bool is_octal; const bool interpolation_allowed; - Char_Parser(const bool t_interpolation_allowed) - : is_escaped(false), + std::string octal_matches; + + Char_Parser(std::string &t_match, const bool t_interpolation_allowed) + : match(t_match), + is_escaped(false), is_interpolated(false), saw_interpolation_marker(false), + is_octal(false), interpolation_allowed(t_interpolation_allowed) { } - void parse(std::string &t_match, const char t_char, const int line, const int col, const std::string &filename) { + ~Char_Parser(){ + if (is_octal) { + process_octal(); + } + } + + void process_octal() + { + int val = stoi(octal_matches, 0, 8); + match.push_back(char(val)); + octal_matches.clear(); + is_escaped = false; + is_octal = false; + } + + void parse(const char t_char, const int line, const int col, const std::string &filename) { if (t_char == '\\') { if (is_escaped) { - t_match.push_back('\\'); + match.push_back('\\'); is_escaped = false; } else { is_escaped = true; } } else { if (is_escaped) { - switch (t_char) { - case ('\'') : t_match.push_back('\''); break; - case ('\"') : t_match.push_back('\"'); break; - case ('?') : t_match.push_back('?'); break; - case ('a') : t_match.push_back('\a'); break; - case ('b') : t_match.push_back('\b'); break; - case ('f') : t_match.push_back('\f'); break; - case ('n') : t_match.push_back('\n'); break; - case ('r') : t_match.push_back('\r'); break; - case ('t') : t_match.push_back('\t'); break; - case ('v') : t_match.push_back('\v'); break; - case ('$') : t_match.push_back('$'); break; - default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); + bool is_octal_char = t_char >= '0' && t_char <= '7'; + + if (is_octal) { + if (is_octal_char) { + octal_matches.push_back(t_char); + + if (octal_matches.size() == 3) { + process_octal(); + } + } else { + process_octal(); + match.push_back(t_char); + } + } else if (is_octal_char) { + is_octal = true; + octal_matches.push_back(t_char); + } else { + switch (t_char) { + case ('\'') : match.push_back('\''); break; + case ('\"') : match.push_back('\"'); break; + case ('?') : match.push_back('?'); break; + case ('a') : match.push_back('\a'); break; + case ('b') : match.push_back('\b'); break; + case ('f') : match.push_back('\f'); break; + case ('n') : match.push_back('\n'); break; + case ('r') : match.push_back('\r'); break; + case ('t') : match.push_back('\t'); break; + case ('v') : match.push_back('\v'); break; + case ('$') : match.push_back('$'); break; + default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); + } + is_escaped = false; } } else if (interpolation_allowed && t_char == '$') { saw_interpolation_marker = true; } else { - t_match.push_back(t_char); + match.push_back(t_char); } - is_escaped = false; } } @@ -1011,73 +1050,77 @@ namespace chaiscript if (Quoted_String_()) { std::string match; - - Char_Parser cparser(true); - const auto prev_stack_top = m_match_stack.size(); - auto s = start + 1, end = m_position - 1; + bool is_interpolated = [&]() { + Char_Parser cparser(match, true); - while (s != end) { - if (cparser.saw_interpolation_marker) { - if (*s == '{') { - //We've found an interpolation point - if (cparser.is_interpolated) { - //If we've seen previous interpolation, add on instead of making a new one - m_match_stack.push_back(make_node(match, start.line, start.col)); + auto s = start + 1, end = m_position - 1; - build_match(prev_stack_top, "+"); - } else { - m_match_stack.push_back(make_node(match, start.line, start.col)); - } + while (s != end) { + if (cparser.saw_interpolation_marker) { + if (*s == '{') { + //We've found an interpolation point - //We've finished with the part of the string up to this point, so clear it - match.clear(); + if (cparser.is_interpolated) { + //If we've seen previous interpolation, add on instead of making a new one + m_match_stack.push_back(make_node(match, start.line, start.col)); - std::string eval_match; - - ++s; - while ((s != end) && (*s != '}')) { - eval_match.push_back(*s); - ++s; - } - - if (*s == '}') { - cparser.is_interpolated = true; - ++s; - - const auto tostr_stack_top = m_match_stack.size(); - - m_match_stack.push_back(make_node("to_string", start.line, start.col)); - - const auto ev_stack_top = m_match_stack.size(); - - try { - ChaiScript_Parser parser; - parser.parse(eval_match, "instr eval"); - m_match_stack.push_back(parser.ast()); - } catch (const exception::eval_error &e) { - throw exception::eval_error(e.what(), File_Position(start.line, start.col), *m_filename); + build_match(prev_stack_top, "+"); + } else { + m_match_stack.push_back(make_node(match, start.line, start.col)); } - build_match(ev_stack_top); - build_match(tostr_stack_top); - build_match(prev_stack_top, "+"); - } else { - throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename); - } - } else { - match.push_back('$'); - } - cparser.saw_interpolation_marker = false; - } else { - cparser.parse(match, *s, start.line, start.col, *m_filename); - ++s; - } - } + //We've finished with the part of the string up to this point, so clear it + match.clear(); - if (cparser.is_interpolated) { + std::string eval_match; + + ++s; + while ((s != end) && (*s != '}')) { + eval_match.push_back(*s); + ++s; + } + + if (*s == '}') { + cparser.is_interpolated = true; + ++s; + + const auto tostr_stack_top = m_match_stack.size(); + + m_match_stack.push_back(make_node("to_string", start.line, start.col)); + + const auto ev_stack_top = m_match_stack.size(); + + try { + ChaiScript_Parser parser; + parser.parse(eval_match, "instr eval"); + m_match_stack.push_back(parser.ast()); + } catch (const exception::eval_error &e) { + throw exception::eval_error(e.what(), File_Position(start.line, start.col), *m_filename); + } + + build_match(ev_stack_top); + build_match(tostr_stack_top); + build_match(prev_stack_top, "+"); + } else { + throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename); + } + } else { + match.push_back('$'); + } + cparser.saw_interpolation_marker = false; + } else { + cparser.parse(*s, start.line, start.col, *m_filename); + ++s; + } + } + + return cparser.is_interpolated; + }(); + + if (is_interpolated) { m_match_stack.push_back(make_node(match, start.line, start.col)); build_match(prev_stack_top, "+"); @@ -1129,10 +1172,14 @@ namespace chaiscript const auto start = m_position; if (Single_Quoted_String_()) { std::string match; - Char_Parser cparser(false); - for (auto s = start + 1, end = m_position - 1; s != end; ++s) { - cparser.parse(match, *s, start.line, start.col, *m_filename); + { + // scope for cparser destrutor + Char_Parser cparser(match, false); + + for (auto s = start + 1, end = m_position - 1; s != end; ++s) { + cparser.parse(*s, start.line, start.col, *m_filename); + } } m_match_stack.push_back(make_node(match, start.line, start.col)); diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index e8cae19..e5ba4f7 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -384,8 +384,6 @@ class JSON return std::to_string( Internal.Int ); case Class::Boolean: return Internal.Bool ? "true" : "false"; - default: - return ""; } } @@ -548,7 +546,7 @@ namespace { JSON parse_number( const string &str, size_t &offset ) { JSON Number; string val, exp_str; - char c; + char c = '\0'; bool isDouble = false; long exp = 0; for (; offset < str.size() ;) { From 18e5ee0ba213dca0608810e4942c90bd7b58c10a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Oct 2015 12:16:44 -0600 Subject: [PATCH 22/45] Wrap up generic string escape support Closes #211 --- .../chaiscript/language/chaiscript_parser.hpp | 50 +++++++++++++++---- unittests/hex_escapes.chai | 6 +++ unittests/octal_escapes.chai | 6 +++ 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 unittests/hex_escapes.chai create mode 100644 unittests/octal_escapes.chai diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index ebb8bfc..bd902f1 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -949,23 +949,28 @@ namespace chaiscript return false; } + template struct Char_Parser { - std::string &match; + string_type &match; + using char_type = typename string_type::value_type; bool is_escaped; bool is_interpolated; bool saw_interpolation_marker; bool is_octal; + bool is_hex; const bool interpolation_allowed; - std::string octal_matches; + string_type octal_matches; + string_type hex_matches; - Char_Parser(std::string &t_match, const bool t_interpolation_allowed) + Char_Parser(string_type &t_match, const bool t_interpolation_allowed) : match(t_match), is_escaped(false), is_interpolated(false), saw_interpolation_marker(false), is_octal(false), + is_hex(false), interpolation_allowed(t_interpolation_allowed) { } @@ -974,18 +979,32 @@ namespace chaiscript if (is_octal) { process_octal(); } + + if (is_hex) { + process_hex(); + } } + void process_hex() + { + auto val = stoll(hex_matches, 0, 16); + match.push_back(char_type(val)); + hex_matches.clear(); + is_escaped = false; + is_hex = false; + } + + void process_octal() { - int val = stoi(octal_matches, 0, 8); - match.push_back(char(val)); + auto val = stoll(octal_matches, 0, 8); + match.push_back(char_type(val)); octal_matches.clear(); is_escaped = false; is_octal = false; } - void parse(const char t_char, const int line, const int col, const std::string &filename) { + void parse(const char_type t_char, const int line, const int col, const std::string &filename) { if (t_char == '\\') { if (is_escaped) { match.push_back('\\'); @@ -995,7 +1014,7 @@ namespace chaiscript } } else { if (is_escaped) { - bool is_octal_char = t_char >= '0' && t_char <= '7'; + const bool is_octal_char = t_char >= '0' && t_char <= '7'; if (is_octal) { if (is_octal_char) { @@ -1008,9 +1027,22 @@ namespace chaiscript process_octal(); match.push_back(t_char); } + } else if (is_hex) { + const bool is_hex_char = (t_char >= '0' && t_char <= '9') + || (t_char >= 'a' && t_char <= 'f') + || (t_char >= 'A' && t_char <= 'F'); + + if (is_hex_char) { + hex_matches.push_back(t_char); + } else { + process_hex(); + match.push_back(t_char); + } } else if (is_octal_char) { is_octal = true; octal_matches.push_back(t_char); + } else if (t_char == 'x') { + is_hex = true; } else { switch (t_char) { case ('\'') : match.push_back('\''); break; @@ -1053,7 +1085,7 @@ namespace chaiscript const auto prev_stack_top = m_match_stack.size(); bool is_interpolated = [&]() { - Char_Parser cparser(match, true); + Char_Parser cparser(match, true); auto s = start + 1, end = m_position - 1; @@ -1175,7 +1207,7 @@ namespace chaiscript { // scope for cparser destrutor - Char_Parser cparser(match, false); + Char_Parser cparser(match, false); for (auto s = start + 1, end = m_position - 1; s != end; ++s) { cparser.parse(*s, start.line, start.col, *m_filename); diff --git a/unittests/hex_escapes.chai b/unittests/hex_escapes.chai new file mode 100644 index 0000000..283ef87 --- /dev/null +++ b/unittests/hex_escapes.chai @@ -0,0 +1,6 @@ + +assert_equal("\x39", "9") +assert_equal("\x039", "9") +assert_equal("\x39g", "9g") +assert_equal("b\x39g", "b9g") + diff --git a/unittests/octal_escapes.chai b/unittests/octal_escapes.chai new file mode 100644 index 0000000..83b5392 --- /dev/null +++ b/unittests/octal_escapes.chai @@ -0,0 +1,6 @@ + +assert_equal("\71", "9") +assert_equal("\071", "9") +assert_equal("\71a", "9a") +assert_equal("b\71a", "b9a") + From 9d183603334662bf58a81a37f76ac4a9ff126e9b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Oct 2015 12:46:50 -0600 Subject: [PATCH 23/45] Older compiler backport issues --- include/chaiscript/language/chaiscript_parser.hpp | 4 ++-- include/chaiscript/utility/json.hpp | 4 +++- include/chaiscript/utility/json_wrap.hpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index bd902f1..ddc2111 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -953,7 +953,7 @@ namespace chaiscript struct Char_Parser { string_type &match; - using char_type = typename string_type::value_type; + typedef typename string_type::value_type char_type; bool is_escaped; bool is_interpolated; bool saw_interpolation_marker; @@ -1084,7 +1084,7 @@ namespace chaiscript std::string match; const auto prev_stack_top = m_match_stack.size(); - bool is_interpolated = [&]() { + bool is_interpolated = [&]()->bool { Char_Parser cparser(match, true); diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index e5ba4f7..893f7bf 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -249,7 +249,7 @@ class JSON SetType( Class::Object ); return Internal.Map->operator[]( key ); } - JSON& operator[]( unsigned index ) { + JSON& operator[]( const size_t index ) { SetType( Class::Array ); if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); return Internal.List->operator[]( index ); @@ -385,6 +385,8 @@ class JSON case Class::Boolean: return Internal.Bool ? "true" : "false"; } + + throw std::runtime_error("Unhandled JSON type"); } friend std::ostream& operator<<( std::ostream&, const JSON & ); diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 1b0631d..d15528f 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -12,7 +12,7 @@ namespace chaiscript static ModulePtr library(ModulePtr m = std::make_shared()) { - m->add(chaiscript::fun([](const std::string &t_str) { return json_wrap::from_json(t_str); }), "from_json"); + m->add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json"); m->add(chaiscript::fun(&json_wrap::to_json), "to_json"); return m; From beedf13d01696bfd05a6ecbc825b4270646ddb49 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 3 Oct 2015 16:38:41 -0600 Subject: [PATCH 24/45] Make binary literals sized like other integer types --- include/chaiscript/dispatchkit/bootstrap.hpp | 2 + .../chaiscript/language/chaiscript_parser.hpp | 126 +++++++----------- unittests/integer_literal_test.cpp | 3 +- unittests/number_suffixes.chai | 4 +- 4 files changed, 55 insertions(+), 80 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 0cd7bab..ef5a94b 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -509,6 +509,8 @@ namespace chaiscript bootstrap_pod_type("long", m); bootstrap_pod_type("unsigned_int", m); bootstrap_pod_type("unsigned_long", m); + bootstrap_pod_type("long_long", m); + bootstrap_pod_type("unsigned_long_long", m); bootstrap_pod_type("size_t", m); bootstrap_pod_type("char", m); bootstrap_pod_type("wchar_t", m); diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index ddc2111..a05129a 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -621,8 +621,7 @@ namespace chaiscript - template - static Boxed_Value buildInt(const IntType &t_type, const std::string &t_val) + static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed) { bool unsigned_ = false; bool long_ = false; @@ -649,52 +648,57 @@ namespace chaiscript } } - std::stringstream ss(t_val.substr(0, i)); - ss >> t_type; - std::stringstream testu(t_val.substr(0, i)); - uint64_t u; - testu >> t_type >> u; - - bool unsignedrequired = false; - - if ((u >> (sizeof(int) * 8)) > 0) - { - //requires something bigger than int - long_ = true; - } + const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; static_assert(sizeof(long) == sizeof(uint64_t) || sizeof(long) * 2 == sizeof(uint64_t), "Unexpected sizing of integer types"); + bool unsignedrequired = false; - if ((sizeof(long) < sizeof(uint64_t)) - && (u >> ((sizeof(uint64_t) - sizeof(long)) * 8)) > 0) - { - //requires something bigger than long - longlong_ = true; - } + try { + auto u = std::stoll(val,nullptr,base); - - const size_t size = [&]()->size_t{ - if (longlong_) + if ((u >> (sizeof(int) * 8)) > 0) { - return sizeof(int64_t) * 8; - } else if (long_) { - return sizeof(long) * 8; - } else { - return sizeof(int) * 8; + //requires something bigger than int + long_ = true; } - }(); - if ( (u >> (size - 1)) > 0) - { + if ((sizeof(long) < sizeof(uint64_t)) + && (u >> ((sizeof(uint64_t) - sizeof(long)) * 8)) > 0) + { + //requires something bigger than long + longlong_ = true; + } + + const size_t size = [&]()->size_t{ + if (longlong_) + { + return sizeof(int64_t) * 8; + } else if (long_) { + return sizeof(long) * 8; + } else { + return sizeof(int) * 8; + } + }(); + + if ( (u >> (size - 1)) > 0) + { + unsignedrequired = true; + } + } catch (const std::out_of_range &) { + // it cannot fit in a signed long long... + std::cout << "forcing long long for '" << val << "'\n"; unsignedrequired = true; + + long_ = sizeof(unsigned long long) == sizeof(unsigned long); + longlong_ = sizeof(unsigned long long) != sizeof(unsigned long); } if (unsignedrequired && !unsigned_) { - if (t_type == &std::hex || t_type == &std::oct) + if (base != 10) { - // with hex and octal we are happy to just make it unsigned + // with bin, hex and oct we are happy to just make it unsigned unsigned_ = true; } else { // with decimal we must bump it up to the next size @@ -711,32 +715,20 @@ namespace chaiscript { if (longlong_) { - uint64_t val; - ss >> val; - return const_var(val); + return const_var(stoull(val,nullptr,base)); } else if (long_) { - unsigned long val; - ss >> val; - return const_var(val); + return const_var(stoul(val,nullptr,base)); } else { - unsigned int val; - ss >> val; - return const_var(val); + return const_var(static_cast(stoul(val,nullptr,base))); } } else { if (longlong_) { - int64_t val; - ss >> val; - return const_var(val); + return const_var(stoll(val,nullptr,base)); } else if (long_) { - long val; - ss >> val; - return const_var(val); + return const_var(stol(val,nullptr,base)); } else { - int val; - ss >> val; - return const_var(val); + return const_var(stoi(val,nullptr,base)); } } } @@ -758,35 +750,15 @@ namespace chaiscript if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) { if (Hex_()) { auto match = Position::str(start, m_position); - auto bv = buildInt(std::hex, match); + auto bv = buildInt(16, match, true); m_match_stack.emplace_back(make_node(std::move(match), start.line, start.col, std::move(bv))); return true; } if (Binary_()) { auto match = Position::str(start, m_position); - int64_t temp_int = 0; - size_t pos = 0; - const auto end = match.length(); - - while ((pos < end) && (pos < (2 + sizeof(int) * 8))) { - temp_int <<= 1; - if (match[pos] == '1') { - temp_int += 1; - } - ++pos; - } - - Boxed_Value i = [&]()->Boxed_Value{ - if (match.length() <= sizeof(int) * 8) - { - return const_var(static_cast(temp_int)); - } else { - return const_var(temp_int); - } - }(); - - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(i))); + auto bv = buildInt(2, match, true); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); return true; } if (Float_()) { @@ -799,11 +771,11 @@ namespace chaiscript IntSuffix_(); auto match = Position::str(start, m_position); if (!match.empty() && (match[0] == '0')) { - auto bv = buildInt(std::oct, match); + auto bv = buildInt(8, match, false); m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); } else if (!match.empty()) { - auto bv = buildInt(std::dec, match); + auto bv = buildInt(10, match, false); m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); } else { return false; diff --git a/unittests/integer_literal_test.cpp b/unittests/integer_literal_test.cpp index ecaff8a..9a02c20 100644 --- a/unittests/integer_literal_test.cpp +++ b/unittests/integer_literal_test.cpp @@ -5,9 +5,10 @@ template bool test_literal(T val, const std::string &str) { + std::cout << "Comparing : " << val; chaiscript::ChaiScript chai; T val2 = chai.eval(str); - std::cout << "Comparing : " << val << " " << val2 << '\n'; + std::cout << " " << val2 << '\n'; return val == val2; } diff --git a/unittests/number_suffixes.chai b/unittests/number_suffixes.chai index 99ac03f..b8fd1f5 100644 --- a/unittests/number_suffixes.chai +++ b/unittests/number_suffixes.chai @@ -2,8 +2,8 @@ assert_equal(true, int_type.bare_equal(1.get_type_info())) assert_equal(true, unsigned_int_type.bare_equal(1u.get_type_info())) assert_equal(true, unsigned_long_type.bare_equal(1lu.get_type_info())) assert_equal(true, long_type.bare_equal(1l.get_type_info())) -assert_equal(true, int64_t_type.bare_equal(1ll.get_type_info())) -assert_equal(true, uint64_t_type.bare_equal(1ull.get_type_info())) +assert_equal(true, long_long_type.bare_equal(1ll.get_type_info())) +assert_equal(true, unsigned_long_long_type.bare_equal(1ull.get_type_info())) assert_equal(true, double_type.bare_equal(1.6.get_type_info())) assert_equal(true, float_type.bare_equal(1.6f.get_type_info())) From e221ceaa4c3fd8254ff730ec96e9010105936471 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 3 Oct 2015 17:11:03 -0600 Subject: [PATCH 25/45] Greatly simplify integer sizing code --- .../chaiscript/language/chaiscript_parser.hpp | 87 ++++--------------- 1 file changed, 17 insertions(+), 70 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index a05129a..c995f7d 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -648,87 +648,34 @@ namespace chaiscript } } - const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; - static_assert(sizeof(long) == sizeof(uint64_t) || sizeof(long) * 2 == sizeof(uint64_t), "Unexpected sizing of integer types"); - bool unsignedrequired = false; +// static_assert(sizeof(long) == sizeof(uint64_t) || sizeof(long) * 2 == sizeof(uint64_t), "Unexpected sizing of integer types"); try { auto u = std::stoll(val,nullptr,base); - if ((u >> (sizeof(int) * 8)) > 0) - { - //requires something bigger than int - long_ = true; + if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); } - if ((sizeof(long) < sizeof(uint64_t)) - && (u >> ((sizeof(uint64_t) - sizeof(long)) * 8)) > 0) - { - //requires something bigger than long - longlong_ = true; - } - - const size_t size = [&]()->size_t{ - if (longlong_) - { - return sizeof(int64_t) * 8; - } else if (long_) { - return sizeof(long) * 8; - } else { - return sizeof(int) * 8; - } - }(); - - if ( (u >> (size - 1)) > 0) - { - unsignedrequired = true; - } } catch (const std::out_of_range &) { - // it cannot fit in a signed long long... - std::cout << "forcing long long for '" << val << "'\n"; - unsignedrequired = true; + auto u = std::stoull(val,nullptr,base); - long_ = sizeof(unsigned long long) == sizeof(unsigned long); - longlong_ = sizeof(unsigned long long) != sizeof(unsigned long); - } - - if (unsignedrequired && !unsigned_) - { - if (base != 10) - { - // with bin, hex and oct we are happy to just make it unsigned - unsigned_ = true; + if (u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); } else { - // with decimal we must bump it up to the next size - if (long_) - { - longlong_ = true; - } else if (!long_ && !longlong_) { - long_ = true; - } - } - } - - if (unsigned_) - { - if (longlong_) - { - return const_var(stoull(val,nullptr,base)); - } else if (long_) { - return const_var(stoul(val,nullptr,base)); - } else { - return const_var(static_cast(stoul(val,nullptr,base))); - } - } else { - if (longlong_) - { - return const_var(stoll(val,nullptr,base)); - } else if (long_) { - return const_var(stol(val,nullptr,base)); - } else { - return const_var(stoi(val,nullptr,base)); + return const_var(static_cast(u)); } } } From d2cf12f94849c453927fbb840451158ace649207 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 3 Oct 2015 21:01:52 -0600 Subject: [PATCH 26/45] Add tests for binary literals --- .../chaiscript/language/chaiscript_parser.hpp | 12 +++++-- unittests/integer_literal_test.cpp | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index c995f7d..f582ab1 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -650,11 +650,14 @@ namespace chaiscript const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; -// static_assert(sizeof(long) == sizeof(uint64_t) || sizeof(long) * 2 == sizeof(uint64_t), "Unexpected sizing of integer types"); - try { auto u = std::stoll(val,nullptr,base); +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" +#endif + if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { @@ -669,6 +672,11 @@ namespace chaiscript return const_var(static_cast(u)); } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + } catch (const std::out_of_range &) { auto u = std::stoull(val,nullptr,base); diff --git a/unittests/integer_literal_test.cpp b/unittests/integer_literal_test.cpp index 9a02c20..58e954a 100644 --- a/unittests/integer_literal_test.cpp +++ b/unittests/integer_literal_test.cpp @@ -75,6 +75,39 @@ int main() && TEST_LITERAL(177777777777777777) && TEST_LITERAL(1777777777777777777) + && test_literal(0xF, "0b1111") + && test_literal(0xFF, "0b11111111") + && test_literal(0xFFF, "0b111111111111") + && test_literal(0xFFFF, "0b1111111111111111") + && test_literal(0xFFFFF, "0b11111111111111111111") + && test_literal(0xFFFFFF, "0b111111111111111111111111") + && test_literal(0xFFFFFFF, "0b1111111111111111111111111111") + && test_literal(0xFFFFFFFF, "0b11111111111111111111111111111111") + && test_literal(0xFFFFFFFFF, "0b111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFF, "0b1111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111111111111") + + && test_literal(0x7, "0b111") + && test_literal(0x7F, "0b1111111") + && test_literal(0x7FF, "0b11111111111") + && test_literal(0x7FFF, "0b111111111111111") + && test_literal(0x7FFFF, "0b1111111111111111111") + && test_literal(0x7FFFFF, "0b11111111111111111111111") + && test_literal(0x7FFFFFF, "0b111111111111111111111111111") + && test_literal(0x7FFFFFFF, "0b1111111111111111111111111111111") + && test_literal(0x7FFFFFFFF, "0b11111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFF, "0b111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFF, "0b1111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111111") ) { return EXIT_SUCCESS; From 14b3870efb4eb25a49d5af34636de6ab3060cb40 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sun, 4 Oct 2015 08:53:22 -0600 Subject: [PATCH 27/45] Fix integer overflow and bad numeric parses --- .../chaiscript/language/chaiscript_parser.hpp | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index f582ab1..57387d3 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -676,14 +676,19 @@ namespace chaiscript #pragma GCC diagnostic pop #endif - } catch (const std::out_of_range &) { - auto u = std::stoull(val,nullptr,base); + // too big to be signed + try { + auto u = std::stoull(val,nullptr,base); - if (u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { - return const_var(static_cast(u)); - } else { - return const_var(static_cast(u)); + if (u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); + } + } catch (const std::out_of_range &) { + // it's just simply too big + return const_var(std::numeric_limits::max()); } } } @@ -703,39 +708,44 @@ namespace chaiscript } else { const auto start = m_position; if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) { - if (Hex_()) { - auto match = Position::str(start, m_position); - auto bv = buildInt(16, match, true); - m_match_stack.emplace_back(make_node(std::move(match), start.line, start.col, std::move(bv))); - return true; - } + try { + if (Hex_()) { + auto match = Position::str(start, m_position); + auto bv = buildInt(16, match, true); + m_match_stack.emplace_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + return true; + } - if (Binary_()) { - auto match = Position::str(start, m_position); - auto bv = buildInt(2, match, true); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); - return true; - } - if (Float_()) { - auto match = Position::str(start, m_position); - auto bv = buildFloat(match); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); - return true; - } - else { - IntSuffix_(); - auto match = Position::str(start, m_position); - if (!match.empty() && (match[0] == '0')) { - auto bv = buildInt(8, match, false); + if (Binary_()) { + auto match = Position::str(start, m_position); + auto bv = buildInt(2, match, true); m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + return true; } - else if (!match.empty()) { - auto bv = buildInt(10, match, false); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); - } else { - return false; + if (Float_()) { + auto match = Position::str(start, m_position); + auto bv = buildFloat(match); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + return true; } - return true; + else { + IntSuffix_(); + auto match = Position::str(start, m_position); + if (!match.empty() && (match[0] == '0')) { + auto bv = buildInt(8, match, false); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + } + else if (!match.empty()) { + auto bv = buildInt(10, match, false); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + } else { + return false; + } + return true; + } + } catch (const std::invalid_argument &) { + // error parsing number passed in to buildFloat/buildInt + return false; } } else { From 1add4c4b0f05a355158f2b2e0bfc36329bc9c8e8 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sun, 4 Oct 2015 14:32:23 -0600 Subject: [PATCH 28/45] Fix issues with integer parsing on MSVC See #212 --- .../chaiscript/language/chaiscript_parser.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 57387d3..11a2d04 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -17,9 +17,19 @@ #include + #include "../dispatchkit/boxed_value.hpp" #include "chaiscript_common.hpp" + +#if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min) +#pragma push_macro("max") // Why Microsoft? why? This is worse than bad +#undef max +#pragma push_macro("min") +#undef min +#endif + + namespace chaiscript { /// \brief Classes and functions used during the parsing process. @@ -912,6 +922,8 @@ namespace chaiscript { } + Char_Parser &operator=(const Char_Parser &) = delete; + ~Char_Parser(){ if (is_octal) { process_octal(); @@ -2393,5 +2405,12 @@ namespace chaiscript } } + +#ifdef CHAISCRIPT_MSVC +#pragma pop_macro("min") +#pragma pop_macro("max") +#endif + + #endif /* CHAISCRIPT_PARSER_HPP_ */ From 3e62a99f821433f690ee69a0ffa00b28ff70208f Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 7 Oct 2015 09:55:15 -0600 Subject: [PATCH 29/45] Add factory example with scripted callbacks --- CMakeLists.txt | 2 + samples/factory.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 samples/factory.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 07ae809..417f74a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,6 +259,8 @@ if(BUILD_SAMPLES) target_link_libraries(memory_leak_test ${LIBS}) add_executable(inheritance samples/inheritance.cpp) target_link_libraries(inheritance ${LIBS}) + add_executable(factory samples/factory.cpp) + target_link_libraries(factory ${LIBS}) add_executable(fun_call_performance samples/fun_call_performance.cpp) target_link_libraries(fun_call_performance ${LIBS}) endif() diff --git a/samples/factory.cpp b/samples/factory.cpp new file mode 100644 index 0000000..ac8eb47 --- /dev/null +++ b/samples/factory.cpp @@ -0,0 +1,103 @@ +#include +#include + +class Entity +{ + public: + int width; + int height; + int x; + int y; + std::string name; + + std::function updater; + + Entity(const int t_width, const int t_height, const int t_x, const int t_y, std::string t_name) + : width(t_width), height(t_height), x(t_x), y(t_y), name(std::move(t_name)) + { + } +}; + +class Factory +{ + public: + // we may as well pass the parameters for the entity to the factory method, this does the initialization + // in one step. + Entity *make_entity(const int width, const int height, const int x, const int y, const std::string &name) + { + auto entity = entities.insert({name, Entity{width, height, x, y, name}}); + return &(entity.first->second); + } + + Entity *get_entity(const std::string &name) + { + return &entities.at(name); + } + + + // loop over all entities and all their updater function (if it exists) + void update_entities() + { + for (auto &entity : entities) + { + if (entity.second.updater) { + entity.second.updater(entity.second); + } + } + } + + + private: + // we cannot store the entities in a std::vector if we want to return a pointer to them, + // because a vector automatically resizing itself can invalidate the pointer that was returned. + // using a map guarantees that the memory assigned to the entity will never change, plus + // lets us easily look up an entity by name + std::map entities; +}; + +int main() +{ + chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + + chai.add(chaiscript::fun(&Entity::width), "width"); + chai.add(chaiscript::fun(&Entity::height), "height"); + chai.add(chaiscript::fun(&Entity::x), "x"); + chai.add(chaiscript::fun(&Entity::y), "y"); + chai.add(chaiscript::fun(&Entity::name), "name"); + chai.add(chaiscript::fun(&Entity::updater), "updater"); + chai.add(chaiscript::user_type(), "Entity"); // this isn't strictly necessary but makes error messages nicer + + chai.add(chaiscript::fun(&Factory::make_entity), "make_entity"); + chai.add(chaiscript::fun(&Factory::get_entity), "get_entity"); + chai.add(chaiscript::fun(&Factory::update_entities), "update_entities"); + chai.add(chaiscript::user_type(), "Factory"); // this isn't strictly necessary but makes error messages nicer + + + Factory f; + chai.add(chaiscript::var(&f), "f"); + + std::string script = R""( + f.make_entity(10,10,1,1,"entity1").updater = fun(e){ e.x += 1; e.y += 1 }; + f.make_entity(10,10,10,10,"entity2").updater = fun(e){ e.x += 2; e.y += 2 }; + f.make_entity(10,10,20,20,"entity3"); + + print(f.get_entity("entity1").x == 1) + print(f.get_entity("entity2").x == 10) + print(f.get_entity("entity3").x == 20) + + f.update_entities(); // this runs the function objects we set in the previous lines + // we should now see the updated values + + print(f.get_entity("entity1").x == 2) + print(f.get_entity("entity2").x == 12) + print(f.get_entity("entity3").x == 20) // this one has no updater, so it stays the same + )""; + + + chai.eval(script); + + + +} + + From 84e2d449b9e51863db0edbe305feceb6a331e38a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 15:02:49 -0600 Subject: [PATCH 30/45] Support `default` case in the non-last position --- .../chaiscript/language/chaiscript_eval.hpp | 2 +- .../chaiscript/language/chaiscript_parser.hpp | 2 ++ unittests/switch_default_2.chai | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 unittests/switch_default_2.chai diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index f6fde40..aa01de9 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -950,7 +950,7 @@ namespace chaiscript } else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { this->children[currentCase]->eval(t_ss); - breaking = true; + hasMatched = true; } } catch (detail::Break_Loop &) { diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 11a2d04..d012e26 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1807,6 +1807,8 @@ namespace chaiscript build_match(prev_stack_top); } else if (Keyword("default")) { + retval = true; + while (Eol()) {} if (!Block()) { diff --git a/unittests/switch_default_2.chai b/unittests/switch_default_2.chai new file mode 100644 index 0000000..5082ab6 --- /dev/null +++ b/unittests/switch_default_2.chai @@ -0,0 +1,18 @@ +var total = 0; + +switch(2) { + case (1) { + total += 1; + } + default { + total += 16; + } + case (3) { + total += 4; + } + case (4) { + total += 8; + } +} + +assert_equal(total, 28) From b11ebf9e8fe25db5d3ded4b834f3263be66ba978 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 21:13:17 -0600 Subject: [PATCH 31/45] Add failing test for vector assignment operations --- unittests/vector_assignment.chai | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 unittests/vector_assignment.chai diff --git a/unittests/vector_assignment.chai b/unittests/vector_assignment.chai new file mode 100644 index 0000000..f3abc04 --- /dev/null +++ b/unittests/vector_assignment.chai @@ -0,0 +1,16 @@ +var v = [] +v.push_back(3.4, 1, "bob"); + +assert_true(v[0] == 3.4) +assert_true(v[1] == 1) +assert_true(v[2] == "bob") + +v[0] = 2.9 +v[1] = 3 +v[2] = "tom" + +assert_true(v[0] == 2.9) +assert_true(v[1] == 3) +assert_true(v[2] == "tom") + + From 36765df3c0f6a002536e035994d96111a2a20cb7 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 21:20:12 -0600 Subject: [PATCH 32/45] Fix vector element assignment issues --- include/chaiscript/dispatchkit/bootstrap.hpp | 1 + include/chaiscript/dispatchkit/boxed_value.hpp | 7 +++++++ include/chaiscript/dispatchkit/dispatchkit.hpp | 8 +------- include/chaiscript/language/chaiscript_prelude.chai | 10 +++++----- unittests/vector_assignment.chai | 4 +++- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index ef5a94b..7e0fd6b 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -470,6 +470,7 @@ namespace chaiscript m->add(fun(&Boxed_Value::is_type), "is_type"); m->add(fun(&Boxed_Value::get_attr), "get_var_attr"); m->add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); + m->add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); m->add(fun(&Boxed_Value::get_type_info), "get_type_info"); m->add(user_type(), "Type_Info"); diff --git a/include/chaiscript/dispatchkit/boxed_value.hpp b/include/chaiscript/dispatchkit/boxed_value.hpp index ae21382..1c2ebe7 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -297,6 +297,13 @@ namespace chaiscript return *this; } + Boxed_Value &clone_attrs(const Boxed_Value &t_obj) + { + copy_attrs(t_obj); + reset_return_value(); + return *this; + } + /// \returns true if the two Boxed_Values share the same internal type static bool type_match(const Boxed_Value &l, const Boxed_Value &r) CHAISCRIPT_NOEXCEPT diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 806abae..305de99 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1031,13 +1031,7 @@ namespace chaiscript uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); const auto funs = get_function(t_name, loc); if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); - Boxed_Value bv = dispatch::dispatch(*funs.second, params, m_conversions); - // the result of a clone is never to be marked as a return_value - // \todo see if we can eliminate this comparison - if (t_name == "clone") { - bv.reset_return_value(); - } - return bv; + return dispatch::dispatch(*funs.second, params, m_conversions); } diff --git a/include/chaiscript/language/chaiscript_prelude.chai b/include/chaiscript/language/chaiscript_prelude.chai index acb8407..55514bb 100644 --- a/include/chaiscript/language/chaiscript_prelude.chai +++ b/include/chaiscript/language/chaiscript_prelude.chai @@ -41,25 +41,25 @@ def new(x) { } def clone(double x) { - double(x).copy_var_attrs(x) + double(x).clone_var_attrs(x) } def clone(string x) { - string(x).copy_var_attrs(x) + string(x).clone_var_attrs(x) } def clone(vector x) { - vector(x).copy_var_attrs(x) + vector(x).clone_var_attrs(x) } def clone(int x) { - int(x).copy_var_attrs(x) + int(x).clone_var_attrs(x) } def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) { - eval(type_name(x))(x).copy_var_attrs(x); + eval(type_name(x))(x).clone_var_attrs(x); } diff --git a/unittests/vector_assignment.chai b/unittests/vector_assignment.chai index f3abc04..8646105 100644 --- a/unittests/vector_assignment.chai +++ b/unittests/vector_assignment.chai @@ -1,5 +1,7 @@ var v = [] -v.push_back(3.4, 1, "bob"); +v.push_back(3.4); +v.push_back(1); +v.push_back("bob"); assert_true(v[0] == 3.4) assert_true(v[1] == 1) From 84554ed0a501d8b586554f9050487b94955972f9 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 21:32:16 -0600 Subject: [PATCH 33/45] Add another vector assignment test --- unittests/vector_assignment_3.chai | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 unittests/vector_assignment_3.chai diff --git a/unittests/vector_assignment_3.chai b/unittests/vector_assignment_3.chai new file mode 100644 index 0000000..61d4b58 --- /dev/null +++ b/unittests/vector_assignment_3.chai @@ -0,0 +1,5 @@ + +var v = [] +v.push_back(int(1)); +v[0] = 3; + From ad69bf7d38dc3511a0ffbb009e0b3be766c24df9 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 21:42:25 -0600 Subject: [PATCH 34/45] Get vector push_back_ref working as expected --- include/chaiscript/dispatchkit/bootstrap.hpp | 1 + include/chaiscript/dispatchkit/bootstrap_stl.hpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 7e0fd6b..c063ca8 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -467,6 +467,7 @@ namespace chaiscript m->add(fun(&Boxed_Value::is_ref), "is_var_reference"); m->add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); m->add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); + m->add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); m->add(fun(&Boxed_Value::is_type), "is_type"); m->add(fun(&Boxed_Value::get_attr), "get_var_attr"); m->add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 186ccba..8b55981 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -335,6 +335,7 @@ namespace chaiscript "def push_back(" + type + " container, x)\n" "{ \n" " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" " container.push_back_ref(x) \n" " } else { \n" " container.push_back_ref(clone(x)); \n" @@ -375,6 +376,7 @@ namespace chaiscript "def push_front(" + type + " container, x)\n" "{ \n" " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" " container.push_front_ref(x) \n" " } else { \n" " container.push_front_ref(clone(x)); \n" From 5aecb7f17b42928b1780986419423532258257c7 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 21:59:46 -0600 Subject: [PATCH 35/45] Add boolean comparison tests #217 --- unittests/bool_comparisons.chai | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 unittests/bool_comparisons.chai diff --git a/unittests/bool_comparisons.chai b/unittests/bool_comparisons.chai new file mode 100644 index 0000000..6fdea7a --- /dev/null +++ b/unittests/bool_comparisons.chai @@ -0,0 +1,12 @@ + +assert_true(true == true) +assert_false(true == false) +assert_true(true != false) +assert_true(false != true) +assert_true(false || true) +assert_true(true || false) +assert_false(true && false) +assert_false(false && true) +assert_true(!false) +assert_false(!true) + From 985b62705f448accd2843665e7674a5905ec3579 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 15 Oct 2015 22:06:06 -0600 Subject: [PATCH 36/45] Add support for != bools closes #217 --- include/chaiscript/dispatchkit/bootstrap.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index c063ca8..ac8c5dc 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -494,6 +494,7 @@ namespace chaiscript basic_constructors("bool", m); operators::assign(m); operators::equal(m); + operators::not_equal(m); m->add(fun([](const std::string &s) -> std::string { return s; }), "to_string"); m->add(fun(&Bootstrap::bool_to_string), "to_string"); From 3a675bf379cc38c4b7c3902ccd603b9272cdb910 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 16 Oct 2015 18:21:49 -0600 Subject: [PATCH 37/45] Add config option for compiling with gprof output --- CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07ae809..0b67b72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,13 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(LINKER_FLAGS "${LINKER_FLAGS} -flto") endif() + option(GPROF_OUTPUT "Generate profile data" FALSE) + if (GPROF_OUTPUT) + add_definitions(-pg) + set(LINKER_FLAGS "${LINKER_FLAGS} -pg") + endif() + + option(PROFILE_GENERATE "Generate profile data" FALSE) if (PROFILE_GENERATE) add_definitions(-fprofile-generate) From 38b98c55ccd97ef38d941ab019c1aace5e33e9f2 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 16 Oct 2015 18:37:02 -0600 Subject: [PATCH 38/45] Add test for dynamic object option explicit --- .../dynamic_object_dynamic_attrs_explicit.chai | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 unittests/dynamic_object_dynamic_attrs_explicit.chai diff --git a/unittests/dynamic_object_dynamic_attrs_explicit.chai b/unittests/dynamic_object_dynamic_attrs_explicit.chai new file mode 100644 index 0000000..b26b610 --- /dev/null +++ b/unittests/dynamic_object_dynamic_attrs_explicit.chai @@ -0,0 +1,14 @@ + +class MyClass { + def MyClass() + { + this.set_explicit(true); + } +}; + +var o = MyClass(); + +assert_true(o.is_explicit()); + +assert_throws(fun[o](){o.x = 2}) + From 882cbf2dfbe0bd50a03c8a5056754f18d1d63300 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 16 Oct 2015 18:47:26 -0600 Subject: [PATCH 39/45] Add option explicit code, but don't throw yet Work towards #218 --- include/chaiscript/dispatchkit/bootstrap.hpp | 2 ++ include/chaiscript/dispatchkit/dynamic_object.hpp | 15 +++++++++++++-- .../dynamic_object_dynamic_attrs_explicit.chai | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index ac8c5dc..7e3a301 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -440,6 +440,8 @@ namespace chaiscript m->add(constructor(), "Dynamic_Object"); m->add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); m->add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); + m->add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit"); + m->add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 9ea827c..7df1abf 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -28,14 +28,24 @@ namespace chaiscript { public: Dynamic_Object(std::string t_type_name) - : m_type_name(std::move(t_type_name)) + : m_type_name(std::move(t_type_name)), m_option_explicit(false) { } - Dynamic_Object() : m_type_name("") + Dynamic_Object() : m_type_name(""), m_option_explicit(false) { } + bool is_explicit() const + { + return m_option_explicit; + } + + void set_explicit(const bool t_explicit) + { + m_option_explicit = t_explicit; + } + std::string get_type_name() const { return m_type_name; @@ -85,6 +95,7 @@ namespace chaiscript private: std::string m_type_name; + bool m_option_explicit; std::map m_attrs; }; diff --git a/unittests/dynamic_object_dynamic_attrs_explicit.chai b/unittests/dynamic_object_dynamic_attrs_explicit.chai index b26b610..b37dc0f 100644 --- a/unittests/dynamic_object_dynamic_attrs_explicit.chai +++ b/unittests/dynamic_object_dynamic_attrs_explicit.chai @@ -10,5 +10,5 @@ var o = MyClass(); assert_true(o.is_explicit()); -assert_throws(fun[o](){o.x = 2}) +assert_throws("error", fun[o](){o.x = 2}) From 7ba7b81a5cd6ded8788d94040d9d66322aecb109 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 16 Oct 2015 21:41:54 -0600 Subject: [PATCH 40/45] Implement option explicit for dynamic objects. Closes #218 --- cheatsheet.md | 12 ++++++++++++ include/chaiscript/dispatchkit/dispatchkit.hpp | 17 +++++++++++------ .../chaiscript/dispatchkit/dynamic_object.hpp | 18 ++++++++++++++++++ .../chaiscript/dispatchkit/proxy_functions.hpp | 8 ++++++++ 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cheatsheet.md b/cheatsheet.md index 9ff4ad4..841cb92 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -354,6 +354,18 @@ o.f = fun(y) { print(this.x + y); } o.f(10); // prints 13 ``` +### Option Explicit + +If you want to disable dynamic parameter definitions, you can `set_explicit`. + +``` +class My_Class { + def My_Class() { + this.set_explicit(true); + this.x = 2; // this would fail with explicit set to true + } +}; + ## method_missing A function of the signature `method_missing(object, name, param1, param2, param3)` will be called if an appropriate diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 305de99..aa09486 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1002,12 +1002,17 @@ namespace chaiscript }(); if (!functions.empty()) { - if (is_no_param) { - std::vector tmp_params(params); - tmp_params.insert(tmp_params.begin() + 1, var(t_name)); - return do_attribute_call(2, tmp_params, functions, m_conversions); - } else { - return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, m_conversions); + try { + if (is_no_param) { + std::vector tmp_params(params); + tmp_params.insert(tmp_params.begin() + 1, var(t_name)); + return do_attribute_call(2, tmp_params, functions, m_conversions); + } else { + return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, m_conversions); + } + } catch (const dispatch::option_explicit_set &e) { + throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end()), + e.what()); } } diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 7df1abf..eaabf9b 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -24,6 +24,16 @@ namespace chaiscript { namespace dispatch { + struct option_explicit_set : std::runtime_error { + option_explicit_set(const std::string &t_param_name) + : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist") + { + + } + + virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {} + }; + class Dynamic_Object { public: @@ -79,11 +89,19 @@ namespace chaiscript Boxed_Value &method_missing(const std::string &t_method_name) { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + return get_attr(t_method_name); } const Boxed_Value &method_missing(const std::string &t_method_name) const { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + return get_attr(t_method_name); } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index d9da2e6..6214b4d 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -760,6 +760,14 @@ namespace chaiscript { } + dispatch_error(std::vector t_parameters, + std::vector t_functions, + const std::string &t_desc) + : std::runtime_error(t_desc), parameters(std::move(t_parameters)), functions(std::move(t_functions)) + { + } + + dispatch_error(const dispatch_error &) = default; virtual ~dispatch_error() CHAISCRIPT_NOEXCEPT {} From 09748275db98e2d75964307e78b9c20a72beb81b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 17 Oct 2015 09:22:13 -0600 Subject: [PATCH 41/45] Fix warnings from clang --- .decent_ci-Linux.yaml | 2 +- .../chaiscript/dispatchkit/dynamic_object.hpp | 2 ++ .../chaiscript/language/chaiscript_parser.hpp | 17 ++++++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.decent_ci-Linux.yaml b/.decent_ci-Linux.yaml index 24990bd..246241a 100644 --- a/.decent_ci-Linux.yaml +++ b/.decent_ci-Linux.yaml @@ -12,7 +12,7 @@ compilers: build_tag: AddressSanitizer version: "3.6" skip_packaging: true - cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON + cmake_extra_flags: -DRUN_FUZZY_TESTS:BOOL=TRUE -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON - name: "clang" build_tag: ThreadSanitizer version: "3.6" diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index eaabf9b..1e79eea 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -31,6 +31,8 @@ namespace chaiscript } + option_explicit_set(const option_explicit_set &) = default; + virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {} }; diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index d012e26..a9b9311 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -660,14 +660,16 @@ namespace chaiscript const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; - try { - auto u = std::stoll(val,nullptr,base); - #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wtautological-compare" #endif + try { + auto u = std::stoll(val,nullptr,base); + + if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { @@ -682,10 +684,6 @@ namespace chaiscript return const_var(static_cast(u)); } -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - } catch (const std::out_of_range &) { // too big to be signed try { @@ -701,6 +699,11 @@ namespace chaiscript return const_var(std::numeric_limits::max()); } } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } template From bc388e59da50c56cb813927b3b2e0ba2e1127634 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 17 Oct 2015 09:23:05 -0600 Subject: [PATCH 42/45] Fix style warning from cppcheck --- samples/test_num_exceptions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/test_num_exceptions.cpp b/samples/test_num_exceptions.cpp index ba97f7a..36bbb75 100644 --- a/samples/test_num_exceptions.cpp +++ b/samples/test_num_exceptions.cpp @@ -23,7 +23,7 @@ int main( int /*argc*/ , char * /*argv*/[] ) { ch.eval( script ); } - catch ( std::exception e ) + catch ( const std::exception &e ) { printf( " >>> Exception thrown: %s \n" , e.what( ) ); } From 8496a860432615cbee02b0ac28bc100dae4f1a06 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 20 Oct 2015 18:14:42 -0600 Subject: [PATCH 43/45] Use a bitset instead of bools for type_info flags --- include/chaiscript/dispatchkit/type_info.hpp | 38 ++++++++++---------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 0d5c5f3..105440b 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace chaiscript { @@ -32,17 +33,17 @@ namespace chaiscript CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void, bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti) : m_type_info(t_ti), m_bare_type_info(t_bare_ti), - m_is_const(t_is_const), m_is_reference(t_is_reference), m_is_pointer(t_is_pointer), - m_is_void(t_is_void), m_is_arithmetic(t_is_arithmetic), - m_is_undef(false) + m_flags((t_is_const << is_const_flag) + + (t_is_reference << is_reference_flag) + + (t_is_pointer << is_pointer_flag) + + (t_is_void << is_void_flag) + + (t_is_arithmetic << is_arithmetic_flag)) { } CHAISCRIPT_CONSTEXPR Type_Info() : m_type_info(nullptr), m_bare_type_info(nullptr), - m_is_const(false), m_is_reference(false), m_is_pointer(false), - m_is_void(false), m_is_arithmetic(false), - m_is_undef(true) + m_flags(1 << is_undef_flag) { } @@ -83,12 +84,12 @@ namespace chaiscript && (*m_bare_type_info) == ti; } - CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_is_const; } - CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_is_reference; } - CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_is_void; } - CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_is_arithmetic; } - CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_is_undef; } - CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_is_pointer; } + CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_flags[is_const_flag]; } + CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_flags[is_reference_flag]; } + CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_flags[is_void_flag]; } + CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_flags[is_arithmetic_flag]; } + CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_flags[is_undef_flag]; } + CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_flags[is_pointer_flag]; } std::string name() const { @@ -118,12 +119,13 @@ namespace chaiscript private: const std::type_info *m_type_info; const std::type_info *m_bare_type_info; - bool m_is_const; - bool m_is_reference; - bool m_is_pointer; - bool m_is_void; - bool m_is_arithmetic; - bool m_is_undef; + std::bitset<6> m_flags; + static const int is_const_flag = 0; + static const int is_reference_flag = 1; + static const int is_pointer_flag = 2; + static const int is_void_flag = 3; + static const int is_arithmetic_flag = 4; + static const int is_undef_flag = 5; }; namespace detail From c9a5bf6f83ce60be005dd720375e30b98481f87d Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 20 Oct 2015 18:19:03 -0600 Subject: [PATCH 44/45] fix warning from GCC for unknown flag --- include/chaiscript/chaiscript_defines.hpp | 6 +++++- include/chaiscript/language/chaiscript_parser.hpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 5c54051..c2ba4ae 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -39,7 +39,11 @@ #define CHAISCRIPT_GCC_4_6 #endif -#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(__llvm__) +#if defined(__llvm__) +#define CHAISCRIPT_CLANG +#endif + +#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(CHAISCRIPT_CLANG) #define CHAISCRIPT_OVERRIDE override #else #define CHAISCRIPT_OVERRIDE diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index a9b9311..37b717f 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -663,7 +663,11 @@ namespace chaiscript #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" + +#ifdef CHAISCRIPT_CLANG #pragma GCC diagnostic ignored "-Wtautological-compare" +#endif + #endif try { From 40b1549b3bdd53503bf4170465d2df132185aabe Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 21 Oct 2015 09:30:22 -0600 Subject: [PATCH 45/45] Fix use of broken bitset implementation in g++ --- include/chaiscript/dispatchkit/type_info.hpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 105440b..5be3a2e 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -11,7 +11,6 @@ #include #include #include -#include namespace chaiscript { @@ -84,12 +83,12 @@ namespace chaiscript && (*m_bare_type_info) == ti; } - CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_flags[is_const_flag]; } - CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_flags[is_reference_flag]; } - CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_flags[is_void_flag]; } - CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_flags[is_arithmetic_flag]; } - CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_flags[is_undef_flag]; } - CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_flags[is_pointer_flag]; } + CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_const_flag); } + CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_reference_flag); } + CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_void_flag); } + CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_arithmetic_flag); } + CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_undef_flag); } + CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_pointer_flag); } std::string name() const { @@ -119,7 +118,7 @@ namespace chaiscript private: const std::type_info *m_type_info; const std::type_info *m_bare_type_info; - std::bitset<6> m_flags; + unsigned int m_flags; static const int is_const_flag = 0; static const int is_reference_flag = 1; static const int is_pointer_flag = 2;