Merge branch 'develop' into Fix_Crash_From_CppCon

This commit is contained in:
Jason Turner 2015-11-20 05:53:44 -07:00
commit 49436e5740
48 changed files with 1738 additions and 368 deletions

View File

@ -12,7 +12,7 @@ compilers:
build_tag: AddressSanitizer build_tag: AddressSanitizer
version: "3.6" version: "3.6"
skip_packaging: true 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" - name: "clang"
build_tag: ThreadSanitizer build_tag: ThreadSanitizer
version: "3.6" version: "3.6"

View File

@ -68,6 +68,13 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(LINKER_FLAGS "${LINKER_FLAGS} -flto") set(LINKER_FLAGS "${LINKER_FLAGS} -flto")
endif() 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) option(PROFILE_GENERATE "Generate profile data" FALSE)
if (PROFILE_GENERATE) if (PROFILE_GENERATE)
add_definitions(-fprofile-generate) add_definitions(-fprofile-generate)
@ -174,7 +181,7 @@ else()
add_definitions(-Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG}) 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") 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() else()
add_definitions(-Wnoexcept) add_definitions(-Wnoexcept)
endif() endif()
@ -259,6 +266,8 @@ if(BUILD_SAMPLES)
target_link_libraries(memory_leak_test ${LIBS}) target_link_libraries(memory_leak_test ${LIBS})
add_executable(inheritance samples/inheritance.cpp) add_executable(inheritance samples/inheritance.cpp)
target_link_libraries(inheritance ${LIBS}) 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) add_executable(fun_call_performance samples/fun_call_performance.cpp)
target_link_libraries(fun_call_performance ${LIBS}) target_link_libraries(fun_call_performance ${LIBS})
endif() endif()

View File

@ -354,6 +354,18 @@ o.f = fun(y) { print(this.x + y); }
o.f(10); // prints 13 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 ## method_missing
A function of the signature `method_missing(object, name, param1, param2, param3)` will be called if an appropriate A function of the signature `method_missing(object, name, param1, param2, param3)` will be called if an appropriate

View File

@ -0,0 +1,26 @@
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);
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";
}

View File

@ -39,7 +39,11 @@
#define CHAISCRIPT_GCC_4_6 #define CHAISCRIPT_GCC_4_6
#endif #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 #define CHAISCRIPT_OVERRIDE override
#else #else
#define CHAISCRIPT_OVERRIDE #define CHAISCRIPT_OVERRIDE
@ -52,7 +56,7 @@
#define CHAISCRIPT_MODULE_EXPORT extern "C" #define CHAISCRIPT_MODULE_EXPORT extern "C"
#endif #endif
#ifdef CHAISCRIPT_MSVC #ifdef CHAISCRIPT_MSVC_12
#define CHAISCRIPT_NOEXCEPT throw() #define CHAISCRIPT_NOEXCEPT throw()
#define CHAISCRIPT_CONSTEXPR #define CHAISCRIPT_CONSTEXPR
#else #else

View File

@ -19,6 +19,7 @@
#include "dispatchkit/bootstrap_stl.hpp" #include "dispatchkit/bootstrap_stl.hpp"
#include "dispatchkit/boxed_value.hpp" #include "dispatchkit/boxed_value.hpp"
#include "language/chaiscript_prelude.chai" #include "language/chaiscript_prelude.chai"
#include "utility/json_wrap.hpp"
#ifndef CHAISCRIPT_NO_THREADS #ifndef CHAISCRIPT_NO_THREADS
#include <future> #include <future>
@ -51,6 +52,8 @@ namespace chaiscript
lib->add(chaiscript::fun([](const std::function<chaiscript::Boxed_Value ()> &t_func){ return std::async(std::launch::async, t_func);}), "async"); lib->add(chaiscript::fun([](const std::function<chaiscript::Boxed_Value ()> &t_func){ return std::async(std::launch::async, t_func);}), "async");
#endif #endif
lib->add(json_wrap::library());
lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ ); lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ );
return lib; return lib;

View File

@ -440,6 +440,8 @@ namespace chaiscript
m->add(constructor<dispatch::Dynamic_Object ()>(), "Dynamic_Object"); m->add(constructor<dispatch::Dynamic_Object ()>(), "Dynamic_Object");
m->add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); 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::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<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "get_attr"); m->add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "get_attr");
m->add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), "get_attr"); m->add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), "get_attr");
@ -467,9 +469,11 @@ namespace chaiscript
m->add(fun(&Boxed_Value::is_ref), "is_var_reference"); 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_pointer), "is_var_pointer");
m->add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); 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::is_type), "is_type");
m->add(fun(&Boxed_Value::get_attr), "get_var_attr"); 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::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(fun(&Boxed_Value::get_type_info), "get_type_info");
m->add(user_type<Type_Info>(), "Type_Info"); m->add(user_type<Type_Info>(), "Type_Info");
@ -492,6 +496,7 @@ namespace chaiscript
basic_constructors<bool>("bool", m); basic_constructors<bool>("bool", m);
operators::assign<bool>(m); operators::assign<bool>(m);
operators::equal<bool>(m); operators::equal<bool>(m);
operators::not_equal<bool>(m);
m->add(fun([](const std::string &s) -> std::string { return s; }), "to_string"); m->add(fun([](const std::string &s) -> std::string { return s; }), "to_string");
m->add(fun(&Bootstrap::bool_to_string), "to_string"); m->add(fun(&Bootstrap::bool_to_string), "to_string");
@ -509,6 +514,8 @@ namespace chaiscript
bootstrap_pod_type<long>("long", m); bootstrap_pod_type<long>("long", m);
bootstrap_pod_type<unsigned int>("unsigned_int", m); bootstrap_pod_type<unsigned int>("unsigned_int", m);
bootstrap_pod_type<unsigned long>("unsigned_long", m); bootstrap_pod_type<unsigned long>("unsigned_long", m);
bootstrap_pod_type<long long>("long_long", m);
bootstrap_pod_type<unsigned long long>("unsigned_long_long", m);
bootstrap_pod_type<size_t>("size_t", m); bootstrap_pod_type<size_t>("size_t", m);
bootstrap_pod_type<char>("char", m); bootstrap_pod_type<char>("char", m);
bootstrap_pod_type<wchar_t>("wchar_t", m); bootstrap_pod_type<wchar_t>("wchar_t", m);

View File

