diff --git a/DesignGoals.md b/DesignGoals.md new file mode 100644 index 00000000..c9971556 --- /dev/null +++ b/DesignGoals.md @@ -0,0 +1,28 @@ +# Introduction + +This document outlines the principles that drive the development of ChaiScript. ChaiScript does not intent to be the perfect tool for *every* situation, but it does intend to be a good general purpose tool for *most* situations. + +# Goals + +1. Trivially easy to integrate with C++ projects +2. 0 external depenencies +3. "Perfect" integration with C++ + * Direct mapping between ChaiScript objects and C++ objects + * Direct mapping between ChaiScript functions and C++ functions + * Direct mapping between ChaiScript exceptions and C++ exceptions +3. Never surprise the C++ developer + * Object lifetimes managed by the stack + * Familiar syntax to C++ developers +4. Perform "well enough" to not get in the way + + +# Alternatives + +## Sol2 + +If you are looking for the fastest performing scripting language and don't mind Lua, you might want to consider [sol2](https://github.com/ThePhD/sol2). + +## SWIG + +If you are looking for the most flexible solution to be able to support multiple target languages, consider [SWIG](http://swig.org) + diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 0ff0ac3f..1002caff 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -60,6 +60,10 @@ #define CHAISCRIPT_MODULE_EXPORT extern "C" #endif +#if defined(CHAISCRIPT_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || defined(CHAISCRIPT_CLANG) +#define CHAISCRIPT_UTF16_UTF32 +#endif + #ifdef _DEBUG #define CHAISCRIPT_DEBUG true #else diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 48341dd0..8da5c702 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -16,6 +16,11 @@ #include #include +#if defined(CHAISCRIPT_UTF16_UTF32) +#include +#include +#endif + #include "../dispatchkit/boxed_value.hpp" @@ -56,6 +61,53 @@ namespace chaiscript , max_alphabet , lengthof_alphabet = 256 }; + + // Generic for u16, u32 and wchar + template + struct Char_Parser_Helper + { + // common for all implementations + static std::string u8str_from_ll(long long val) + { + typedef std::string::value_type char_type; + + char_type c[2]; + c[1] = char_type(val); + c[0] = char_type(val >> 8); + + if (c[0] == 0) + { + return std::string(1, c[1]); // size, character + } + + return std::string(c, 2); // char buffer, size + } + + static string_type str_from_ll(long long val) + { + typedef typename string_type::value_type target_char_type; +#if defined (CHAISCRIPT_UTF16_UTF32) + // prepare converter + std::wstring_convert, target_char_type> converter; + // convert + return converter.from_bytes(u8str_from_ll(val)); +#else + // no conversion available, just put value as character + return string_type(1, target_char_type(val)); // size, character +#endif + } + }; + + // Specialization for char AKA UTF-8 + template<> + struct Char_Parser_Helper + { + static std::string str_from_ll(long long val) + { + // little SFINAE trick to avoid base class + return Char_Parser_Helper::u8str_from_ll(val); + } + }; } @@ -880,6 +932,7 @@ namespace chaiscript bool saw_interpolation_marker = false; bool is_octal = false; bool is_hex = false; + bool is_unicode = false; const bool interpolation_allowed; string_type octal_matches; @@ -901,6 +954,10 @@ namespace chaiscript if (is_hex) { process_hex(); } + + if (is_unicode) { + process_unicode(); + } } void process_hex() @@ -922,9 +979,23 @@ namespace chaiscript is_octal = false; } + + void process_unicode() + { + auto val = stoll(hex_matches, 0, 16); + hex_matches.clear(); + match += detail::Char_Parser_Helper::str_from_ll(val); + is_escaped = false; + is_unicode = false; + } + 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'; + 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_octal) { if (is_octal_char) { octal_matches.push_back(t_char); @@ -937,10 +1008,6 @@ namespace chaiscript 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); @@ -955,6 +1022,21 @@ namespace chaiscript } else { process_hex(); } + } else if (is_unicode) { + if (is_hex_char) { + hex_matches.push_back(t_char); + + if(hex_matches.size() == 4) { + // Format is specified to be 'slash'uABCD + // on collecting from A to D do parsing + process_unicode(); + } + return; + } else { + // Not a unicode anymore, try parsing any way + // May be someone used 'slash'uAA only + process_unicode(); + } } if (t_char == '\\') { @@ -971,6 +1053,8 @@ namespace chaiscript octal_matches.push_back(t_char); } else if (t_char == 'x') { is_hex = true; + } else if (t_char == 'u') { + is_unicode = true; } else { switch (t_char) { case ('\'') : match.push_back('\''); break; diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 294901e1..561b93e5 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -47,6 +47,79 @@ class JSON }; private: + + struct QuickFlatMap + { + auto find(const std::string &s) { + return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + } + + auto find(const std::string &s) const { + return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + } + + auto size() const { + return data.size(); + } + + auto begin() const { + return data.begin(); + } + + auto end() const { + return data.end(); + } + + + auto begin() { + return data.begin(); + } + + auto end() { + return data.end(); + } + + + JSON &operator[](const std::string &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + data.emplace_back(s, JSON()); + return data.back().second; + } + } + + JSON &at(const std::string &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + const JSON &at(const std::string &s) const { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + size_t count(const std::string &s) const { + return (find(s) != data.end())?1:0; + } + + std::vector> data; + + using iterator = decltype(data)::iterator; + using const_iterator = decltype(data)::const_iterator; + + + }; + struct Internal { template auto clone(const std::unique_ptr &ptr) { @@ -100,7 +173,7 @@ class JSON String.reset(); switch( type ) { - case Class::Object: Map = std::make_unique>(); break; + case Class::Object: Map = std::make_unique(); break; case Class::Array: List = std::make_unique>(); break; case Class::String: String = std::make_unique(); break; case Class::Floating: Float = 0.0; break; @@ -116,7 +189,7 @@ class JSON Internal &operator=(Internal &&) = default; std::unique_ptr> List; - std::unique_ptr> Map; + std::unique_ptr Map; std::unique_ptr String; double Float = 0; long Int = 0; @@ -229,7 +302,7 @@ class JSON bool has_key( const std::string &key ) const { if( internal.Type == Class::Object ) { - return internal.Map->find( key ) != internal.Map->end(); + return internal.Map->count(key) != 0; } return false; @@ -274,11 +347,11 @@ class JSON return ok ? internal.Bool : false; } - JSONWrapper> object_range() { + JSONWrapper object_range() { if( internal.Type == Class::Object ) { - return JSONWrapper>( internal.Map.get() ); + return JSONWrapper( internal.Map.get() ); } else { - return JSONWrapper>( nullptr ); + return JSONWrapper( nullptr ); } } @@ -290,11 +363,11 @@ class JSON } } - JSONConstWrapper> object_range() const { + JSONConstWrapper object_range() const { if( internal.Type == Class::Object ) { - return JSONConstWrapper>( internal.Map.get() ); + return JSONConstWrapper( internal.Map.get() ); } else { - return JSONConstWrapper>( nullptr ); + return JSONConstWrapper( nullptr ); } } diff --git a/readme.md b/readme.md index 339b8052..ddb0ed3d 100644 --- a/readme.md +++ b/readme.md @@ -43,10 +43,9 @@ languages: Requirements ============ -ChaiScript requires a C++11 compiler to build with support for variadic -templates. It has been tested with gcc 4.6 and clang 3.1 (with libcxx). MacOS -10.8 (Mountain Lion) is also known to support the C++11 build with Apple's -clang 4.0. MSVC 2013 or newer is supports also. For more information see the build +ChaiScript requires a C++14 compiler to build with support for variadic +templates. It has been tested with gcc 4.9 and clang 3.6 (with libcxx). +For more information see the build [dashboard](http://chaiscript.com/ChaiScript-BuildResults/index.html). Usage diff --git a/samples/example.cpp b/samples/example.cpp index 507c5c0e..0316ebf6 100644 --- a/samples/example.cpp +++ b/samples/example.cpp @@ -73,7 +73,7 @@ int main(int /*argc*/, char * /*argv*/[]) { //Create a new system object and share it with the chaiscript engine System system; - chai.add(var(&system), "system"); + chai.add_global(var(&system), "system"); //Add a bound callback method chai.add(fun(&System::add_callback, std::ref(system)), "add_callback_bound"); diff --git a/unittests/string_unicode_ascii.chai b/unittests/string_unicode_ascii.chai new file mode 100644 index 00000000..aca62d8f --- /dev/null +++ b/unittests/string_unicode_ascii.chai @@ -0,0 +1,8 @@ +assert_equal('\u0020', ' ') +assert_equal('\u0021', '!') +assert_equal('\u0030', '0') +assert_equal('\u0040', '@') +assert_equal('\u005B', '[') +assert_equal('\u005d', ']') +assert_equal('\u0061', 'a') +assert_equal('\u007e', '~') diff --git a/unittests/string_unicode_parse.chai b/unittests/string_unicode_parse.chai new file mode 100644 index 00000000..50da68bb --- /dev/null +++ b/unittests/string_unicode_parse.chai @@ -0,0 +1,11 @@ +assert_equal('\u00aa', '\u00AA') +assert_equal('\u00bb', '\uBB') +assert_equal('\ucc', '\u00CC') +assert_equal('\udd', '\uDD') + +assert_equal('\u0ee', '\uEE') +assert_equal('\ue', '\u000E') + +assert_equal("\u30\u31\u32", "012") +assert_equal("\u33Test", "3Test") +assert_equal("Test\u0040", "Test@") diff --git a/unittests/string_unicode_unicode.chai b/unittests/string_unicode_unicode.chai new file mode 100644 index 00000000..2a237ed8 --- /dev/null +++ b/unittests/string_unicode_unicode.chai @@ -0,0 +1,5 @@ +assert_equal("\uc39c", "Ü") +assert_equal("U for \uc39cmlauts", "U for Ümlauts") +assert_equal("More \uc39cml\uc3a4\uc3bcts", "More Ümläüts") + +assert_equal("Thorn \uc3be sign", "Thorn þ sign")