Merge branch 'develop' into Fix_Crash_From_CppCon
This commit is contained in:
commit
49436e5740
@ -12,7 +12,7 @@ compilers:
|
||||
build_tag: AddressSanitizer
|
||||
version: "3.6"
|
||||
skip_packaging: true
|
||||
cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON
|
||||
cmake_extra_flags: -DRUN_FUZZY_TESTS:BOOL=TRUE -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON
|
||||
- name: "clang"
|
||||
build_tag: ThreadSanitizer
|
||||
version: "3.6"
|
||||
|
@ -68,6 +68,13 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(LINKER_FLAGS "${LINKER_FLAGS} -flto")
|
||||
endif()
|
||||
|
||||
option(GPROF_OUTPUT "Generate profile data" FALSE)
|
||||
if (GPROF_OUTPUT)
|
||||
add_definitions(-pg)
|
||||
set(LINKER_FLAGS "${LINKER_FLAGS} -pg")
|
||||
endif()
|
||||
|
||||
|
||||
option(PROFILE_GENERATE "Generate profile data" FALSE)
|
||||
if (PROFILE_GENERATE)
|
||||
add_definitions(-fprofile-generate)
|
||||
@ -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})
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_definitions(-Weverything -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors)
|
||||
add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors)
|
||||
else()
|
||||
add_definitions(-Wnoexcept)
|
||||
endif()
|
||||
@ -259,6 +266,8 @@ if(BUILD_SAMPLES)
|
||||
target_link_libraries(memory_leak_test ${LIBS})
|
||||
add_executable(inheritance samples/inheritance.cpp)
|
||||
target_link_libraries(inheritance ${LIBS})
|
||||
add_executable(factory samples/factory.cpp)
|
||||
target_link_libraries(factory ${LIBS})
|
||||
add_executable(fun_call_performance samples/fun_call_performance.cpp)
|
||||
target_link_libraries(fun_call_performance ${LIBS})
|
||||
endif()
|
||||
|
@ -354,6 +354,18 @@ o.f = fun(y) { print(this.x + y); }
|
||||
o.f(10); // prints 13
|
||||
```
|
||||
|
||||
### Option Explicit
|
||||
|
||||
If you want to disable dynamic parameter definitions, you can `set_explicit`.
|
||||
|
||||
```
|
||||
class My_Class {
|
||||
def My_Class() {
|
||||
this.set_explicit(true);
|
||||
this.x = 2; // this would fail with explicit set to true
|
||||
}
|
||||
};
|
||||
|
||||
## method_missing
|
||||
|
||||
A function of the signature `method_missing(object, name, param1, param2, param3)` will be called if an appropriate
|
||||
|
26
contrib/codeanalysis/profile_cpp_calls.chai
Normal file
26
contrib/codeanalysis/profile_cpp_calls.chai
Normal 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";
|
||||
}
|
@ -39,7 +39,11 @@
|
||||
#define CHAISCRIPT_GCC_4_6
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(__llvm__)
|
||||
#if defined(__llvm__)
|
||||
#define CHAISCRIPT_CLANG
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(CHAISCRIPT_CLANG)
|
||||
#define CHAISCRIPT_OVERRIDE override
|
||||
#else
|
||||
#define CHAISCRIPT_OVERRIDE
|
||||
@ -52,7 +56,7 @@
|
||||
#define CHAISCRIPT_MODULE_EXPORT extern "C"
|
||||
#endif
|
||||
|
||||
#ifdef CHAISCRIPT_MSVC
|
||||
#ifdef CHAISCRIPT_MSVC_12
|
||||
#define CHAISCRIPT_NOEXCEPT throw()
|
||||
#define CHAISCRIPT_CONSTEXPR
|
||||
#else
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "dispatchkit/bootstrap_stl.hpp"
|
||||
#include "dispatchkit/boxed_value.hpp"
|
||||
#include "language/chaiscript_prelude.chai"
|
||||
#include "utility/json_wrap.hpp"
|
||||
|
||||
#ifndef CHAISCRIPT_NO_THREADS
|
||||
#include <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");
|
||||
#endif
|
||||
|
||||
lib->add(json_wrap::library());
|
||||
|
||||
lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ );
|
||||
|
||||
return lib;
|
||||
|
@ -440,6 +440,8 @@ namespace chaiscript
|
||||
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_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<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_pointer), "is_var_pointer");
|
||||
m->add(fun(&Boxed_Value::is_return_value), "is_var_return_value");
|
||||
m->add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value");
|
||||
m->add(fun(&Boxed_Value::is_type), "is_type");
|
||||
m->add(fun(&Boxed_Value::get_attr), "get_var_attr");
|
||||
m->add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs");
|
||||
m->add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs");
|
||||
|
||||
m->add(fun(&Boxed_Value::get_type_info), "get_type_info");
|
||||
m->add(user_type<Type_Info>(), "Type_Info");
|
||||
@ -492,6 +496,7 @@ namespace chaiscript
|
||||
basic_constructors<bool>("bool", m);
|
||||
operators::assign<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(&Bootstrap::bool_to_string), "to_string");
|
||||
@ -509,6 +514,8 @@ namespace chaiscript
|
||||
bootstrap_pod_type<long>("long", m);
|
||||
bootstrap_pod_type<unsigned int>("unsigned_int", 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<char>("char", m);
|
||||
bootstrap_pod_type<wchar_t>("wchar_t", m);
|
||||
|
@ -335,6 +335,7 @@ namespace chaiscript
|
||||
"def push_back(" + type + " container, x)\n"
|
||||
"{ \n"
|
||||
" if (x.is_var_return_value()) {\n"
|
||||
" x.reset_var_return_value() \n"
|
||||
" container.push_back_ref(x) \n"
|
||||
" } else { \n"
|
||||
" container.push_back_ref(clone(x)); \n"
|
||||
@ -375,6 +376,7 @@ namespace chaiscript
|
||||
"def push_front(" + type + " container, x)\n"
|
||||
"{ \n"
|
||||
" if (x.is_var_return_value()) {\n"
|
||||
" x.reset_var_return_value() \n"
|
||||
" container.push_front_ref(x) \n"
|
||||
" } else { \n"
|
||||
" container.push_front_ref(clone(x)); \n"
|
||||
@ -468,6 +470,30 @@ namespace chaiscript
|
||||
m->add(fun(static_cast<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);
|
||||
default_constructible_type<MapType>(type, m);
|
||||
assignable_type<MapType>(type, m);
|
||||
@ -523,7 +549,7 @@ namespace chaiscript
|
||||
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
|
||||
{
|
||||
m->eval(R"(
|
||||
def Vector::`==`(rhs) : type_match(rhs, this) {
|
||||
def Vector::`==`(Vector rhs) {
|
||||
if ( rhs.size() != this.size() ) {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -100,6 +100,7 @@ namespace chaiscript
|
||||
:(Common_Types::t_uint64);
|
||||
}
|
||||
|
||||
|
||||
static Common_Types get_common_type(const Boxed_Value &t_bv)
|
||||
{
|
||||
const Type_Info &inp_ = t_bv.get_type_info();
|
||||
@ -120,8 +121,12 @@ namespace chaiscript
|
||||
return get_common_type(sizeof(unsigned int), false);
|
||||
} else if (inp_ == typeid(long)) {
|
||||
return get_common_type(sizeof(long), true);
|
||||
} else if (inp_ == typeid(long long)) {
|
||||
return get_common_type(sizeof(long long), true);
|
||||
} else if (inp_ == typeid(unsigned long)) {
|
||||
return get_common_type(sizeof(unsigned long), false);
|
||||
} else if (inp_ == typeid(unsigned long long)) {
|
||||
return get_common_type(sizeof(unsigned long long), false);
|
||||
} else if (inp_ == typeid(std::int8_t)) {
|
||||
return Common_Types::t_int8;
|
||||
} else if (inp_ == typeid(std::int16_t)) {
|
||||
@ -513,6 +518,21 @@ namespace chaiscript
|
||||
validate_boxed_number(bv);
|
||||
}
|
||||
|
||||
static bool is_floating_point(const Boxed_Value &t_bv)
|
||||
{
|
||||
const Type_Info &inp_ = t_bv.get_type_info();
|
||||
|
||||
if (inp_ == typeid(double)) {
|
||||
return true;
|
||||
} else if (inp_ == typeid(long double)) {
|
||||
return true;
|
||||
} else if (inp_ == typeid(float)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Boxed_Number get_as(const Type_Info &inp_) const
|
||||
{
|
||||
if (inp_.bare_equal_type_info(typeid(int))) {
|
||||
@ -537,8 +557,12 @@ namespace chaiscript
|
||||
return Boxed_Number(get_as<unsigned int>());
|
||||
} else if (inp_.bare_equal_type_info(typeid(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))) {
|
||||
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))) {
|
||||
return Boxed_Number(get_as<int8_t>());
|
||||
} else if (inp_.bare_equal_type_info(typeid(int16_t))) {
|
||||
|
@ -297,6 +297,13 @@ namespace chaiscript
|
||||
return *this;
|
||||
}
|
||||
|
||||
Boxed_Value &clone_attrs(const Boxed_Value &t_obj)
|
||||
{
|
||||
copy_attrs(t_obj);
|
||||
reset_return_value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// \returns true if the two Boxed_Values share the same internal type
|
||||
static bool type_match(const Boxed_Value &l, const Boxed_Value &r) CHAISCRIPT_NOEXCEPT
|
||||
|
@ -417,9 +417,9 @@ namespace chaiscript
|
||||
|
||||
struct State
|
||||
{
|
||||
std::map<std::string, std::vector<Proxy_Function> > m_functions;
|
||||
std::map<std::string, Proxy_Function> m_function_objects;
|
||||
std::map<std::string, Boxed_Value> m_boxed_functions;
|
||||
std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> m_functions;
|
||||
std::vector<std::pair<std::string, Proxy_Function>> m_function_objects;
|
||||
std::vector<std::pair<std::string, Boxed_Value>> m_boxed_functions;
|
||||
std::map<std::string, Boxed_Value> m_global_objects;
|
||||
Type_Name_Map m_types;
|
||||
std::set<std::string> m_reserved_words;
|
||||
@ -518,7 +518,7 @@ namespace chaiscript
|
||||
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())
|
||||
{
|
||||
@ -533,7 +533,7 @@ namespace chaiscript
|
||||
{
|
||||
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);
|
||||
if (itr == m_state.m_global_objects.end())
|
||||
@ -551,7 +551,7 @@ namespace chaiscript
|
||||
{
|
||||
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())
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
|
||||
// Is the value we are looking for a global?
|
||||
{
|
||||
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
|
||||
// Is the value we are looking for a global or function?
|
||||
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
|
||||
|
||||
const auto itr = m_state.m_global_objects.find(name);
|
||||
if (itr != m_state.m_global_objects.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
// If all that failed, then check to see if it's a function
|
||||
return get_function_object(name);
|
||||
// no? is it a function object?
|
||||
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
|
||||
@ -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());
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
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())
|
||||
{
|
||||
return itr->second;
|
||||
return std::make_pair(std::distance(funs.begin(), itr), itr->second);
|
||||
} 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
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
auto itr = funs.find(t_name);
|
||||
auto itr = find_keyed_value(funs, t_name, t_hint);
|
||||
|
||||
if (itr != funs.end())
|
||||
{
|
||||
return itr->second;
|
||||
return std::make_pair(std::distance(funs.begin(), itr), itr->second);
|
||||
} else {
|
||||
throw std::range_error("Object not found: " + t_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Return true if a function exists
|
||||
bool function_exists(const std::string &name) const
|
||||
{
|
||||
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
|
||||
|
||||
const auto &functions = get_functions_int();
|
||||
return functions.find(name) != functions.end();
|
||||
return find_keyed_value(functions, name) != functions.end();
|
||||
}
|
||||
|
||||
/// \returns All values in the local thread state in the parent scope, or if it doesn't exist,
|
||||
@ -817,11 +836,8 @@ namespace chaiscript
|
||||
}
|
||||
|
||||
// add the global values
|
||||
{
|
||||
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
|
||||
|
||||
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
|
||||
retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -858,7 +874,7 @@ namespace chaiscript
|
||||
|
||||
for (const auto & function : functions)
|
||||
{
|
||||
for (const auto & internal_func : function.second)
|
||||
for (const auto & internal_func : *function.second)
|
||||
{
|
||||
rets.emplace_back(function.first, internal_func);
|
||||
}
|
||||
@ -903,9 +919,11 @@ namespace chaiscript
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4715)
|
||||
#endif
|
||||
Boxed_Value call_member(const std::string &t_name, const std::vector<Boxed_Value> ¶ms, 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> ¶ms, bool t_has_params)
|
||||
{
|
||||
const auto funs = get_function(t_name);
|
||||
uint_fast32_t loc = t_loc.load(std::memory_order_relaxed);
|
||||
const auto funs = get_function(t_name, loc);
|
||||
if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed);
|
||||
|
||||
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
|
||||
@ -941,14 +959,14 @@ namespace chaiscript
|
||||
}
|
||||
};
|
||||
|
||||
if (is_attribute_call(funs, params, t_has_params)) {
|
||||
return do_attribute_call(1, params, funs, m_conversions);
|
||||
if (is_attribute_call(*funs.second, params, t_has_params)) {
|
||||
return do_attribute_call(1, params, *funs.second, m_conversions);
|
||||
} else {
|
||||
std::exception_ptr except;
|
||||
|
||||
if (!funs.empty()) {
|
||||
if (!funs.second->empty()) {
|
||||
try {
|
||||
return dispatch::dispatch(funs, params, m_conversions);
|
||||
return dispatch::dispatch(*funs.second, params, m_conversions);
|
||||
} catch(chaiscript::exception::dispatch_error&) {
|
||||
except = std::current_exception();
|
||||
}
|
||||
@ -960,7 +978,9 @@ namespace chaiscript
|
||||
const auto functions = [&]()->std::vector<Proxy_Function> {
|
||||
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)) {
|
||||
fs.push_back(f);
|
||||
@ -982,6 +1002,7 @@ namespace chaiscript
|
||||
}();
|
||||
|
||||
if (!functions.empty()) {
|
||||
try {
|
||||
if (is_no_param) {
|
||||
std::vector<Boxed_Value> tmp_params(params);
|
||||
tmp_params.insert(tmp_params.begin() + 1, var(t_name));
|
||||
@ -989,6 +1010,10 @@ namespace chaiscript
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
||||
// If we get all the way down here we know there was no "method_missing"
|
||||
@ -996,7 +1021,7 @@ namespace chaiscript
|
||||
if (except) {
|
||||
std::rethrow_exception(except);
|
||||
} 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> ¶ms) const
|
||||
Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> ¶ms) const
|
||||
{
|
||||
Boxed_Value bv = dispatch::dispatch(get_function(t_name), params, m_conversions);
|
||||
// the result of a clone is never to be marked as a return_value
|
||||
if (t_name == "clone") {
|
||||
bv.reset_return_value();
|
||||
}
|
||||
return bv;
|
||||
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);
|
||||
return dispatch::dispatch(*funs.second, params, m_conversions);
|
||||
}
|
||||
|
||||
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
|
||||
void dump_object(const Boxed_Value &o) const
|
||||
@ -1140,7 +1149,6 @@ namespace chaiscript
|
||||
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> l2(m_global_object_mutex);
|
||||
|
||||
return m_state;
|
||||
}
|
||||
@ -1148,7 +1156,6 @@ namespace chaiscript
|
||||
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> l2(m_global_object_mutex);
|
||||
|
||||
m_state = t_state;
|
||||
}
|
||||
@ -1245,32 +1252,32 @@ namespace chaiscript
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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.
|
||||
/// \throws exception::name_conflict_error if there's a function matching the given one being added
|
||||
void add_function(const Proxy_Function &t_f, const std::string &t_name)
|
||||
@ -1392,16 +1442,13 @@ namespace chaiscript
|
||||
|
||||
auto &funcs = get_functions_int();
|
||||
|
||||
auto itr = funcs.find(t_name);
|
||||
|
||||
auto &func_objs = get_function_objects_int();
|
||||
auto &boxed_funcs = get_boxed_functions_int();
|
||||
auto itr = find_keyed_value(funcs, t_name);
|
||||
|
||||
Proxy_Function new_func =
|
||||
[&]() -> Proxy_Function {
|
||||
if (itr != funcs.end())
|
||||
{
|
||||
auto &vec = itr->second;
|
||||
auto vec = *itr->second;
|
||||
for (const auto &func : vec)
|
||||
{
|
||||
if ((*t_f) == *(func))
|
||||
@ -1410,33 +1457,35 @@ namespace chaiscript
|
||||
}
|
||||
}
|
||||
|
||||
vec.reserve(vec.size() + 1); // tightly control vec growth
|
||||
vec.push_back(t_f);
|
||||
std::stable_sort(vec.begin(), vec.end(), &function_less_than);
|
||||
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()) {
|
||||
// if the function is the only function but it also contains
|
||||
// arithmetic operators, we must wrap it in a dispatch function
|
||||
// to allow for automatic arithmetic type conversions
|
||||
std::vector<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));
|
||||
} 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;
|
||||
}
|
||||
}();
|
||||
|
||||
boxed_funcs[t_name] = const_var(new_func);
|
||||
func_objs[t_name] = std::move(new_func);
|
||||
add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func));
|
||||
add_keyed_value(get_function_objects_int(), t_name, std::move(new_func));
|
||||
}
|
||||
|
||||
mutable chaiscript::detail::threading::shared_mutex m_mutex;
|
||||
mutable chaiscript::detail::threading::shared_mutex m_global_object_mutex;
|
||||
|
||||
|
||||
Type_Conversions m_conversions;
|
||||
chaiscript::detail::threading::Thread_Storage<Stack_Holder> m_stack_holder;
|
||||
|
||||
mutable std::atomic_uint_fast32_t m_method_missing_loc;
|
||||
|
||||
State m_state;
|
||||
};
|
||||
|
@ -24,23 +24,55 @@ namespace chaiscript
|
||||
{
|
||||
namespace dispatch
|
||||
{
|
||||
struct option_explicit_set : std::runtime_error {
|
||||
option_explicit_set(const std::string &t_param_name)
|
||||
: std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
option_explicit_set(const option_explicit_set &) = default;
|
||||
|
||||
virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {}
|
||||
};
|
||||
|
||||
class Dynamic_Object
|
||||
{
|
||||
public:
|
||||
Dynamic_Object(std::string t_type_name)
|
||||
: m_type_name(std::move(t_type_name))
|
||||
: m_type_name(std::move(t_type_name)), m_option_explicit(false)
|
||||
{
|
||||
}
|
||||
|
||||
Dynamic_Object() : m_type_name("")
|
||||
Dynamic_Object() : m_type_name(""), m_option_explicit(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_explicit() const
|
||||
{
|
||||
return m_option_explicit;
|
||||
}
|
||||
|
||||
void set_explicit(const bool t_explicit)
|
||||
{
|
||||
m_option_explicit = t_explicit;
|
||||
}
|
||||
|
||||
std::string get_type_name() const
|
||||
{
|
||||
return m_type_name;
|
||||
}
|
||||
|
||||
const Boxed_Value &operator[](const std::string &t_attr_name) const
|
||||
{
|
||||
return get_attr(t_attr_name);
|
||||
}
|
||||
|
||||
Boxed_Value &operator[](const std::string &t_attr_name)
|
||||
{
|
||||
return get_attr(t_attr_name);
|
||||
}
|
||||
|
||||
const Boxed_Value &get_attr(const std::string &t_attr_name) const
|
||||
{
|
||||
auto a = m_attrs.find(t_attr_name);
|
||||
@ -59,11 +91,19 @@ namespace chaiscript
|
||||
|
||||
Boxed_Value &method_missing(const std::string &t_method_name)
|
||||
{
|
||||
if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) {
|
||||
throw option_explicit_set(t_method_name);
|
||||
}
|
||||
|
||||
return get_attr(t_method_name);
|
||||
}
|
||||
|
||||
const Boxed_Value &method_missing(const std::string &t_method_name) const
|
||||
{
|
||||
if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) {
|
||||
throw option_explicit_set(t_method_name);
|
||||
}
|
||||
|
||||
return get_attr(t_method_name);
|
||||
}
|
||||
|
||||
@ -75,6 +115,7 @@ namespace chaiscript
|
||||
|
||||
private:
|
||||
std::string m_type_name;
|
||||
bool m_option_explicit;
|
||||
|
||||
std::map<std::string, Boxed_Value> m_attrs;
|
||||
};
|
||||
|
@ -40,7 +40,7 @@ namespace chaiscript
|
||||
{
|
||||
const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(),
|
||||
[](const Const_Proxy_Function &f) {
|
||||
return f->get_arity() == -1 || f->get_arity() == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
|
||||
return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
|
||||
});
|
||||
|
||||
if (!has_arity_match) {
|
||||
|
@ -211,7 +211,7 @@ namespace chaiscript
|
||||
if (ti.is_undef()
|
||||
|| ti.bare_equal(user_type<Boxed_Value>())
|
||||
|| (!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())
|
||||
|| bv.get_type_info().bare_equal(user_type<std::shared_ptr<const Proxy_Function_Base> >())
|
||||
|| 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;
|
||||
virtual ~dispatch_error() CHAISCRIPT_NOEXCEPT {}
|
||||
|
||||
|
@ -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,
|
||||
bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti)
|
||||
: m_type_info(t_ti), m_bare_type_info(t_bare_ti),
|
||||
m_is_const(t_is_const), m_is_reference(t_is_reference), m_is_pointer(t_is_pointer),
|
||||
m_is_void(t_is_void), m_is_arithmetic(t_is_arithmetic),
|
||||
m_is_undef(false)
|
||||
m_flags((t_is_const << is_const_flag)
|
||||
+ (t_is_reference << is_reference_flag)
|
||||
+ (t_is_pointer << is_pointer_flag)
|
||||
+ (t_is_void << is_void_flag)
|
||||
+ (t_is_arithmetic << is_arithmetic_flag))
|
||||
{
|
||||
}
|
||||
|
||||
CHAISCRIPT_CONSTEXPR Type_Info()
|
||||
: m_type_info(nullptr), m_bare_type_info(nullptr),
|
||||
m_is_const(false), m_is_reference(false), m_is_pointer(false),
|
||||
m_is_void(false), m_is_arithmetic(false),
|
||||
m_is_undef(true)
|
||||
m_flags(1 << is_undef_flag)
|
||||
{
|
||||
}
|
||||
|
||||
@ -83,12 +83,12 @@ namespace chaiscript
|
||||
&& (*m_bare_type_info) == ti;
|
||||
}
|
||||
|
||||
CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_is_const; }
|
||||
CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_is_reference; }
|
||||
CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_is_void; }
|
||||
CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_is_arithmetic; }
|
||||
CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_is_undef; }
|
||||
CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_is_pointer; }
|
||||
CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_const_flag); }
|
||||
CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_reference_flag); }
|
||||
CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_void_flag); }
|
||||
CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_arithmetic_flag); }
|
||||
CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_undef_flag); }
|
||||
CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_flags & (1 << is_pointer_flag); }
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
@ -118,12 +118,13 @@ namespace chaiscript
|
||||
private:
|
||||
const std::type_info *m_type_info;
|
||||
const std::type_info *m_bare_type_info;
|
||||
bool m_is_const;
|
||||
bool m_is_reference;
|
||||
bool m_is_pointer;
|
||||
bool m_is_void;
|
||||
bool m_is_arithmetic;
|
||||
bool m_is_undef;
|
||||
unsigned int m_flags;
|
||||
static const int is_const_flag = 0;
|
||||
static const int is_reference_flag = 1;
|
||||
static const int is_pointer_flag = 2;
|
||||
static const int is_void_flag = 3;
|
||||
static const int is_arithmetic_flag = 4;
|
||||
static const int is_undef_flag = 5;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
@ -60,7 +60,6 @@ namespace chaiscript
|
||||
|
||||
chaiscript::eval::detail::Stack_Push_Pop tpp(state);
|
||||
if (thisobj) state.add_object("this", *thisobj);
|
||||
chaiscript::eval::detail::Scope_Push_Pop spp(state);
|
||||
|
||||
if (t_locals) {
|
||||
for (const auto &local : *t_locals) {
|
||||
@ -69,8 +68,10 @@ namespace chaiscript
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < t_param_names.size(); ++i) {
|
||||
if (t_param_names[i] != "this") {
|
||||
state.add_object(t_param_names[i], t_vals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return t_node->eval(state);
|
||||
@ -117,7 +118,7 @@ namespace chaiscript
|
||||
} else {
|
||||
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
||||
fpp.save_params({t_lhs, t_rhs});
|
||||
return t_ss->call_function(t_oper_string, t_lhs, t_rhs);
|
||||
return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs});
|
||||
}
|
||||
}
|
||||
catch(const exception::dispatch_error &e){
|
||||
@ -127,6 +128,7 @@ namespace chaiscript
|
||||
|
||||
private:
|
||||
Operators::Opers m_oper;
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
};
|
||||
|
||||
struct Int_AST_Node : public AST_Node {
|
||||
@ -395,6 +397,8 @@ namespace chaiscript
|
||||
{ assert(children.size() == 3); }
|
||||
|
||||
Operators::Opers m_oper;
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
mutable std::atomic_uint_fast32_t m_clone_loc;
|
||||
|
||||
virtual ~Equation_AST_Node() {}
|
||||
virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE {
|
||||
@ -430,14 +434,14 @@ namespace chaiscript
|
||||
} else {
|
||||
if (!rhs.is_return_value())
|
||||
{
|
||||
rhs = t_ss->call_function("clone", rhs);
|
||||
rhs = t_ss->call_function("clone", m_clone_loc, {rhs});
|
||||
}
|
||||
rhs.reset_return_value();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return t_ss->call_function(this->children[1]->text, std::move(lhs), rhs);
|
||||
return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs});
|
||||
}
|
||||
catch(const exception::dispatch_error &e){
|
||||
throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss);
|
||||
@ -457,7 +461,7 @@ namespace chaiscript
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return t_ss->call_function(this->children[1]->text, std::move(lhs), rhs);
|
||||
return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs});
|
||||
} catch(const exception::dispatch_error &e){
|
||||
throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss);
|
||||
}
|
||||
@ -540,7 +544,7 @@ namespace chaiscript
|
||||
|
||||
try {
|
||||
fpp.save_params(params);
|
||||
return t_ss->call_function("[]", params);
|
||||
return t_ss->call_function("[]", m_loc, params);
|
||||
}
|
||||
catch(const exception::dispatch_error &e){
|
||||
throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss );
|
||||
@ -562,6 +566,8 @@ namespace chaiscript
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
};
|
||||
|
||||
struct Dot_Access_AST_Node : public AST_Node {
|
||||
@ -591,7 +597,7 @@ namespace chaiscript
|
||||
fpp.save_params(params);
|
||||
|
||||
try {
|
||||
retval = t_ss->call_member(m_fun_name, std::move(params), has_function_params);
|
||||
retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params);
|
||||
}
|
||||
catch(const exception::dispatch_error &e){
|
||||
if (e.functions.empty())
|
||||
@ -607,7 +613,7 @@ namespace chaiscript
|
||||
|
||||
if (this->children[2]->identifier == AST_Node_Type::Array_Call) {
|
||||
try {
|
||||
retval = t_ss->call_function("[]", retval, this->children[2]->children[1]->eval(t_ss));
|
||||
retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[2]->children[1]->eval(t_ss)});
|
||||
}
|
||||
catch(const exception::dispatch_error &e){
|
||||
throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss);
|
||||
@ -618,6 +624,8 @@ namespace chaiscript
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
mutable std::atomic_uint_fast32_t m_array_loc;
|
||||
std::string m_fun_name;
|
||||
};
|
||||
|
||||
@ -931,7 +939,7 @@ namespace chaiscript
|
||||
if (this->children[currentCase]->identifier == AST_Node_Type::Case) {
|
||||
//This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
|
||||
try {
|
||||
if (hasMatched || boxed_cast<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);
|
||||
hasMatched = true;
|
||||
}
|
||||
@ -942,7 +950,7 @@ namespace chaiscript
|
||||
}
|
||||
else if (this->children[currentCase]->identifier == AST_Node_Type::Default) {
|
||||
this->children[currentCase]->eval(t_ss);
|
||||
breaking = true;
|
||||
hasMatched = true;
|
||||
}
|
||||
}
|
||||
catch (detail::Break_Loop &) {
|
||||
@ -952,6 +960,8 @@ namespace chaiscript
|
||||
}
|
||||
return Boxed_Value();
|
||||
}
|
||||
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
};
|
||||
|
||||
struct Case_AST_Node : public AST_Node {
|
||||
@ -998,7 +1008,7 @@ namespace chaiscript
|
||||
for (const auto &child : children[0]->children) {
|
||||
auto obj = child->eval(t_ss);
|
||||
if (!obj.is_return_value()) {
|
||||
vec.push_back(t_ss->call_function("clone", obj));
|
||||
vec.push_back(t_ss->call_function("clone", m_loc, {obj}));
|
||||
} else {
|
||||
vec.push_back(std::move(obj));
|
||||
}
|
||||
@ -1015,6 +1025,8 @@ namespace chaiscript
|
||||
{
|
||||
return "[" + AST_Node::pretty_print() + "]";
|
||||
}
|
||||
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
};
|
||||
|
||||
struct Inline_Map_AST_Node : public AST_Node {
|
||||
@ -1029,7 +1041,7 @@ namespace chaiscript
|
||||
for (const auto &child : children[0]->children) {
|
||||
auto obj = child->children[1]->eval(t_ss);
|
||||
if (!obj.is_return_value()) {
|
||||
obj = t_ss->call_function("clone", obj);
|
||||
obj = t_ss->call_function("clone", m_loc, {obj});
|
||||
}
|
||||
|
||||
retval[t_ss->boxed_cast<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 {
|
||||
@ -1124,7 +1137,7 @@ namespace chaiscript
|
||||
} else {
|
||||
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
||||
fpp.save_params({bv});
|
||||
return t_ss->call_function(children[0]->text, std::move(bv));
|
||||
return t_ss->call_function(children[0]->text, m_loc, {std::move(bv)});
|
||||
}
|
||||
} catch (const exception::dispatch_error &e) {
|
||||
throw exception::eval_error("Error with prefix operator evaluation: '" + children[0]->text + "'", e.parameters, e.functions, false, *t_ss);
|
||||
@ -1133,6 +1146,7 @@ namespace chaiscript
|
||||
|
||||
private:
|
||||
Operators::Opers m_oper;
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
};
|
||||
|
||||
struct Break_AST_Node : public AST_Node {
|
||||
@ -1195,14 +1209,14 @@ namespace chaiscript
|
||||
try {
|
||||
auto oper1 = children[0]->children[0]->children[0]->eval(t_ss);
|
||||
auto oper2 = children[0]->children[0]->children[1]->eval(t_ss);
|
||||
return t_ss->call_function("generate_range",
|
||||
oper1, oper2);
|
||||
return t_ss->call_function("generate_range", m_loc, {oper1, oper2});
|
||||
}
|
||||
catch (const exception::dispatch_error &e) {
|
||||
throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss);
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::atomic_uint_fast32_t m_loc;
|
||||
};
|
||||
|
||||
struct Annotation_AST_Node : public AST_Node {
|
||||
|
@ -17,9 +17,19 @@
|
||||
#include <cstring>
|
||||
|
||||
|
||||
|
||||
#include "../dispatchkit/boxed_value.hpp"
|
||||
#include "chaiscript_common.hpp"
|
||||
|
||||
|
||||
#if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min)
|
||||
#pragma push_macro("max") // Why Microsoft? why? This is worse than bad
|
||||
#undef max
|
||||
#pragma push_macro("min")
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
|
||||
namespace chaiscript
|
||||
{
|
||||
/// \brief Classes and functions used during the parsing process.
|
||||
@ -621,8 +631,7 @@ namespace chaiscript
|
||||
|
||||
|
||||
|
||||
template<typename IntType>
|
||||
static Boxed_Value buildInt(const IntType &t_type, const std::string &t_val)
|
||||
static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed)
|
||||
{
|
||||
bool unsigned_ = false;
|
||||
bool long_ = false;
|
||||
@ -649,96 +658,56 @@ namespace chaiscript
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss(t_val.substr(0, i));
|
||||
ss >> t_type;
|
||||
const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val;
|
||||
|
||||
std::stringstream testu(t_val.substr(0, i));
|
||||
uint64_t u;
|
||||
testu >> t_type >> u;
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#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)
|
||||
{
|
||||
//requires something bigger than int
|
||||
long_ = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(long) == sizeof(uint64_t) || sizeof(long) * 2 == sizeof(uint64_t), "Unexpected sizing of integer types");
|
||||
|
||||
if ((sizeof(long) < sizeof(uint64_t))
|
||||
&& (u >> ((sizeof(uint64_t) - sizeof(long)) * 8)) > 0)
|
||||
{
|
||||
//requires something bigger than long
|
||||
longlong_ = true;
|
||||
}
|
||||
try {
|
||||
auto u = std::stoll(val,nullptr,base);
|
||||
|
||||
|
||||
const size_t size = [&]()->size_t{
|
||||
if (longlong_)
|
||||
{
|
||||
return sizeof(int64_t) * 8;
|
||||
} else if (long_) {
|
||||
return sizeof(long) * 8;
|
||||
if (!unsigned_ && !long_ && u >= std::numeric_limits<int>::min() && u <= std::numeric_limits<int>::max()) {
|
||||
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 const_var(static_cast<unsigned int>(u));
|
||||
} else if (!unsigned_ && !longlong_ && u >= std::numeric_limits<long>::min() && u <= std::numeric_limits<long>::max()) {
|
||||
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 {
|
||||
return sizeof(int) * 8;
|
||||
}
|
||||
}();
|
||||
|
||||
if ( (u >> (size - 1)) > 0)
|
||||
{
|
||||
unsignedrequired = true;
|
||||
return const_var(static_cast<unsigned long long>(u));
|
||||
}
|
||||
|
||||
if (unsignedrequired && !unsigned_)
|
||||
{
|
||||
if (t_type == &std::hex || t_type == &std::oct)
|
||||
{
|
||||
// with hex and octal we are happy to just make it unsigned
|
||||
unsigned_ = true;
|
||||
} catch (const std::out_of_range &) {
|
||||
// too big to be signed
|
||||
try {
|
||||
auto u = std::stoull(val,nullptr,base);
|
||||
|
||||
if (u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
|
||||
return const_var(static_cast<unsigned long>(u));
|
||||
} else {
|
||||
// with decimal we must bump it up to the next size
|
||||
if (long_)
|
||||
{
|
||||
longlong_ = true;
|
||||
} else if (!long_ && !longlong_) {
|
||||
long_ = true;
|
||||
return const_var(static_cast<unsigned long long>(u));
|
||||
}
|
||||
} catch (const std::out_of_range &) {
|
||||
// it's just simply too big
|
||||
return const_var(std::numeric_limits<long long>::max());
|
||||
}
|
||||
}
|
||||
|
||||
if (unsigned_)
|
||||
{
|
||||
if (longlong_)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename ... Param>
|
||||
@ -756,37 +725,18 @@ namespace chaiscript
|
||||
} else {
|
||||
const auto start = m_position;
|
||||
if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) {
|
||||
try {
|
||||
if (Hex_()) {
|
||||
auto match = Position::str(start, m_position);
|
||||
auto bv = buildInt(std::hex, match);
|
||||
auto bv = buildInt(16, match, true);
|
||||
m_match_stack.emplace_back(make_node<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 (match.length() <= sizeof(int) * 8)
|
||||
{
|
||||
return const_var(static_cast<int>(temp_int));
|
||||
} else {
|
||||
return const_var(temp_int);
|
||||
}
|
||||
}();
|
||||
|
||||
m_match_stack.push_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(i)));
|
||||
auto bv = buildInt(2, match, true);
|
||||
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_()) {
|
||||
@ -799,17 +749,21 @@ namespace chaiscript
|
||||
IntSuffix_();
|
||||
auto match = Position::str(start, m_position);
|
||||
if (!match.empty() && (match[0] == '0')) {
|
||||
auto bv = buildInt(std::oct, match);
|
||||
auto bv = buildInt(8, match, false);
|
||||
m_match_stack.push_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(bv)));
|
||||
}
|
||||
else if (!match.empty()) {
|
||||
auto bv = buildInt(std::dec, match);
|
||||
auto bv = buildInt(10, match, false);
|
||||
m_match_stack.push_back(make_node<eval::Int_AST_Node>(std::move(match), start.line, start.col, std::move(bv)));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (const std::invalid_argument &) {
|
||||
// error parsing number passed in to buildFloat/buildInt
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
@ -949,6 +903,130 @@ namespace chaiscript
|
||||
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.
|
||||
bool Quoted_String(const bool t_capture = false) {
|
||||
SkipWS();
|
||||
@ -960,19 +1038,20 @@ namespace chaiscript
|
||||
|
||||
if (Quoted_String_()) {
|
||||
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();
|
||||
|
||||
bool is_interpolated = [&]()->bool {
|
||||
Char_Parser<std::string> cparser(match, true);
|
||||
|
||||
|
||||
auto s = start + 1, end = m_position - 1;
|
||||
|
||||
while (s != end) {
|
||||
if (saw_interpolation_marker) {
|
||||
if (cparser.saw_interpolation_marker) {
|
||||
if (*s == '{') {
|
||||
//We've found an interpolation point
|
||||
|
||||
if (is_interpolated) {
|
||||
if (cparser.is_interpolated) {
|
||||
//If we've seen previous interpolation, add on instead of making a new one
|
||||
m_match_stack.push_back(make_node<eval::Quoted_String_AST_Node>(match, start.line, start.col));
|
||||
|
||||
@ -993,7 +1072,7 @@ namespace chaiscript
|
||||
}
|
||||
|
||||
if (*s == '}') {
|
||||
is_interpolated = true;
|
||||
cparser.is_interpolated = true;
|
||||
++s;
|
||||
|
||||
const auto tostr_stack_top = m_match_stack.size();
|
||||
@ -1019,39 +1098,16 @@ namespace chaiscript
|
||||
} else {
|
||||
match.push_back('$');
|
||||
}
|
||||
saw_interpolation_marker = false;
|
||||
cparser.saw_interpolation_marker = false;
|
||||
} else {
|
||||
if (*s == '\\') {
|
||||
if (is_escaped) {
|
||||
match.push_back('\\');
|
||||
is_escaped = false;
|
||||
} else {
|
||||
is_escaped = true;
|
||||
}
|
||||
} else {
|
||||
if (is_escaped) {
|
||||
switch (*s) {
|
||||
case ('b') : match.push_back('\b'); break;
|
||||
case ('f') : match.push_back('\f'); break;
|
||||
case ('n') : match.push_back('\n'); break;
|
||||
case ('r') : match.push_back('\r'); break;
|
||||
case ('t') : match.push_back('\t'); break;
|
||||
case ('\'') : match.push_back('\''); break;
|
||||
case ('\"') : match.push_back('\"'); break;
|
||||
case ('$') : match.push_back('$'); break;
|
||||
default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(start.line, start.col), *m_filename);
|
||||
}
|
||||
} else if (*s == '$') {
|
||||
saw_interpolation_marker = true;
|
||||
} else {
|
||||
match.push_back(*s);
|
||||
}
|
||||
is_escaped = false;
|
||||
}
|
||||
cparser.parse(*s, start.line, start.col, *m_filename);
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
return cparser.is_interpolated;
|
||||
}();
|
||||
|
||||
if (is_interpolated) {
|
||||
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;
|
||||
if (Single_Quoted_String_()) {
|
||||
std::string match;
|
||||
bool is_escaped = false;
|
||||
|
||||
{
|
||||
// scope for cparser destrutor
|
||||
Char_Parser<std::string> cparser(match, false);
|
||||
|
||||
for (auto s = start + 1, end = m_position - 1; s != end; ++s) {
|
||||
if (*s == '\\') {
|
||||
if (is_escaped) {
|
||||
match.push_back('\\');
|
||||
is_escaped = false;
|
||||
} else {
|
||||
is_escaped = true;
|
||||
}
|
||||
} else {
|
||||
if (is_escaped) {
|
||||
switch (*s) {
|
||||
case ('b') : match.push_back('\b'); break;
|
||||
case ('f') : match.push_back('\f'); break;
|
||||
case ('n') : match.push_back('\n'); break;
|
||||
case ('r') : match.push_back('\r'); break;
|
||||
case ('t') : match.push_back('\t'); break;
|
||||
case ('\'') : match.push_back('\''); break;
|
||||
case ('\"') : match.push_back('\"'); break;
|
||||
default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(start.line, start.col), *m_filename);
|
||||
}
|
||||
} else {
|
||||
match.push_back(*s);
|
||||
}
|
||||
is_escaped = false;
|
||||
cparser.parse(*s, start.line, start.col, *m_filename);
|
||||
}
|
||||
}
|
||||
|
||||
m_match_stack.push_back(make_node<eval::Single_Quoted_String_AST_Node>(match, start.line, start.col));
|
||||
return true;
|
||||
}
|
||||
@ -1775,6 +1814,8 @@ namespace chaiscript
|
||||
|
||||
build_match<eval::Case_AST_Node>(prev_stack_top);
|
||||
} else if (Keyword("default")) {
|
||||
retval = true;
|
||||
|
||||
while (Eol()) {}
|
||||
|
||||
if (!Block()) {
|
||||
@ -2373,5 +2414,12 @@ namespace chaiscript
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CHAISCRIPT_MSVC
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("max")
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* CHAISCRIPT_PARSER_HPP_ */
|
||||
|
||||
|
@ -41,25 +41,25 @@ def new(x) {
|
||||
}
|
||||
|
||||
def clone(double x) {
|
||||
double(x).copy_var_attrs(x)
|
||||
double(x).clone_var_attrs(x)
|
||||
}
|
||||
|
||||
def clone(string x) {
|
||||
string(x).copy_var_attrs(x)
|
||||
string(x).clone_var_attrs(x)
|
||||
}
|
||||
|
||||
def clone(vector x) {
|
||||
vector(x).copy_var_attrs(x)
|
||||
vector(x).clone_var_attrs(x)
|
||||
}
|
||||
|
||||
|
||||
def clone(int x) {
|
||||
int(x).copy_var_attrs(x)
|
||||
int(x).clone_var_attrs(x)
|
||||
}
|
||||
|
||||
def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x)
|
||||
{
|
||||
eval(type_name(x))(x).copy_var_attrs(x);
|
||||
eval(type_name(x))(x).clone_var_attrs(x);
|
||||
}
|
||||
|
||||
|
||||
|
647
include/chaiscript/utility/json.hpp
Normal file
647
include/chaiscript/utility/json.hpp
Normal 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
|
158
include/chaiscript/utility/json_wrap.hpp
Normal file
158
include/chaiscript/utility/json_wrap.hpp
Normal 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
103
samples/factory.cpp
Normal 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);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ int main( int /*argc*/ , char * /*argv*/[] )
|
||||
{
|
||||
ch.eval( script );
|
||||
}
|
||||
catch ( std::exception e )
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
printf( " >>> Exception thrown: %s \n" , e.what( ) );
|
||||
}
|
||||
|
12
unittests/bool_comparisons.chai
Normal file
12
unittests/bool_comparisons.chai
Normal 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)
|
||||
|
@ -784,6 +784,23 @@ TEST_CASE("Variable Scope When Calling From C++ 2")
|
||||
CHECK_THROWS(func());
|
||||
}
|
||||
|
||||
void ulonglong(unsigned long long i) {
|
||||
std::cout << i << '\n';
|
||||
}
|
||||
|
||||
|
||||
void longlong(long long i) {
|
||||
std::cout << i << '\n';
|
||||
}
|
||||
|
||||
TEST_CASE("Test long long dispatch")
|
||||
{
|
||||
chaiscript::ChaiScript chai;
|
||||
chai.add(chaiscript::fun(&longlong), "longlong");
|
||||
chai.add(chaiscript::fun(&ulonglong), "ulonglong");
|
||||
chai.eval("longlong(15)");
|
||||
chai.eval("ulonglong(15)");
|
||||
}
|
||||
|
||||
|
||||
struct Returned_Converted_Config
|
||||
|
14
unittests/dynamic_object_dynamic_attrs_explicit.chai
Normal file
14
unittests/dynamic_object_dynamic_attrs_explicit.chai
Normal 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})
|
||||
|
6
unittests/hex_escapes.chai
Normal file
6
unittests/hex_escapes.chai
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
assert_equal("\x39", "9")
|
||||
assert_equal("\x039", "9")
|
||||
assert_equal("\x39g", "9g")
|
||||
assert_equal("b\x39g", "b9g")
|
||||
|
@ -5,9 +5,10 @@
|
||||
template<typename T>
|
||||
bool test_literal(T val, const std::string &str)
|
||||
{
|
||||
std::cout << "Comparing : " << val;
|
||||
chaiscript::ChaiScript chai;
|
||||
T val2 = chai.eval<T>(str);
|
||||
std::cout << "Comparing : " << val << " " << val2 << '\n';
|
||||
std::cout << " " << val2 << '\n';
|
||||
return val == val2;
|
||||
}
|
||||
|
||||
@ -74,6 +75,39 @@ int main()
|
||||
&& TEST_LITERAL(177777777777777777)
|
||||
&& TEST_LITERAL(1777777777777777777)
|
||||
|
||||
&& test_literal(0xF, "0b1111")
|
||||
&& test_literal(0xFF, "0b11111111")
|
||||
&& test_literal(0xFFF, "0b111111111111")
|
||||
&& test_literal(0xFFFF, "0b1111111111111111")
|
||||
&& test_literal(0xFFFFF, "0b11111111111111111111")
|
||||
&& test_literal(0xFFFFFF, "0b111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFF, "0b1111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFF, "0b11111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFF, "0b111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFF, "0b1111111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0xFFFFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111111111111")
|
||||
|
||||
&& test_literal(0x7, "0b111")
|
||||
&& test_literal(0x7F, "0b1111111")
|
||||
&& test_literal(0x7FF, "0b11111111111")
|
||||
&& test_literal(0x7FFF, "0b111111111111111")
|
||||
&& test_literal(0x7FFFF, "0b1111111111111111111")
|
||||
&& test_literal(0x7FFFFF, "0b11111111111111111111111")
|
||||
&& test_literal(0x7FFFFFF, "0b111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFF, "0b1111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFF, "0b11111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFF, "0b111111111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFFF, "0b1111111111111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111111")
|
||||
&& test_literal(0x7FFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111111")
|
||||
)
|
||||
{
|
||||
return EXIT_SUCCESS;
|
||||
|
3
unittests/json_1.chai
Normal file
3
unittests/json_1.chai
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
assert_true(from_json("null").is_var_null())
|
1
unittests/json_10.chai
Normal file
1
unittests/json_10.chai
Normal 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
14
unittests/json_11.chai
Normal 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
1
unittests/json_12.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("\"\""), "")
|
1
unittests/json_13.chai
Normal file
1
unittests/json_13.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("1.20E+2"), 1.20e2)
|
3
unittests/json_2.chai
Normal file
3
unittests/json_2.chai
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
assert_true(from_json("true"))
|
||||
|
1
unittests/json_3.chai
Normal file
1
unittests/json_3.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("100"), 100)
|
1
unittests/json_4.chai
Normal file
1
unittests/json_4.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("1.234"), 1.234)
|
1
unittests/json_5.chai
Normal file
1
unittests/json_5.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("\"StringTest\""), "StringTest")
|
1
unittests/json_6.chai
Normal file
1
unittests/json_6.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("{}"), Map())
|
4
unittests/json_7.chai
Normal file
4
unittests/json_7.chai
Normal file
@ -0,0 +1,4 @@
|
||||
assert_equal(from_json("\n" +
|
||||
"{\n" +
|
||||
" \"Key\" : \"Value\"\n" +
|
||||
"}\n"), ["Key":"Value"])
|
1
unittests/json_8.chai
Normal file
1
unittests/json_8.chai
Normal file
@ -0,0 +1 @@
|
||||
assert_equal(from_json("[]"), [])
|
2
unittests/json_9.chai
Normal file
2
unittests/json_9.chai
Normal file
@ -0,0 +1,2 @@
|
||||
assert_equal(from_json("[1,2,3]"), [1,2,3])
|
||||
|
6
unittests/json_roundtrip.chai
Normal file
6
unittests/json_roundtrip.chai
Normal 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)
|
||||
|
||||
|
@ -2,8 +2,8 @@ assert_equal(true, int_type.bare_equal(1.get_type_info()))
|
||||
assert_equal(true, unsigned_int_type.bare_equal(1u.get_type_info()))
|
||||
assert_equal(true, unsigned_long_type.bare_equal(1lu.get_type_info()))
|
||||
assert_equal(true, long_type.bare_equal(1l.get_type_info()))
|
||||
assert_equal(true, int64_t_type.bare_equal(1ll.get_type_info()))
|
||||
assert_equal(true, uint64_t_type.bare_equal(1ull.get_type_info()))
|
||||
assert_equal(true, long_long_type.bare_equal(1ll.get_type_info()))
|
||||
assert_equal(true, unsigned_long_long_type.bare_equal(1ull.get_type_info()))
|
||||
|
||||
assert_equal(true, double_type.bare_equal(1.6.get_type_info()))
|
||||
assert_equal(true, float_type.bare_equal(1.6f.get_type_info()))
|
||||
|
6
unittests/octal_escapes.chai
Normal file
6
unittests/octal_escapes.chai
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
assert_equal("\71", "9")
|
||||
assert_equal("\071", "9")
|
||||
assert_equal("\71a", "9a")
|
||||
assert_equal("b\71a", "b9a")
|
||||
|
7
unittests/operator_overload3.chai
Normal file
7
unittests/operator_overload3.chai
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
def string::`/=`(double d) { this = "${this}/=${d}"; return this; }
|
||||
|
||||
var s = "Hello World"
|
||||
s /= 2
|
||||
|
6
unittests/operator_overload4.chai
Normal file
6
unittests/operator_overload4.chai
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
def string::`*`(double d) { return "${this} * ${d}"; }
|
||||
|
||||
"Hello World" * 2
|
||||
|
18
unittests/switch_default_2.chai
Normal file
18
unittests/switch_default_2.chai
Normal 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)
|
18
unittests/vector_assignment.chai
Normal file
18
unittests/vector_assignment.chai
Normal 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")
|
||||
|
||||
|
5
unittests/vector_assignment_3.chai
Normal file
5
unittests/vector_assignment_3.chai
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
var v = []
|
||||
v.push_back(int(1));
|
||||
v[0] = 3;
|
||||
|
Loading…
x
Reference in New Issue
Block a user