@ -335,6 +335,7 @@ namespace chaiscript
"def push_back(" + type + " container, x)\n" "def push_back(" + type + " container, x)\n"
"{ \n" "{ \n"
" if (x.is_var_return_value()) {\n" " if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n"
" container.push_back_ref(x) \n" " container.push_back_ref(x) \n"
" } else { \n" " } else { \n"
" container.push_back_ref(clone(x)); \n" " container.push_back_ref(clone(x)); \n"
@ -375,6 +376,7 @@ namespace chaiscript
"def push_front(" + type + " container, x)\n" "def push_front(" + type + " container, x)\n"
"{ \n" "{ \n"
" if (x.is_var_return_value()) {\n" " if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n"
" container.push_front_ref(x) \n" " container.push_front_ref(x) \n"
" } else { \n" " } else { \n"
" container.push_front_ref(clone(x)); \n" " container.push_front_ref(clone(x)); \n"
@ -468,6 +470,30 @@ namespace chaiscript
m->add(fun(static_cast<elem_access>(&MapType::at)), "at"); m->add(fun(static_cast<elem_access>(&MapType::at)), "at");
m->add(fun(static_cast<const_elem_access>(&MapType::at)), "at"); m->add(fun(static_cast<const_elem_access>(&MapType::at)), "at");
if (typeid(MapType) == typeid(std::map<std::string, Boxed_Value>))
{
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<MapType>(type, m); container_type<MapType>(type, m);
default_constructible_type<MapType>(type, m); default_constructible_type<MapType>(type, m);
assignable_type<MapType>(type, m); assignable_type<MapType>(type, m);
@ -523,7 +549,7 @@ namespace chaiscript
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>)) if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
{ {
m->eval(R"( m->eval(R"(
def Vector::`==`(rhs) : type_match(rhs, this) { def Vector::`==`(Vector rhs) {
if ( rhs.size() != this.size() ) { if ( rhs.size() != this.size() ) {
return false; return false;
} else { } else {

View File

@ -100,6 +100,7 @@ namespace chaiscript
:(Common_Types::t_uint64); :(Common_Types::t_uint64);
} }
static Common_Types get_common_type(const Boxed_Value &t_bv) static Common_Types get_common_type(const Boxed_Value &t_bv)
{ {
const Type_Info &inp_ = t_bv.get_type_info(); const Type_Info &inp_ = t_bv.get_type_info();
@ -120,8 +121,12 @@ namespace chaiscript
return get_common_type(sizeof(unsigned int), false); return get_common_type(sizeof(unsigned int), false);
} else if (inp_ == typeid(long)) { } else if (inp_ == typeid(long)) {
return get_common_type(sizeof(long), true); 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)) { } else if (inp_ == typeid(unsigned long)) {
return get_common_type(sizeof(unsigned long), false); 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)) { } else if (inp_ == typeid(std::int8_t)) {
return Common_Types::t_int8; return Common_Types::t_int8;
} else if (inp_ == typeid(std::int16_t)) { } else if (inp_ == typeid(std::int16_t)) {
@ -513,6 +518,21 @@ namespace chaiscript
validate_boxed_number(bv); 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 Boxed_Number get_as(const Type_Info &inp_) const
{ {
if (inp_.bare_equal_type_info(typeid(int))) { if (inp_.bare_equal_type_info(typeid(int))) {
@ -537,8 +557,12 @@ namespace chaiscript
return Boxed_Number(get_as<unsigned int>()); return Boxed_Number(get_as<unsigned int>());
} else if (inp_.bare_equal_type_info(typeid(long))) { } else if (inp_.bare_equal_type_info(typeid(long))) {
return Boxed_Number(get_as<long>()); return Boxed_Number(get_as<long>());
} else if (inp_.bare_equal_type_info(typeid(long long))) {
return Boxed_Number(get_as<long long>());
} else if (inp_.bare_equal_type_info(typeid(unsigned long))) { } else if (inp_.bare_equal_type_info(typeid(unsigned long))) {
return Boxed_Number(get_as<unsigned long>()); return Boxed_Number(get_as<unsigned long>());
} else if (inp_.bare_equal_type_info(typeid(unsigned long long))) {
return Boxed_Number(get_as<unsigned long long>());
} else if (inp_.bare_equal_type_info(typeid(int8_t))) { } else if (inp_.bare_equal_type_info(typeid(int8_t))) {
return Boxed_Number(get_as<int8_t>()); return Boxed_Number(get_as<int8_t>());
} else if (inp_.bare_equal_type_info(typeid(int16_t))) { } else if (inp_.bare_equal_type_info(typeid(int16_t))) {

View File

@ -297,6 +297,13 @@ namespace chaiscript
return *this; 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 /// \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 static bool type_match(const Boxed_Value &l, const Boxed_Value &r) CHAISCRIPT_NOEXCEPT

View File

@ -417,9 +417,9 @@ namespace chaiscript
struct State struct State
{ {
std::map<std::string, std::vector<Proxy_Function> > m_functions; std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> m_functions;
std::map<std::string, Proxy_Function> m_function_objects; std::vector<std::pair<std::string, Proxy_Function>> m_function_objects;
std::map<std::string, Boxed_Value> m_boxed_functions; std::vector<std::pair<std::string, Boxed_Value>> m_boxed_functions;
std::map<std::string, Boxed_Value> m_global_objects; std::map<std::string, Boxed_Value> m_global_objects;
Type_Name_Map m_types; Type_Name_Map m_types;
std::set<std::string> m_reserved_words; std::set<std::string> m_reserved_words;
@ -518,7 +518,7 @@ namespace chaiscript
throw chaiscript::exception::global_non_const(); throw chaiscript::exception::global_non_const();
} }
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex); chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
{ {
@ -533,7 +533,7 @@ namespace chaiscript
{ {
validate_object_name(name); validate_object_name(name);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex); chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto itr = m_state.m_global_objects.find(name); const auto itr = m_state.m_global_objects.find(name);
if (itr == m_state.m_global_objects.end()) if (itr == m_state.m_global_objects.end())
@ -551,7 +551,7 @@ namespace chaiscript
{ {
validate_object_name(name); validate_object_name(name);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex); chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
{ {
@ -648,19 +648,21 @@ namespace chaiscript
return stack[stack.size() - 1 - ((loc & static_cast<uint_fast32_t>(Loc::stack_mask)) >> 16)][loc & static_cast<uint_fast32_t>(Loc::loc_mask)].second; return stack[stack.size() - 1 - ((loc & static_cast<uint_fast32_t>(Loc::stack_mask)) >> 16)][loc & static_cast<uint_fast32_t>(Loc::loc_mask)].second;
} }
// Is the value we are looking for a global? // Is the value we are looking for a global or function?
{ chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
const auto itr = m_state.m_global_objects.find(name); const auto itr = m_state.m_global_objects.find(name);
if (itr != m_state.m_global_objects.end()) if (itr != m_state.m_global_objects.end())
{ {
return itr->second; return itr->second;
}
} }
// If all that failed, then check to see if it's a function // no? is it a function object?
return get_function_object(name); auto obj = get_function_object_int(name, loc);
if (obj.first != loc) t_loc.store(uint_fast32_t(obj.first), std::memory_order_relaxed);
return obj.second;
} }
/// Registers a new named type /// Registers a new named type
@ -718,20 +720,29 @@ namespace chaiscript
return std::vector<std::pair<std::string, Type_Info> >(m_state.m_types.begin(), m_state.m_types.end()); return std::vector<std::pair<std::string, Type_Info> >(m_state.m_types.begin(), m_state.m_types.end());
} }
std::shared_ptr<std::vector<Proxy_Function>> 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(uint_fast32_t(method_missing_funs.first), std::memory_order_relaxed);
return std::move(method_missing_funs.second);
}
/// Return a function by name /// Return a function by name
std::vector< Proxy_Function > get_function(const std::string &t_name) const std::pair<size_t, std::shared_ptr<std::vector< Proxy_Function>>> get_function(const std::string &t_name, const size_t t_hint) const
{ {
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto &funs = get_functions_int(); const auto &funs = get_functions_int();
auto itr = funs.find(t_name); auto itr = find_keyed_value(funs, t_name, t_hint);
if (itr != funs.end()) if (itr != funs.end())
{ {
return itr->second; return std::make_pair(std::distance(funs.begin(), itr), itr->second);
} else { } else {
return std::vector<Proxy_Function>(); return std::make_pair(size_t(0), std::make_shared<std::vector<Proxy_Function>>());
} }
} }
@ -739,28 +750,36 @@ namespace chaiscript
/// \throws std::range_error if it does not /// \throws std::range_error if it does not
Boxed_Value get_function_object(const std::string &t_name) const Boxed_Value get_function_object(const std::string &t_name) const
{ {
// std::cout << "Getting function object: " << t_name << '\n';
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
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
std::pair<size_t, Boxed_Value> get_function_object_int(const std::string &t_name, const size_t t_hint) const
{
const auto &funs = get_boxed_functions_int(); const auto &funs = get_boxed_functions_int();
auto itr = funs.find(t_name); auto itr = find_keyed_value(funs, t_name, t_hint);
if (itr != funs.end()) if (itr != funs.end())
{ {
return itr->second; return std::make_pair(std::distance(funs.begin(), itr), itr->second);
} else { } else {
throw std::range_error("Object not found: " + t_name); throw std::range_error("Object not found: " + t_name);
} }
} }
/// Return true if a function exists /// Return true if a function exists
bool function_exists(const std::string &name) const bool function_exists(const std::string &name) const
{ {
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto &functions = get_functions_int(); 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, /// \returns All values in the local thread state in the parent scope, or if it doesn't exist,
@ -817,11 +836,8 @@ namespace chaiscript
} }
// add the global values // add the global values
{ chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex); retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
}
return retval; return retval;
} }
@ -858,7 +874,7 @@ namespace chaiscript
for (const auto & function : functions) 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); rets.emplace_back(function.first, internal_func);
} }
@ -903,9 +919,11 @@ namespace chaiscript
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4715) #pragma warning(disable : 4715)
#endif #endif
Boxed_Value call_member(const std::string &t_name, const std::vector<Boxed_Value> &params, bool t_has_params) Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params, 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(uint_fast32_t(funs.first), std::memory_order_relaxed);
const auto do_attribute_call = const auto do_attribute_call =
[this](int l_num_params, const std::vector<Boxed_Value> &l_params, const std::vector<Proxy_Function> &l_funs, const Type_Conversions &l_conversions)->Boxed_Value [this](int l_num_params, const std::vector<Boxed_Value> &l_params, const std::vector<Proxy_Function> &l_funs, const Type_Conversions &l_conversions)->Boxed_Value
@ -941,14 +959,14 @@ namespace chaiscript
} }
}; };
if (is_attribute_call(funs, params, t_has_params)) { if (is_attribute_call(*funs.second, params, t_has_params)) {
return do_attribute_call(1, params, funs, m_conversions); return do_attribute_call(1, params, *funs.second, m_conversions);
} else { } else {
std::exception_ptr except; std::exception_ptr except;
if (!funs.empty()) { if (!funs.second->empty()) {
try { try {
return dispatch::dispatch(funs, params, m_conversions); return dispatch::dispatch(*funs.second, params, m_conversions);
} catch(chaiscript::exception::dispatch_error&) { } catch(chaiscript::exception::dispatch_error&) {
except = std::current_exception(); except = std::current_exception();
} }
@ -960,7 +978,9 @@ namespace chaiscript
const auto functions = [&]()->std::vector<Proxy_Function> { const auto functions = [&]()->std::vector<Proxy_Function> {
std::vector<Proxy_Function> fs; std::vector<Proxy_Function> fs;
for (const auto &f : 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)) { if(f->compare_first_type(params[0], m_conversions)) {
fs.push_back(f); fs.push_back(f);
@ -982,12 +1002,17 @@ namespace chaiscript
}(); }();
if (!functions.empty()) { if (!functions.empty()) {
if (is_no_param) { try {
std::vector<Boxed_Value> tmp_params(params); if (is_no_param) {
tmp_params.insert(tmp_params.begin() + 1, var(t_name)); std::vector<Boxed_Value> tmp_params(params);
return do_attribute_call(2, tmp_params, functions, m_conversions); tmp_params.insert(tmp_params.begin() + 1, var(t_name));
} else { return do_attribute_call(2, tmp_params, functions, m_conversions);
return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))}, m_conversions); } else {
return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))}, m_conversions);
}
} catch (const dispatch::option_explicit_set &e) {
throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()),
e.what());
} }
} }
@ -996,7 +1021,7 @@ namespace chaiscript
if (except) { if (except) {
std::rethrow_exception(except); std::rethrow_exception(except);
} else { } else {
throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.begin(), funs.end())); throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()));
} }
} }
} }
@ -1006,30 +1031,14 @@ namespace chaiscript
Boxed_Value call_function(const std::string &t_name, const std::vector<Boxed_Value> &params) const Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params) const
{ {
Boxed_Value bv = dispatch::dispatch(get_function(t_name), params, m_conversions); uint_fast32_t loc = t_loc.load(std::memory_order_relaxed);
// the result of a clone is never to be marked as a return_value const auto funs = get_function(t_name, loc);
if (t_name == "clone") { if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed);
bv.reset_return_value(); return dispatch::dispatch(*funs.second, params, m_conversions);
}
return bv;
} }
Boxed_Value call_function(const std::string &t_name) const
{
return call_function(t_name, std::vector<Boxed_Value>());
}
Boxed_Value call_function(const std::string &t_name, Boxed_Value p1) const
{
return call_function(t_name, std::vector<Boxed_Value>({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<Boxed_Value>({std::move(p1), std::move(p2)}));
}
/// Dump object info to stdout /// Dump object info to stdout
void dump_object(const Boxed_Value &o) const void dump_object(const Boxed_Value &o) const
@ -1140,7 +1149,6 @@ namespace chaiscript
State get_state() const State get_state() const
{ {
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_global_object_mutex);
return m_state; return m_state;
} }
@ -1148,7 +1156,6 @@ namespace chaiscript
void set_state(const State &t_state) void set_state(const State &t_state)
{ {
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l2(m_global_object_mutex);
m_state = t_state; m_state = t_state;
} }
@ -1245,32 +1252,32 @@ namespace chaiscript
private: private:
const std::map<std::string, Boxed_Value> &get_boxed_functions_int() const const std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int() const
{ {
return m_state.m_boxed_functions; return m_state.m_boxed_functions;
} }
std::map<std::string, Boxed_Value> &get_boxed_functions_int() std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int()
{ {
return m_state.m_boxed_functions; return m_state.m_boxed_functions;
} }
const std::map<std::string, Proxy_Function> &get_function_objects_int() const const std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int() const
{ {
return m_state.m_function_objects; return m_state.m_function_objects;
} }
std::map<std::string, Proxy_Function> &get_function_objects_int() std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int()
{ {
return m_state.m_function_objects; return m_state.m_function_objects;
} }
const std::map<std::string, std::vector<Proxy_Function> > &get_functions_int() const const std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int() const
{ {
return m_state.m_functions; return m_state.m_functions;
} }
std::map<std::string, std::vector<Proxy_Function> > &get_functions_int() std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int()
{ {
return m_state.m_functions; return m_state.m_functions;
} }
@ -1384,6 +1391,49 @@ namespace chaiscript
} }
} }
template<typename Container, typename Key, typename Value>
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.reserve(t_c.size() + 1); // tightly control growth of memory usage here
t_c.emplace_back(t_key, std::forward<Value>(t_value));
} else {
typedef typename Container::value_type value_type;
*itr = value_type(t_key, std::forward<Value>(t_value));
}
}
template<typename Container, typename Key>
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<typename Container, typename Key>
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;
});
}
template<typename Container, typename Key>
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. /// Implementation detail for adding a function.
/// \throws exception::name_conflict_error if there's a function matching the given one being added /// \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) void add_function(const Proxy_Function &t_f, const std::string &t_name)
@ -1392,16 +1442,13 @@ namespace chaiscript
auto &funcs = get_functions_int(); auto &funcs = get_functions_int();
auto itr = funcs.find(t_name); auto itr = find_keyed_value(funcs, t_name);
auto &func_objs = get_function_objects_int();
auto &boxed_funcs = get_boxed_functions_int();
Proxy_Function new_func = Proxy_Function new_func =
[&]() -> Proxy_Function { [&]() -> Proxy_Function {
if (itr != funcs.end()) if (itr != funcs.end())
{ {
auto &vec = itr->second; auto vec = *itr->second;
for (const auto &func : vec) for (const auto &func : vec)
{ {
if ((*t_f) == *(func)) if ((*t_f) == *(func))
@ -1410,33 +1457,35 @@ namespace chaiscript
} }
} }
vec.reserve(vec.size() + 1); // tightly control vec growth
vec.push_back(t_f); vec.push_back(t_f);
std::stable_sort(vec.begin(), vec.end(), &function_less_than); std::stable_sort(vec.begin(), vec.end(), &function_less_than);
return std::make_shared<Dispatch_Function>(vec); itr->second = std::make_shared<std::vector<Proxy_Function>>(vec);
return std::make_shared<Dispatch_Function>(std::move(vec));
} else if (t_f->has_arithmetic_param()) { } else if (t_f->has_arithmetic_param()) {
// if the function is the only function but it also contains // if the function is the only function but it also contains
// arithmetic operators, we must wrap it in a dispatch function // arithmetic operators, we must wrap it in a dispatch function
// to allow for automatic arithmetic type conversions // to allow for automatic arithmetic type conversions
std::vector<Proxy_Function> vec({t_f}); std::vector<Proxy_Function> vec({t_f});
funcs.insert(std::make_pair(t_name, vec)); funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(vec));
return std::make_shared<Dispatch_Function>(std::move(vec)); return std::make_shared<Dispatch_Function>(std::move(vec));
} else { } else {
funcs.insert(std::make_pair(t_name, std::vector<Proxy_Function>{t_f})); funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(std::initializer_list<Proxy_Function>({t_f})));
return t_f; return t_f;
} }
}(); }();
boxed_funcs[t_name] = const_var(new_func); add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func));
func_objs[t_name] = std::move(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_mutex;
mutable chaiscript::detail::threading::shared_mutex m_global_object_mutex;
Type_Conversions m_conversions; Type_Conversions m_conversions;
chaiscript::detail::threading::Thread_Storage<Stack_Holder> m_stack_holder; chaiscript::detail::threading::Thread_Storage<Stack_Holder> m_stack_holder;
mutable std::atomic_uint_fast32_t m_method_missing_loc;
State m_state; State m_state;
}; };

