diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 7c5616f8..ec6129b0 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -580,6 +580,8 @@ namespace chaiscript m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); } ), "find_last_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); } ), "find_last_not_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); } ), "find_first_not_of"); + + m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { return (*s += c); } ), "+="); m.add(fun([](String *s) { s->clear(); } ), "clear"); m.add(fun([](const String *s) { return s->empty(); } ), "empty"); @@ -599,7 +601,7 @@ namespace chaiscript m.add(user_type(), type); m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); - m.add(fun(&FutureType::get), "get"); + m.add(fun([](FutureType &t) { return t.get(); }), "get"); m.add(fun(&FutureType::wait), "wait"); } } diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index 3e96a03e..6f295807 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -375,6 +375,10 @@ namespace chaiscript validate_boxed_number(bv); } + static Boxed_Value clone(const Boxed_Value &t_bv) { + return Boxed_Number(t_bv).get_as(t_bv.get_type_info()).bv; + } + static bool is_floating_point(const Boxed_Value &t_bv) { const Type_Info &inp_ = t_bv.get_type_info(); diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 359e18ba..3df99d7b 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -88,7 +88,7 @@ namespace chaiscript /// Types of AST nodes available to the parser and eval - enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl, + enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl, Assign_Decl, Array_Call, Dot_Access, Lambda, Block, Scopeless_Block, Def, While, If, For, Ranged_For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range, Inline_Range, Try, Catch, Finally, Method, Attr_Decl, @@ -103,7 +103,7 @@ namespace chaiscript { /// Helper lookup to get the name of each node type constexpr const char *ast_node_type_to_string(AST_Node_Type ast_node_type) noexcept { - constexpr const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", + constexpr const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 781615c9..2c72dbe8 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -87,6 +87,24 @@ namespace chaiscript return std::move(rv.retval); } } + + inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss) + { + if (!incoming.is_return_value()) + { + if (incoming.get_type_info().is_arithmetic()) { + return Boxed_Number::clone(incoming); + } else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) { + return Boxed_Value(*static_cast(incoming.get_const_ptr())); + } else { + std::array params{std::move(incoming)}; + return t_ss->call_function("clone", t_loc, Function_Params{params}, t_ss.conversions()); + } + } else { + incoming.reset_return_value(); + return incoming; + } + } } template @@ -470,11 +488,7 @@ namespace chaiscript params[0].reset_return_value(); return params[1]; } else { - if (!params[1].is_return_value()) - { - params[1] = t_ss->call_function("clone", m_clone_loc, Function_Params{¶ms[1], std::end(params)}, t_ss.conversions()); - } - params[1].reset_return_value(); + params[1] = detail::clone_if_necessary(std::move(params[1]), m_clone_loc, t_ss); } } @@ -553,6 +567,27 @@ namespace chaiscript } }; + template + struct Assign_Decl_AST_Node final : AST_Node_Impl { + Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Assign_Decl, std::move(t_loc), std::move(t_children)) { } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const std::string &idname = this->children[0]->text; + + try { + Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss)); + bv.reset_return_value(); + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); + } + } + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; + }; + template struct Array_Call_AST_Node final : AST_Node_Impl { @@ -1070,12 +1105,7 @@ namespace chaiscript if (!this->children.empty()) { vec.reserve(this->children[0]->children.size()); for (const auto &child : this->children[0]->children) { - if (auto obj = child->eval(t_ss); - !obj.is_return_value()) { - vec.push_back(t_ss->call_function("clone", m_loc, Function_Params{obj}, t_ss.conversions())); - } else { - vec.push_back(std::move(obj)); - } + vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss)); } } return const_var(std::move(vec)); @@ -1100,12 +1130,8 @@ namespace chaiscript std::map retval; for (const auto &child : this->children[0]->children) { - auto obj = child->children[1]->eval(t_ss); - if (!obj.is_return_value()) { - obj = t_ss->call_function("clone", m_loc, Function_Params{obj}, t_ss.conversions()); - } - - retval[t_ss->boxed_cast(child->children[0]->eval(t_ss))] = std::move(obj); + retval.insert(std::make_pair(t_ss->boxed_cast(child->children[0]->eval(t_ss)), + detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss))); } return const_var(std::move(retval)); diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index 74fa3803..e9cdbf04 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -97,7 +97,7 @@ namespace chaiscript { template bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) noexcept { - if (node.identifier == AST_Node_Type::Var_Decl) { + if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl) { return true; } @@ -208,6 +208,27 @@ namespace chaiscript { } }; + struct Assign_Decl { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if ((node->identifier == AST_Node_Type::Equation) + && node->text == "=" + && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Var_Decl + ) + { + std::vector> new_children; + new_children.push_back(std::move(node->children[0]->children[0])); + new_children.push_back(std::move(node->children[1])); + return chaiscript::make_unique, eval::Assign_Decl_AST_Node>(node->text, + node->location, std::move(new_children) ); + } + + return node; + } + }; + + struct If { template auto optimize(eval::AST_Node_Impl_Ptr node) { @@ -440,7 +461,7 @@ namespace chaiscript { }; typedef Optimizer Optimizer_Default; + optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop, optimizer::Assign_Decl> Optimizer_Default; } } diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 228c1aa0..392e2ce8 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1030,7 +1030,7 @@ namespace chaiscript int in_interpolation = 0; bool in_quote = false; - while (m_position.has_more() && ((*m_position != '\"') || ((*m_position == '\"') && (in_interpolation > 0)) || ((*m_position == '\"') && (prev_char == '\\')))) { + while (m_position.has_more() && ((*m_position != '\"') || (in_interpolation > 0) || (prev_char == '\\'))) { if (!Eol_()) { if (prev_char == '$' && *m_position == '{') { @@ -1328,7 +1328,7 @@ namespace chaiscript char prev_char = *m_position; ++m_position; - while (m_position.has_more() && ((*m_position != '\'') || ((*m_position == '\'') && (prev_char == '\\')))) { + while (m_position.has_more() && ((*m_position != '\'') || (prev_char == '\\'))) { if (!Eol_()) { if (prev_char == '\\') { prev_char = 0; diff --git a/include/chaiscript/language/chaiscript_posix.hpp b/include/chaiscript/language/chaiscript_posix.hpp index eb2ea250..87b66881 100644 --- a/include/chaiscript/language/chaiscript_posix.hpp +++ b/include/chaiscript/language/chaiscript_posix.hpp @@ -41,7 +41,7 @@ namespace chaiscript struct DLSym { DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str()))) + : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) { if (!m_symbol) { @@ -49,19 +49,6 @@ namespace chaiscript } } - static T cast_symbol(void *p) noexcept - { - union cast_union - { - T func_ptr; - void *in_ptr; - }; - - cast_union c; - c.in_ptr = p; - return c.func_ptr; - } - T m_symbol; }; diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index bf88f0ff..d227d85e 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -50,7 +50,7 @@ class JSON private: - using Data = std::variant, std::vector, std::string, double, int, bool>; + using Data = std::variant, std::vector, std::string, double, int64_t, bool>; struct Internal { @@ -66,7 +66,7 @@ class JSON case Class::Array: return std::vector{}; case Class::String: return std::string{}; case Class::Floating: return double{}; - case Class::Integral: return int{}; + case Class::Integral: return int64_t{}; case Class::Boolean: return bool{}; } throw std::runtime_error("unknown type"); @@ -137,7 +137,6 @@ class JSON return std::get_if(Class::Boolean)>(&d); } - Data d; }; @@ -192,7 +191,7 @@ class JSON explicit JSON( T b, typename enable_if::value>::type* = nullptr ) noexcept : internal( static_cast(b) ) {} template - explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) noexcept : internal( static_cast(i) ) {} + explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) noexcept : internal( static_cast(i) ) {} template explicit JSON( T f, typename enable_if::value>::type* = nullptr ) noexcept : internal( static_cast(f) ) {} @@ -281,10 +280,10 @@ class JSON [](){ return double{}; } ); } - int to_int() const noexcept { + int64_t to_int() const noexcept { return internal.visit_or( [](const auto &o){ return o; }, - [](){ return int{}; } + [](){ return int64_t{}; } ); } bool to_bool() const noexcept { @@ -498,7 +497,8 @@ struct JSONParser { char c = '\0'; bool isDouble = false; bool isNegative = false; - long exp = 0; + int64_t exp = 0; + bool isExpNegative = false; if( offset < str.size() && str.at(offset) == '-' ) { isNegative = true; ++offset; @@ -517,7 +517,7 @@ struct JSONParser { if( offset < str.size() && (c == 'E' || c == 'e' )) { c = str.at(offset++); if( c == '-' ) { - exp_str += '-'; + isExpNegative = true; } else if( c == '+' ) { // do nothing } else { @@ -533,9 +533,9 @@ struct JSONParser { } else { break; -} + } } - exp = chaiscript::parse_num( exp_str ); + exp = chaiscript::parse_num( exp_str ) * (isExpNegative?-1:1); } else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); @@ -546,9 +546,9 @@ struct JSONParser { return JSON((isNegative?-1:1) * chaiscript::parse_num( val ) * std::pow( 10, exp )); } else { if( !exp_str.empty() ) { - return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); + return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); } else { - return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); + return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); } } } diff --git a/performance_tests/create_variables.chai b/performance_tests/create_variables.chai new file mode 100644 index 00000000..e63e1a46 --- /dev/null +++ b/performance_tests/create_variables.chai @@ -0,0 +1,11 @@ +def var_test(int n) +{ + for (var i = 0; i < n; ++i) { + var j = 0 + } +} + +var n = 500000 +var_test(n) // takes 2.6 s + + diff --git a/readme.md b/readme.md index 8bf4deec..8323a128 100644 --- a/readme.md +++ b/readme.md @@ -25,7 +25,7 @@ Release under the BSD license, see "license.txt" for details. Introduction ============ -[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Gitter](https://badges.gitter.im/JoinChat.svg)](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ChaiScript is one of the only embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development diff --git a/samples/example.cpp b/samples/example.cpp index 9a0f0070..4810a127 100644 --- a/samples/example.cpp +++ b/samples/example.cpp @@ -51,11 +51,9 @@ struct System void do_callbacks(const std::string &inp) { log("Running Callbacks: " + inp); - for (std::map >::iterator itr = m_callbacks.begin(); - itr != m_callbacks.end(); - ++itr) + for (auto & m_callback : m_callbacks) { - log("Callback: " + itr->first, itr->second(inp)); + log("Callback: " + m_callback.first, m_callback.second(inp)); } } }; @@ -88,25 +86,25 @@ int main(int /*argc*/, char * /*argv*/[]) { // The function "{ 'Callback1' + x }" is created in chaiscript and passed into our C++ application // in the "add_callback" function of struct System the chaiscript function is converted into a // std::function, so it can be handled and called easily and type-safely - chai.eval("system.add_callback(\"#1\", fun(x) { \"Callback1 \" + x });"); + chai.eval(R"(system.add_callback("#1", fun(x) { "Callback1 " + x });)"); // Because we are sharing the "system" object with the chaiscript engine we have equal // access to it both from within chaiscript and from C++ code system.do_callbacks("TestString"); - chai.eval("system.do_callbacks(\"TestString\");"); + chai.eval(R"(system.do_callbacks("TestString");)"); // The log function is overloaded, therefore we have to give the C++ compiler a hint as to which // version we want to register. One way to do this is to create a typedef of the function pointer // then cast your function to that typedef. - typedef void (*PlainLog)(const std::string &); - typedef void (*ModuleLog)(const std::string &, const std::string &); + using PlainLog = void (*)(const std::string &); + using ModuleLog = void (*)(const std::string &, const std::string &); chai.add(fun(PlainLog(&log)), "log"); chai.add(fun(ModuleLog(&log)), "log"); - chai.eval("log(\"Test Message\")"); + chai.eval(R"(log("Test Message"))"); // A shortcut to using eval is just to use the chai operator() - chai("log(\"Test Module\", \"Test Message\");"); + chai(R"(log("Test Module", "Test Message");)"); //Finally, it is possible to register a lambda as a system function, in this //way, we can, for instance add a bound member function to the system @@ -115,7 +113,9 @@ int main(int /*argc*/, char * /*argv*/[]) { //Call bound version of do_callbacks chai("do_callbacks()"); - std::function caller = chai.eval >("fun() { system.do_callbacks(\"From Functor\"); }"); + std::function caller = chai.eval >( + R"(fun() { system.do_callbacks("From Functor"); })" + ); caller(); @@ -134,7 +134,7 @@ int main(int /*argc*/, char * /*argv*/[]) { std::cout << "scripti: " << scripti << '\n'; scripti *= 2; std::cout << "scripti (updated): " << scripti << '\n'; - chai("print(\"Scripti from chai: \" + to_string(scripti))"); + chai(R"(print("Scripti from chai: " + to_string(scripti)))"); //To do: Add examples of handling Boxed_Values directly when needed @@ -146,7 +146,7 @@ int main(int /*argc*/, char * /*argv*/[]) { log("Functor test output", ss.str()); chai.add(var(std::shared_ptr()), "nullvar"); - chai("print(\"This should be true.\"); print(nullvar.is_var_null())"); + chai(R"(print("This should be true."); print(nullvar.is_var_null()))"); // test the global const action chai.add_global_const(const_var(1), "constvar"); @@ -160,7 +160,7 @@ int main(int /*argc*/, char * /*argv*/[]) { // Test ability to register a function that excepts a shared_ptr version of a type - chai("take_shared_ptr(\"Hello World as a shared_ptr\");"); + chai(R"(take_shared_ptr("Hello World as a shared_ptr");)"); chai.add(fun(&bound_log, std::string("Msg")), "BoundFun"); diff --git a/samples/inheritance.cpp b/samples/inheritance.cpp index aba619a4..1c9ddb63 100644 --- a/samples/inheritance.cpp +++ b/samples/inheritance.cpp @@ -122,7 +122,7 @@ int main() assert(myderived.getValue() == "1234"); - chai.eval("myderived.setValue(\"new\")"); // set the value via chaiscript + chai.eval(R"(myderived.setValue("new"))"); // set the value via chaiscript assert(myderived.getValue() == "new"); // call the other derived method via chaiscript and return the value to c++ land: diff --git a/unittests/json_14.chai b/unittests/json_14.chai new file mode 100644 index 00000000..e6490a1d --- /dev/null +++ b/unittests/json_14.chai @@ -0,0 +1,5 @@ +assert_equal(from_json("9.9e-02"), 9.9e-02) +assert_equal(from_json("-13.57e+3"), -13570.0) +assert_equal(from_json("1E-01"), 0.1) +assert_equal(from_json("-314159e-5"), -3.14159) +assert_equal(from_json("5e+04"), 50000)