diff --git a/.decent_ci-Linux.yaml b/.decent_ci-Linux.yaml index 495b699..7de93a7 100644 --- a/.decent_ci-Linux.yaml +++ b/.decent_ci-Linux.yaml @@ -30,4 +30,8 @@ compilers: collect_performance_results: true - name: cppcheck compiler_extra_flags: --enable=all -I include --inline-suppr -Umax --suppress="*:cmake*" --suppress="*:unittests/catch.hpp" --force + - name: custom_check + commands: + - ./contrib/check_for_tabs.rb + diff --git a/cheatsheet.md b/cheatsheet.md index 841cb92..eca67f0 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -77,7 +77,7 @@ chai.add(chaiscript::constructor(), "MyType"); It's not strictly necessary to add types, but it helps with many things. Cloning, better errors, etc. ``` -chai.add(chaiscript::user_type, "MyClass"); +chai.add(chaiscript::user_type(), "MyClass"); ``` ## Adding Type Conversions @@ -365,6 +365,7 @@ class My_Class { this.x = 2; // this would fail with explicit set to true } }; +``` ## method_missing diff --git a/contrib/check_for_tabs.rb b/contrib/check_for_tabs.rb new file mode 100755 index 0000000..bee7d0d --- /dev/null +++ b/contrib/check_for_tabs.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require 'json' + +`grep -rPIHn '\t' src/* include/* samples/*`.lines { |line| + if /(?.+(hpp|cpp|chai)):(?[0-9]+):(?.+)/ =~ line + puts(JSON.dump({:line => linenumber, :filename => filename, :tool => "tab_checker", :message => "Source Code Line Contains Tabs", :messagetype => "warning"})) + end +} + + diff --git a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp index d8df9be..2e08777 100644 --- a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -33,14 +33,14 @@ namespace chaiscript template struct Cast_Helper_Inner { - typedef std::reference_wrapper::type > Result_Type; + typedef typename std::add_const::type Result_Type; static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) { if (ob.get_type_info().bare_equal_type_info(typeid(Result))) { auto p = throw_if_null(ob.get_const_ptr()); - return std::cref(*static_cast(p)); + return *static_cast(p); } else { throw chaiscript::detail::exception::bad_any_cast(); } @@ -52,12 +52,6 @@ namespace chaiscript { }; - /// Cast_Helper_Inner for casting to a const & type - template - struct Cast_Helper_Inner : Cast_Helper_Inner - { - }; - /// Cast_Helper_Inner for casting to a const * type template @@ -68,7 +62,7 @@ namespace chaiscript { if (ob.get_type_info().bare_equal_type_info(typeid(Result))) { - return static_cast(throw_if_null(ob.get_const_ptr())); + return static_cast(ob.get_const_ptr()); } else { throw chaiscript::detail::exception::bad_any_cast(); } @@ -84,7 +78,36 @@ namespace chaiscript { if (!ob.get_type_info().is_const() && ob.get_type_info() == typeid(Result)) { - return static_cast(throw_if_null(ob.get_ptr())); + return static_cast(ob.get_ptr()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + template + struct Cast_Helper_Inner : public Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner : public Cast_Helper_Inner + { + }; + + + /// Cast_Helper_Inner for casting to a & type + template + struct Cast_Helper_Inner + { + typedef const Result& Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + { + if (ob.get_type_info().bare_equal_type_info(typeid(Result))) + { + auto p = throw_if_null(ob.get_const_ptr()); + return *static_cast(p); } else { throw chaiscript::detail::exception::bad_any_cast(); } diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 9cb338c..9614597 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -83,12 +83,12 @@ namespace chaiscript && (*m_bare_type_info) == ti; } - CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return bool(m_flags & (1 << is_const_flag)); } - CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return bool(m_flags & (1 << is_reference_flag)); } - CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return bool(m_flags & (1 << is_void_flag)); } - CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return bool(m_flags & (1 << is_arithmetic_flag)); } - CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return bool(m_flags & (1 << is_undef_flag)); } - CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return bool(m_flags & (1 << is_pointer_flag)); } + CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_const_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_reference_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_void_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_arithmetic_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_undef_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_pointer_flag)) != 0; } std::string name() const { diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 37b717f..6548521 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -961,6 +961,40 @@ namespace chaiscript } void parse(const char_type t_char, const int line, const int col, const std::string &filename) { + const 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(); + } + return; + } else { + process_octal(); + } + } 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); + + if (hex_matches.size() == 2*sizeof(char_type)) { + // This rule differs from the C/C++ standard, but ChaiScript + // does not offer the same workaround options, and having + // hexadecimal sequences longer than can fit into the char + // type is undefined behavior anyway. + process_hex(); + } + return; + } else { + process_hex(); + } + } + if (t_char == '\\') { if (is_escaped) { match.push_back('\\'); @@ -970,31 +1004,7 @@ namespace chaiscript } } else { if (is_escaped) { - const 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_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) { + if (is_octal_char) { is_octal = true; octal_matches.push_back(t_char); } else if (t_char == 'x') { diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 893f7bf..c023c2b 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -421,22 +421,22 @@ class JSON Class Type; }; -JSON Array() { +inline JSON Array() { return JSON::Make( JSON::Class::Array ); } template -JSON Array( T... args ) { +inline JSON Array( T... args ) { JSON arr = JSON::Make( JSON::Class::Array ); arr.append( args... ); return arr; } -JSON Object() { +inline JSON Object() { return JSON::Make( JSON::Class::Object ); } -std::ostream& operator<<( std::ostream &os, const JSON &json ) { +inline std::ostream& operator<<( std::ostream &os, const JSON &json ) { os << json.dump(); return os; } @@ -636,7 +636,7 @@ namespace { } } -JSON JSON::Load( const string &str ) { +inline JSON JSON::Load( const string &str ) { size_t offset = 0; return parse_next( str, offset ); } diff --git a/include/chaiscript/utility/utility.hpp b/include/chaiscript/utility/utility.hpp index ecd5a3f..381afda 100644 --- a/include/chaiscript/utility/utility.hpp +++ b/include/chaiscript/utility/utility.hpp @@ -8,12 +8,14 @@ #define CHAISCRIPT_UTILITY_UTILITY_HPP_ #include +#include #include #include #include "../chaiscript.hpp" #include "../dispatchkit/proxy_functions.hpp" #include "../dispatchkit/type_info.hpp" +#include "../dispatchkit/operators.hpp" namespace chaiscript @@ -62,6 +64,32 @@ namespace chaiscript t_module.add(fun.first, fun.second); } } + + template + typename std::enable_if::value, void>::type + add_class(ModuleType &t_module, + const std::string &t_class_name, + const std::vector::type, std::string>> &t_constants) + { + t_module.add(chaiscript::user_type(), t_class_name); + + t_module.add(chaiscript::constructor(), t_class_name); + t_module.add(chaiscript::constructor(), t_class_name); + + t_module.add([](){ + // add some comparison and assignment operators + using namespace chaiscript::bootstrap::operators; + return assign(not_equal(equal())); + }()); + + t_module.add(chaiscript::fun([](const Enum &e, const typename std::underlying_type::type &i) { return e == i; }), "=="); + t_module.add(chaiscript::fun([](const typename std::underlying_type::type &i, const Enum &e) { return i == e; }), "=="); + + for (const auto &constant : t_constants) + { + t_module.add_global_const(chaiscript::const_var(Enum(constant.first)), constant.second); + } + } } } diff --git a/unittests/boxed_cast_test.cpp b/unittests/boxed_cast_test.cpp index e575649..1e92874 100644 --- a/unittests/boxed_cast_test.cpp +++ b/unittests/boxed_cast_test.cpp @@ -13,9 +13,9 @@ bool run_test_type_conversion(const Boxed_Value &bv, bool expectedpass) try { To ret = chaiscript::boxed_cast(bv); use(ret); - } catch (const chaiscript::exception::bad_boxed_cast &/*e*/) { + } catch (const chaiscript::exception::bad_boxed_cast &e) { if (expectedpass) { -// std::cerr << "Failure in run_test_type_conversion: " << e.what() << '\n'; + std::cerr << "Failure in run_test_type_conversion: " << e.what() << '\n'; return false; } else { return true; diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 63213b0..de37aaf 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -2,6 +2,7 @@ // caught in other cpp files if chaiscript causes them #include +#include #ifdef CHAISCRIPT_MSVC #pragma warning(push) @@ -520,6 +521,65 @@ TEST_CASE("Utility_Test utility class wrapper") } +enum Utility_Test_Numbers +{ + ONE, + TWO, + THREE +}; + +void do_something_with_enum_vector(const std::vector &v) +{ + CHECK(v.size() == 3); + CHECK(v[0] == ONE); + CHECK(v[1] == THREE); + CHECK(v[2] == TWO); +} + +TEST_CASE("Utility_Test utility class wrapper for enum") +{ + + chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); + + using namespace chaiscript; + + chaiscript::utility::add_class(*m, + "Utility_Test_Numbers", + { { ONE, "ONE" }, + { TWO, "TWO" }, + { THREE, "THREE" } + + } + ); + + + chaiscript::ChaiScript chai; + chai.add(m); + + CHECK(chai.eval("ONE ") == 0); + CHECK(chai.eval("TWO ") == 1); + CHECK(chai.eval("THREE ") == 2); + + CHECK(chai.eval("ONE == 0")); + + chai.add(chaiscript::fun(&do_something_with_enum_vector), "do_something_with_enum_vector"); + chai.add(chaiscript::vector_conversion>()); + CHECK_NOTHROW(chai.eval("var a = [ONE, TWO, THREE]")); + CHECK_NOTHROW(chai.eval("do_something_with_enum_vector([ONE, THREE, TWO])")); + CHECK_NOTHROW(chai.eval("[ONE]")); + + const auto v = chai.eval>("a"); + CHECK(v.size() == 3); + CHECK(v.at(1) == TWO); + + CHECK(chai.eval("ONE == ONE")); + CHECK(chai.eval("ONE != TWO")); + CHECK_NOTHROW(chai.eval("var o = ONE; o = TWO")); + + +} + + ////// Object copy count test class Object_Copy_Count_Test @@ -804,3 +864,54 @@ TEST_CASE("Test long long dispatch") chai.eval("ulonglong(15)"); } + +struct Returned_Converted_Config +{ + int num_iterations; + int something_else; + std::string a_string; + std::function a_function; +}; + + +TEST_CASE("Return of converted type from script") +{ + chaiscript::ChaiScript chai; + + chai.add(chaiscript::constructor(), "Returned_Converted_Config"); + chai.add(chaiscript::fun(&Returned_Converted_Config::num_iterations), "num_iterations"); + chai.add(chaiscript::fun(&Returned_Converted_Config::something_else), "something_else"); + chai.add(chaiscript::fun(&Returned_Converted_Config::a_string), "a_string"); + chai.add(chaiscript::fun(&Returned_Converted_Config::a_function), "a_function"); + chai.add(chaiscript::vector_conversion>()); + + auto c = chai.eval>(R"( + var c = Returned_Converted_Config(); + + c.num_iterations = 5; + c.something_else = c.num_iterations * 2; + c.a_string = "string"; + c.a_function = fun(s) { s.size(); } + + print("making vector"); + var v = []; + print("adding config item"); + v.push_back_ref(c); + print("returning vector"); + v; + + )"); + + + std::cout << typeid(decltype(c)).name() << std::endl; + + std::cout << "Info: " << c.size() << " " << &c[0] << std::endl; + + std::cout << "num_iterations " << c[0].num_iterations << '\n' + << "something_else " << c[0].something_else << '\n' + << "a_string " << c[0].a_string << '\n' + << "a_function " << c[0].a_function("bob") << '\n'; + + chai.add(chaiscript::user_type(), "Returned_Converted_Config"); +} + diff --git a/unittests/hex_escapes.chai b/unittests/hex_escapes.chai index 283ef87..fdd0a8a 100644 --- a/unittests/hex_escapes.chai +++ b/unittests/hex_escapes.chai @@ -1,6 +1,7 @@ assert_equal("\x39", "9") -assert_equal("\x039", "9") +assert_equal("\x39ec", "9ec") assert_equal("\x39g", "9g") assert_equal("b\x39g", "b9g") +assert_equal("\x39\x38g", "98g") diff --git a/unittests/map.chai b/unittests/map.chai index b2901ce..eb7a133 100644 --- a/unittests/map.chai +++ b/unittests/map.chai @@ -1,29 +1,41 @@ -assert_equal([true, false, true], map([1,2,3], odd)) +// Map function + +{ + assert_equal([true, false, true], map([1,2,3], odd)) + + var v = [1, 2, 3]; + var y = map(v, fun(s) { s*2; }); + y[0] = 1; + assert_equal(1, y[0]); +} +// Map objects -var m = ["a":1, "b":2]; +{ + var m = ["a":1, "b":2]; -assert_equal(1, m.count("a")) -assert_equal(0, m.count("c")) -assert_equal(1, m.erase("a")) -assert_equal(1, m.size()) -assert_equal(0, m.erase("a")) + assert_equal(1, m.count("a")) + assert_equal(0, m.count("c")) + assert_equal(1, m.erase("a")) + assert_equal(1, m.size()) + assert_equal(0, m.erase("a")) -assert_equal(1, m.size()); + assert_equal(1, m.size()); -var m2 = ["c":3, "b":4] -m.insert(m2); + var m2 = ["c":3, "b":4] + m.insert(m2); -assert_equal(3, m["c"]) -// The inserted values do not overwrite the existing ones -assert_equal(2, m["b"]) -assert_equal(2, m.size()) + assert_equal(3, m["c"]) + // The inserted values do not overwrite the existing ones + assert_equal(2, m["b"]) + assert_equal(2, m.size()) -var v = "bob"; + var v = "bob"; -m.insert_ref(Map_Pair("d", v)) + m.insert_ref(Map_Pair("d", v)) -assert_equal("bob", m["d"]) -v = "bob2" -assert_equal("bob2", m["d"]) + assert_equal("bob", m["d"]) + v = "bob2" + assert_equal("bob2", m["d"]) +} diff --git a/unittests/octal_escapes.chai b/unittests/octal_escapes.chai index 83b5392..a220d57 100644 --- a/unittests/octal_escapes.chai +++ b/unittests/octal_escapes.chai @@ -3,4 +3,5 @@ assert_equal("\71", "9") assert_equal("\071", "9") assert_equal("\71a", "9a") assert_equal("b\71a", "b9a") +assert_equal("\71\70a", "98a")