View File

@ -24,23 +24,55 @@ namespace chaiscript
{ {
namespace dispatch 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")
{
}
option_explicit_set(const option_explicit_set &) = default;
virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {}
};
class Dynamic_Object class Dynamic_Object
{ {
public: public:
Dynamic_Object(std::string t_type_name) 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 std::string get_type_name() const
{ {
return m_type_name; 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 const Boxed_Value &get_attr(const std::string &t_attr_name) const
{ {
auto a = m_attrs.find(t_attr_name); auto a = m_attrs.find(t_attr_name);
@ -59,11 +91,19 @@ namespace chaiscript
Boxed_Value &method_missing(const std::string &t_method_name) 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); return get_attr(t_method_name);
} }
const Boxed_Value &method_missing(const std::string &t_method_name) const 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); return get_attr(t_method_name);
} }
@ -75,6 +115,7 @@ namespace chaiscript
private: private:
std::string m_type_name; std::string m_type_name;
bool m_option_explicit;
std::map<std::string, Boxed_Value> m_attrs; std::map<std::string, Boxed_Value> m_attrs;
}; };

View File

@ -40,7 +40,7 @@ namespace chaiscript
{ {
const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(),
[](const Const_Proxy_Function &f) { [](const Const_Proxy_Function &f) {
return f->get_arity() == -1 || f->get_arity() == chaiscript::dispatch::detail::Arity<FunctionType>::arity; return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
}); });
if (!has_arity_match) { if (!has_arity_match) {

View File

@ -211,7 +211,7 @@ namespace chaiscript
if (ti.is_undef() if (ti.is_undef()
|| ti.bare_equal(user_type<Boxed_Value>()) || ti.bare_equal(user_type<Boxed_Value>())
|| (!bv.get_type_info().is_undef() || (!bv.get_type_info().is_undef()
&& (ti.bare_equal(user_type<Boxed_Number>()) && ( (ti.bare_equal(user_type<Boxed_Number>()) && bv.get_type_info().is_arithmetic())
|| ti.bare_equal(bv.get_type_info()) || ti.bare_equal(bv.get_type_info())
|| bv.get_type_info().bare_equal(user_type<std::shared_ptr<const Proxy_Function_Base> >()) || bv.get_type_info().bare_equal(user_type<std::shared_ptr<const Proxy_Function_Base> >())
|| t_conversions.converts(ti, bv.get_type_info()) || t_conversions.converts(ti, bv.get_type_info())
@ -760,6 +760,14 @@ namespace chaiscript
{ {
} }
dispatch_error(std::vector<Boxed_Value> t_parameters,
std::vector<Const_Proxy_Function> 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; dispatch_error(const dispatch_error &) = default;
virtual ~dispatch_error() CHAISCRIPT_NOEXCEPT {} virtual ~dispatch_error() CHAISCRIPT_NOEXCEPT {}

View File

@ -32,17 +32,17 @@ namespace chaiscript
CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void, 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) 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_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_flags((t_is_const << is_const_flag)
m_is_void(t_is_void), m_is_arithmetic(t_is_arithmetic), + (t_is_reference << is_reference_flag)
m_is_undef(false) + (t_is_pointer << is_pointer_flag)
+ (t_is_void << is_void_flag)
+ (t_is_arithmetic << is_arithmetic_flag))
{ {
} }
CHAISCRIPT_CONSTEXPR Type_Info() CHAISCRIPT_CONSTEXPR Type_Info()
: m_type_info(nullptr), m_bare_type_info(nullptr), : m_type_info(nullptr), m_bare_type_info(nullptr),
m_is_const(false), m_is_reference(false), m_is_pointer(false), m_flags(1 << is_undef_flag)
m_is_void(false), m_is_arithmetic(false),
m_is_undef(true)
{ {
} }
@ -83,12 +83,12 @@ namespace chaiscript
&& (*m_bare_type_info) == ti; && (*m_bare_type_info) == ti;
} }
CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_is_const; } 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_is_reference; } 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_is_void; } 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_is_arithmetic; } 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_is_undef; } 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_is_pointer; } CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_pointer_flag); }
std::string name() const std::string name() const
{ {
@ -118,12 +118,13 @@ namespace chaiscript
private: private:
const std::type_info *m_type_info; const std::type_info *m_type_info;
const std::type_info *m_bare_type_info; const std::type_info *m_bare_type_info;
bool m_is_const; unsigned int m_flags;
bool m_is_reference; static const int is_const_flag = 0;
bool m_is_pointer; static const int is_reference_flag = 1;
bool m_is_void; static const int is_pointer_flag = 2;
bool m_is_arithmetic; static const int is_void_flag = 3;
bool m_is_undef; static const int is_arithmetic_flag = 4;
static const int is_undef_flag = 5;
}; };
namespace detail namespace detail

View File

@ -60,7 +60,6 @@ namespace chaiscript
chaiscript::eval::detail::Stack_Push_Pop tpp(state); chaiscript::eval::detail::Stack_Push_Pop tpp(state);
if (thisobj) state.add_object("this", *thisobj); if (thisobj) state.add_object("this", *thisobj);
chaiscript::eval::detail::Scope_Push_Pop spp(state);
if (t_locals) { if (t_locals) {
for (const auto &local : *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) { 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 { try {
@ -117,7 +118,7 @@ namespace chaiscript
} else { } else {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
fpp.save_params({t_lhs, t_rhs}); 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){ catch(const exception::dispatch_error &e){
@ -127,6 +128,7 @@ namespace chaiscript
private: private:
Operators::Opers m_oper; Operators::Opers m_oper;
mutable std::atomic_uint_fast32_t m_loc;
}; };
struct Int_AST_Node : public AST_Node { struct Int_AST_Node : public AST_Node {
@ -395,6 +397,8 @@ namespace chaiscript
{ assert(children.size() == 3); } { assert(children.size() == 3); }
Operators::Opers m_oper; 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 ~Equation_AST_Node() {}
virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE {
@ -430,14 +434,14 @@ namespace chaiscript
} else { } else {
if (!rhs.is_return_value()) 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(); rhs.reset_return_value();
} }
} }
try { 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){ 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); throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss);
@ -457,7 +461,7 @@ namespace chaiscript
} }
else { else {
try { 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){ } 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); throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss);
} }
@ -540,7 +544,7 @@ namespace chaiscript
try { try {
fpp.save_params(params); fpp.save_params(params);
return t_ss->call_function("[]", params); return t_ss->call_function("[]", m_loc, params);
} }
catch(const exception::dispatch_error &e){ catch(const exception::dispatch_error &e){
throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss ); throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss );
@ -562,6 +566,8 @@ namespace chaiscript
return oss.str(); return oss.str();
} }
mutable std::atomic_uint_fast32_t m_loc;
}; };
struct Dot_Access_AST_Node : public AST_Node { struct Dot_Access_AST_Node : public AST_Node {
@ -591,7 +597,7 @@ namespace chaiscript
fpp.save_params(params); fpp.save_params(params);
try { 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){ catch(const exception::dispatch_error &e){
if (e.functions.empty()) if (e.functions.empty())
@ -607,7 +613,7 @@ namespace chaiscript
if (this->children[2]->identifier == AST_Node_Type::Array_Call) { if (this->children[2]->identifier == AST_Node_Type::Array_Call) {
try { 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){ catch(const exception::dispatch_error &e){
throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss); throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss);
@ -618,6 +624,8 @@ namespace chaiscript
} }
private: private:
mutable std::atomic_uint_fast32_t m_loc;
mutable std::atomic_uint_fast32_t m_array_loc;
std::string m_fun_name; std::string m_fun_name;
}; };
@ -931,7 +939,7 @@ namespace chaiscript
if (this->children[currentCase]->identifier == AST_Node_Type::Case) { 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. //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
try { try {
if (hasMatched || boxed_cast<bool>(t_ss->call_function("==", match_value, this->children[currentCase]->children[0]->eval(t_ss)))) { if (hasMatched || boxed_cast<bool>(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}))) {
this->children[currentCase]->eval(t_ss); this->children[currentCase]->eval(t_ss);
hasMatched = true; hasMatched = true;
} }
@ -942,7 +950,7 @@ namespace chaiscript
} }
else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { else if (this->children[currentCase]->identifier == AST_Node_Type::Default) {
this->children[currentCase]->eval(t_ss); this->children[currentCase]->eval(t_ss);
breaking = true; hasMatched = true;
} }
} }
catch (detail::Break_Loop &) { catch (detail::Break_Loop &) {
@ -952,6 +960,8 @@ namespace chaiscript
} }
return Boxed_Value(); return Boxed_Value();
} }
mutable std::atomic_uint_fast32_t m_loc;
}; };
struct Case_AST_Node : public AST_Node { struct Case_AST_Node : public AST_Node {
@ -998,7 +1008,7 @@ namespace chaiscript
for (const auto &child : children[0]->children) { for (const auto &child : children[0]->children) {
auto obj = child->eval(t_ss); auto obj = child->eval(t_ss);
if (!obj.is_return_value()) { 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 { } else {
vec.push_back(std::move(obj)); vec.push_back(std::move(obj));
} }
@ -1015,6 +1025,8 @@ namespace chaiscript
{ {
return "[" + AST_Node::pretty_print() + "]"; return "[" + AST_Node::pretty_print() + "]";
} }
mutable std::atomic_uint_fast32_t m_loc;
}; };
struct Inline_Map_AST_Node : public AST_Node { struct Inline_Map_AST_Node : public AST_Node {
@ -1029,7 +1041,7 @@ namespace chaiscript
for (const auto &child : children[0]->children) { for (const auto &child : children[0]->children) {
auto obj = child->children[1]->eval(t_ss); auto obj = child->children[1]->eval(t_ss);
if (!obj.is_return_value()) { 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<std::string>(child->children[0]->eval(t_ss))] = std::move(obj); retval[t_ss->boxed_cast<std::string>(child->children[0]->eval(t_ss))] = std::move(obj);
@ -1042,6 +1054,7 @@ namespace chaiscript
} }
} }
mutable std::atomic_uint_fast32_t m_loc;
}; };
struct Return_AST_Node : public AST_Node { struct Return_AST_Node : public AST_Node {
@ -1124,7 +1137,7 @@ namespace chaiscript
} else { } else {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
fpp.save_params({bv}); 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) { } 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); throw exception::eval_error("Error with prefix operator evaluation: '" + children[0]->text + "'", e.parameters, e.functions, false, *t_ss);
@ -1133,6 +1146,7 @@ namespace chaiscript
private: private:
Operators::Opers m_oper; Operators::Opers m_oper;
mutable std::atomic_uint_fast32_t m_loc;
}; };
struct Break_AST_Node : public AST_Node { struct Break_AST_Node : public AST_Node {
@ -1195,14 +1209,14 @@ namespace chaiscript
try { try {
auto oper1 = children[0]->children[0]->children[0]->eval(t_ss); auto oper1 = children[0]->children[0]->children[0]->eval(t_ss);
auto oper2 = children[0]->children[0]->children[1]->eval(t_ss); auto oper2 = children[0]->children[0]->children[1]->eval(t_ss);
return t_ss->call_function("generate_range", return t_ss->call_function("generate_range", m_loc, {oper1, oper2});
oper1, oper2);
} }
catch (const exception::dispatch_error &e) { 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); 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 { struct Annotation_AST_Node : public AST_Node {

View File

@ -17,9 +17,19 @@
#include <cstring> #include <cstring>
#include "../dispatchkit/boxed_value.hpp" #include "../dispatchkit/boxed_value.hpp"
#include "chaiscript_common.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 namespace chaiscript
{ {
/// \brief Classes and functions used during the parsing process. /// \brief Classes and functions used during the parsing process.
@ -621,8 +631,7 @@ namespace chaiscript
template<typename IntType> static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed)
static Boxed_Value buildInt(const IntType &t_type, const std::string &t_val)
{ {
bool unsigned_ = false; bool unsigned_ = false;
bool long_ = false; bool long_ = false;
@ -649,96 +658,56 @@ namespace chaiscript
} }
} }
std::stringstream ss(t_val.substr(0, i)); const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val;
ss >> t_type;
std::stringstream testu(t_val.substr(0, i)); #ifdef __GNUC__
uint64_t u; #pragma GCC diagnostic push
testu >> t_type >> u; #pragma GCC diagnostic ignored "-Wsign-compare"
bool unsignedrequired = false; #ifdef CHAISCRIPT_CLANG
#pragma GCC diagnostic ignored "-Wtautological-compare"
#endif
if ((u >> (sizeof(int) * 8)) > 0) #endif
{
//requires something bigger than int
long_ = true;
}
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 ((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 (!unsigned_ && !long_ && u >= std::numeric_limits<int>::min() && u <= std::numeric_limits<int>::max()) {
if (longlong_) return const_var(static_cast<int>(u));
{ } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits<unsigned int>::min() && u <= std::numeric_limits<unsigned int>::max()) {
return sizeof(int64_t) * 8; return const_var(static_cast<unsigned int>(u));
} else if (long_) { } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits<long>::min() && u <= std::numeric_limits<long>::max()) {
return sizeof(long) * 8; return const_var(static_cast<long>(u));
} else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
return const_var(static_cast<unsigned long>(u));
} else if (!unsigned_ && u >= std::numeric_limits<long long>::min() && u <= std::numeric_limits<long long>::max()) {
return const_var(static_cast<long long>(u));
} else { } else {
return sizeof(int) * 8; return const_var(static_cast<unsigned long long>(u));
} }
}();
if ( (u >> (size - 1)) > 0) } catch (const std::out_of_range &) {
{ // too big to be signed
unsignedrequired = true; try {
} auto u = std::stoull(val,nullptr,base);
if (unsignedrequired && !unsigned_) if (u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
{ return const_var(static_cast<unsigned long>(u));
if (t_type == &std::hex || t_type == &std::oct) } else {
{ return const_var(static_cast<unsigned long long>(u));
// with hex and octal we are happy to just make it unsigned
unsigned_ = true;
} else {
// with decimal we must bump it up to the next size
if (long_)
{
longlong_ = true;
} else if (!long_ && !longlong_) {
long_ = true;
} }
} catch (const std::out_of_range &) {
// it's just simply too big
return const_var(std::numeric_limits<long long>::max());
} }
} }
if (unsigned_) #ifdef __GNUC__
{ #pragma GCC diagnostic pop
if (longlong_) #endif
{
uint64_t val;
ss >> val;
return const_var(val);
} else if (long_) {
unsigned long val;
ss >> val;
return const_var(val);
} else {
unsigned int val;
ss >> val;
return const_var(val);
}
} else {
if (longlong_)
{
int64_t val;
ss >> val;
return const_var(val);
} else if (long_) {
long val;
ss >> val;
return const_var(val);
} else {
int val;
ss >> val;
return const_var(val);
}
}
} }
template<typename T, typename ... Param> template<typename T, typename ... Param>
@ -756,59 +725,44 @@ namespace chaiscript
} else { } else {
const auto start = m_position; const auto start = m_position;
if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) { if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) {
if (Hex_()) { try {
auto match = Position::str(start, m_position); if (Hex_()) {
auto bv = buildInt(std::hex, match); auto match = Position::str(start, m_position);
m_match_stack.emplace_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(bv))); auto bv = buildInt(16, match, true);
return true; m_match_stack.emplace_back(make_node<eval::Int_AST_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 (Binary_()) {
if (match.length() <= sizeof(int) * 8) auto match = Position::str(start, m_position);
{ auto bv = buildInt(2, match, true);
return const_var(static_cast<int>(temp_int)); m_match_stack.push_back(make_node<eval::Int_AST_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<eval::Float_AST_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);
m_match_stack.push_back(make_node<eval::Int_AST_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<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(bv)));
} else { } else {
return const_var(temp_int); return false;
} }
}(); return true;
m_match_stack.push_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(i)));
return true;
}
if (Float_()) {
auto match = Position::str(start, m_position);
auto bv = buildFloat(match);
m_match_stack.push_back(make_node<eval::Float_AST_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(std::oct, match);
m_match_stack.push_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(bv)));
} }
else if (!match.empty()) { } catch (const std::invalid_argument &) {
auto bv = buildInt(std::dec, match); // error parsing number passed in to buildFloat/buildInt
m_match_stack.push_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(bv))); return false;
} else {
return false;
}
return true;
} }
} }
else { else {
@ -949,6 +903,130 @@ namespace chaiscript
return false; return false;
} }
template<typename string_type>
struct Char_Parser
{
string_type &match;
typedef typename string_type::value_type char_type;
bool is_escaped;
bool is_interpolated;
bool saw_interpolation_marker;
bool is_octal;
bool is_hex;
const bool interpolation_allowed;
string_type octal_matches;
string_type hex_matches;
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)
{
}
Char_Parser &operator=(const Char_Parser &) = delete;
~Char_Parser(){
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()
{
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_type t_char, const int line, const int col, const std::string &filename) {
if (t_char == '\\') {
if (is_escaped) {
match.push_back('\\');
is_escaped = false;
} else {
is_escaped = true;
}
} 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) {
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;
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 {
match.push_back(t_char);
}
}
}
};
/// Reads (and potentially captures) a quoted string from input. Translates escaped sequences. /// Reads (and potentially captures) a quoted string from input. Translates escaped sequences.
bool Quoted_String(const bool t_capture = false) { bool Quoted_String(const bool t_capture = false) {
SkipWS(); SkipWS();
@ -960,97 +1038,75 @@ namespace chaiscript
if (Quoted_String_()) { if (Quoted_String_()) {
std::string match; std::string match;
bool is_escaped = false;
bool is_interpolated = false;
bool saw_interpolation_marker = false;
const auto prev_stack_top = m_match_stack.size(); const auto prev_stack_top = m_match_stack.size();
auto s = start + 1, end = m_position - 1; bool is_interpolated = [&]()->bool {
Char_Parser<std::string> cparser(match, true);
while (s != end) {
if (saw_interpolation_marker) {
if (*s == '{') {
//We've found an interpolation point
if (is_interpolated) { auto s = start + 1, end = m_position - 1;
//If we've seen previous interpolation, add on instead of making a new one
m_match_stack.push_back(make_node<eval::Quoted_String_AST_Node>(match, start.line, start.col));
build_match<eval::Binary_Operator_AST_Node>(prev_stack_top, "+"); 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<eval::Quoted_String_AST_Node>(match, start.line, start.col));
build_match<eval::Binary_Operator_AST_Node>(prev_stack_top, "+");
} else {
m_match_stack.push_back(make_node<eval::Quoted_String_AST_Node>(match, start.line, start.col));
}
//We've finished with the part of the string up to this point, so clear it
match.clear();
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<eval::Id_AST_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<eval::Arg_List_AST_Node>(ev_stack_top);
build_match<eval::Fun_Call_AST_Node>(tostr_stack_top);
build_match<eval::Binary_Operator_AST_Node>(prev_stack_top, "+");
} else {
throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename);
}
} else { } else {
m_match_stack.push_back(make_node<eval::Quoted_String_AST_Node>(match, start.line, start.col)); match.push_back('$');
} }
cparser.saw_interpolation_marker = false;
//We've finished with the part of the string up to this point, so clear it } else {
match.clear(); cparser.parse(*s, start.line, start.col, *m_filename);
std::string eval_match;
++s; ++s;
while ((s != end) && (*s != '}')) {
eval_match.push_back(*s);
++s;
}
if (*s == '}') {
is_interpolated = true;
++s;
const auto tostr_stack_top = m_match_stack.size();
m_match_stack.push_back(make_node<eval::Id_AST_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<eval::Arg_List_AST_Node>(ev_stack_top);
build_match<eval::Fun_Call_AST_Node>(tostr_stack_top);
build_match<eval::Binary_Operator_AST_Node>(prev_stack_top, "+");
} else {
throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename);
}
} else {
match.push_back('$');
} }
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;
}
++s;
} }
}
return cparser.is_interpolated;
}();
if (is_interpolated) { if (is_interpolated) {
m_match_stack.push_back(make_node<eval::Quoted_String_AST_Node>(match, start.line, start.col)); m_match_stack.push_back(make_node<eval::Quoted_String_AST_Node>(match, start.line, start.col));
@ -1104,33 +1160,16 @@ namespace chaiscript
const auto start = m_position; const auto start = m_position;
if (Single_Quoted_String_()) { if (Single_Quoted_String_()) {
std::string match; std::string match;
bool is_escaped = false;
for (auto s = start + 1, end = m_position - 1; s != end; ++s) { {
if (*s == '\\') { // scope for cparser destrutor
if (is_escaped) { Char_Parser<std::string> cparser(match, false);
match.push_back('\\');
is_escaped = false; for (auto s = start + 1, end = m_position - 1; s != end; ++s) {
} else { cparser.parse(*s, start.line, start.col, *m_filename);
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;
} }
} }
m_match_stack.push_back(make_node<eval::Single_Quoted_String_AST_Node>(match, start.line, start.col)); m_match_stack.push_back(make_node<eval::Single_Quoted_String_AST_Node>(match, start.line, start.col));
return true; return true;
} }
@ -1775,6 +1814,8 @@ namespace chaiscript
build_match<eval::Case_AST_Node>(prev_stack_top); build_match<eval::Case_AST_Node>(prev_stack_top);
} else if (Keyword("default")) { } else if (Keyword("default")) {
retval = true;
while (Eol()) {} while (Eol()) {}
if (!Block()) { if (!Block()) {
@ -2373,5 +2414,12 @@ namespace chaiscript
} }
} }
#ifdef CHAISCRIPT_MSVC
#pragma pop_macro("min")
#pragma pop_macro("max")
#endif
#endif /* CHAISCRIPT_PARSER_HPP_ */ #endif /* CHAISCRIPT_PARSER_HPP_ */

View File

@ -41,25 +41,25 @@ def new(x) {
} }
def clone(double x) { def clone(double x) {
double(x).copy_var_attrs(x) double(x).clone_var_attrs(x)
} }
def clone(string x) { def clone(string x) {
string(x).copy_var_attrs(x) string(x).clone_var_attrs(x)
} }
def clone(vector x) { def clone(vector x) {
vector(x).copy_var_attrs(x) vector(x).clone_var_attrs(x)
} }
def clone(int 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) 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);
} }

View File

@ -0,0 +1,647 @@
// From github.com/nbsdx/SimpleJSON.
// Released under the DWTFYW PL
//
#pragma once
#ifndef SIMPLEJSON_HPP
#define SIMPLEJSON_HPP
#include <cstdint>
#include <cmath>
#include <cctype>
#include <string>
#include <deque>
#include <map>
#include <type_traits>
#include <initializer_list>
#include <ostream>
#include <iostream>
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<JSON> *List;
map<string,JSON> *Map;
string *String;
double Float;
long Int;
bool Bool;
} Internal;
public:
enum class Class {
Null,
Object,
Array,
String,
Floating,
Integral,
Boolean
};
template <typename Container>
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 <typename Container>
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)
: Internal(), Type(Class::Null)
{
SetType( type );
}
JSON( initializer_list<JSON> list )
: Internal(), Type(Class::Null)
{
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<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( 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<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( 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 <typename T>
JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){}
template <typename T>
JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = 0 ) : Internal( long(i) ), Type( Class::Integral ){}
template <typename T>
JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = 0 ) : Internal( double(f) ), Type( Class::Floating ){}
template <typename T>
JSON( T s, typename enable_if<is_convertible<T,string>::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 <typename T>
void append( T arg ) {
SetType( Class::Array ); Internal.List->emplace_back( arg );
}
template <typename T, typename... U>
void append( T arg, U... args ) {
append( arg ); append( args... );
}
template <typename T>
typename enable_if<is_same<T,bool>::value, JSON&>::type operator=( T b ) {
SetType( Class::Boolean ); Internal.Bool = b; return *this;
}
template <typename T>
typename enable_if<is_integral<T>::value && !is_same<T,bool>::value, JSON&>::type operator=( T i ) {
SetType( Class::Integral ); Internal.Int = i; return *this;
}
template <typename T>
typename enable_if<is_floating_point<T>::value, JSON&>::type operator=( T f ) {
SetType( Class::Floating ); Internal.Float = f; return *this;
}
template <typename T>
typename enable_if<is_convertible<T,string>::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[]( const size_t 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<int>(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<int>(Internal.Map->size());
else if( Type == Class::Array )
return static_cast<int>(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 ? *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<map<string,JSON>> ObjectRange() {
if( Type == Class::Object )
return JSONWrapper<map<string,JSON>>( Internal.Map );
return JSONWrapper<map<string,JSON>>( nullptr );
}
JSONWrapper<deque<JSON>> ArrayRange() {
if( Type == Class::Array )
return JSONWrapper<deque<JSON>>( Internal.List );
return JSONWrapper<deque<JSON>>( nullptr );
}
JSONConstWrapper<map<string,JSON>> ObjectRange() const {
if( Type == Class::Object )
return JSONConstWrapper<map<string,JSON>>( Internal.Map );
return JSONConstWrapper<map<string,JSON>>( nullptr );
}
JSONConstWrapper<deque<JSON>> ArrayRange() const {
if( Type == Class::Array )
return JSONConstWrapper<deque<JSON>>( Internal.List );
return JSONConstWrapper<deque<JSON>>( 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";
}
throw std::runtime_error("Unhandled JSON type");
}
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<string,JSON>(); break;
case Class::Array: Internal.List = new deque<JSON>(); 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;
};
JSON Array() {
return JSON::Make( JSON::Class::Array );
}
template <typename... T>
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 (;offset<str.size();) {
JSON Key = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] != ':' ) {
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 );
Object[Key.ToString()] = Value;
consume_ws( str, offset );
if( str[offset] == ',' ) {
++offset; continue;
}
else if( str[offset] == '}' ) {
++offset; break;
}
else {
throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str[offset] + "'\n");
}
}
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 (;offset < str.size();) {
Array[index++] = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] == ',' ) {
++offset; continue;
}
else if( str[offset] == ']' ) {
++offset; break;
}
else {
throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'\n");
}
}
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 {
throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'");
}
}
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 = '\0';
bool isDouble = false;
long exp = 0;
for (; offset < str.size() ;) {
c = str[offset++];
if( (c == '-') || (c >= '0' && c <= '9') )
val += c;
else if( c == '.' ) {
val += c;
isDouble = true;
}
else
break;
}
if( offset < str.size() && (c == 'E' || c == 'e' )) {
c = str[ offset++ ];
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 != '}' ) {
throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'");
}
else
break;
}
exp = std::stol( exp_str );
}
else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) {
throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'");
}
--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" ) {
offset += 4;
Bool = true;
} else if( str.substr( offset, 5 ) == "false" ) {
offset += 5;
Bool = false;
} else {
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" ) {
throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'");
}
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 );
}
throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'");
}
}
JSON JSON::Load( const string &str ) {
size_t offset = 0;
return parse_next( str, offset );
}
} // End Namespace json
#endif

View File

@ -0,0 +1,158 @@
#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<Module>())
{
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;
}
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<std::string, Boxed_Value> m;
for (const auto &p : t_json.ObjectRange())
{
m.insert(std::make_pair(p.first, from_json(p.second)));
}
return Boxed_Value(m);
}
case json::JSON::Class::Array:
{
std::vector<Boxed_Value> 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) );
}
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 {
const std::map<std::string, Boxed_Value> m = chaiscript::boxed_cast<const std::map<std::string, Boxed_Value> &>(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<Boxed_Value> v = chaiscript::boxed_cast<const std::vector<Boxed_Value> &>(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<double>();
} else {
obj = bn.get_as<long>();
}
return obj;
} catch (const chaiscript::detail::exception::bad_any_cast &) {
// not a number
}
try {
bool b = boxed_cast<bool>(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<std::string>(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<const dispatch::Dynamic_Object &>(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");
}
};
}
#endif

103
samples/factory.cpp Normal file
View File

@ -0,0 +1,103 @@
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_stdlib.hpp>
class Entity
{
public:
int width;
int height;
int x;
int y;
std::string name;
std::function<void (Entity &)> 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<std::string, Entity> 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>(), "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>(), "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);
}

View File

@ -23,7 +23,7 @@ int main( int /*argc*/ , char * /*argv*/[] )
{ {
ch.eval( script ); ch.eval( script );
} }
catch ( std::exception e ) catch ( const std::exception &e )
{ {
printf( " >>> Exception thrown: %s \n" , e.what( ) ); printf( " >>> Exception thrown: %s \n" , e.what( ) );
} }

View File

@ -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)

View File

@ -784,6 +784,23 @@ TEST_CASE("Variable Scope When Calling From C++ 2")
CHECK_THROWS(func()); 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)");
}
struct Returned_Converted_Config struct Returned_Converted_Config

View File

@ -0,0 +1,14 @@
class MyClass {
def MyClass()
{
this.set_explicit(true);
}
};
var o = MyClass();
assert_true(o.is_explicit());
assert_throws("error", fun[o](){o.x = 2})

View File

@ -0,0 +1,6 @@
assert_equal("\x39", "9")
assert_equal("\x039", "9")
assert_equal("\x39g", "9g")
assert_equal("b\x39g", "b9g")

View File

@ -5,9 +5,10 @@
template<typename T> template<typename T>
bool test_literal(T val, const std::string &str) bool test_literal(T val, const std::string &str)
{ {
std::cout << "Comparing : " << val;
chaiscript::ChaiScript chai; chaiscript::ChaiScript chai;
T val2 = chai.eval<T>(str); T val2 = chai.eval<T>(str);
std::cout << "Comparing : " << val << " " << val2 << '\n'; std::cout << " " << val2 << '\n';
return val == val2; return val == val2;
} }
@ -74,6 +75,39 @@ int main()
&& TEST_LITERAL(177777777777777777) && TEST_LITERAL(177777777777777777)
&& TEST_LITERAL(1777777777777777777) && 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; return EXIT_SUCCESS;

3
unittests/json_1.chai Normal file
View File

@ -0,0 +1,3 @@
assert_true(from_json("null").is_var_null())

1
unittests/json_10.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("\"This is a\\n\\nMultiline string\""), "This is a\n\nMultiline string")

14
unittests/json_11.chai Normal file
View File

@ -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" ]);

1
unittests/json_12.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("\"\""), "")

1
unittests/json_13.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("1.20E+2"), 1.20e2)

3
unittests/json_2.chai Normal file
View File

@ -0,0 +1,3 @@
assert_true(from_json("true"))

1
unittests/json_3.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("100"), 100)

1
unittests/json_4.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("1.234"), 1.234)

1
unittests/json_5.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("\"StringTest\""), "StringTest")

1
unittests/json_6.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("{}"), Map())

4
unittests/json_7.chai Normal file
View File

@ -0,0 +1,4 @@
assert_equal(from_json("\n" +
"{\n" +
" \"Key\" : \"Value\"\n" +
"}\n"), ["Key":"Value"])

1
unittests/json_8.chai Normal file
View File

@ -0,0 +1 @@
assert_equal(from_json("[]"), [])

2
unittests/json_9.chai Normal file
View File

@ -0,0 +1,2 @@
assert_equal(from_json("[1,2,3]"), [1,2,3])

View File

@ -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)

View File

@ -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_int_type.bare_equal(1u.get_type_info()))
assert_equal(true, unsigned_long_type.bare_equal(1lu.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, long_type.bare_equal(1l.get_type_info()))
assert_equal(true, int64_t_type.bare_equal(1ll.get_type_info())) assert_equal(true, long_long_type.bare_equal(1ll.get_type_info()))
assert_equal(true, uint64_t_type.bare_equal(1ull.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, double_type.bare_equal(1.6.get_type_info()))
assert_equal(true, float_type.bare_equal(1.6f.get_type_info())) assert_equal(true, float_type.bare_equal(1.6f.get_type_info()))

View File

@ -0,0 +1,6 @@
assert_equal("\71", "9")
assert_equal("\071", "9")
assert_equal("\71a", "9a")
assert_equal("b\71a", "b9a")

View File

@ -0,0 +1,7 @@
def string::`/=`(double d) { this = "${this}/=${d}"; return this; }
var s = "Hello World"
s /= 2

View File

@ -0,0 +1,6 @@
def string::`*`(double d) { return "${this} * ${d}"; }
"Hello World" * 2

View File

@ -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)

View File

@ -0,0 +1,18 @@
var v = []
v.push_back(3.4);
v.push_back(1);
v.push_back("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")

View File

@ -0,0 +1,5 @@
var v = []
v.push_back(int(1));
v[0] = 3;