Merge github.com:ChaiScript/ChaiScript into develop

This commit is contained in:
njlr 2017-07-21 11:06:10 +01:00
commit f640784d56
35 changed files with 1152 additions and 373 deletions

View File

@ -8,6 +8,7 @@ addons:
packages:
- g++-4.9
- g++-5
- g++-6
coverity_scan:
project:
name: "ChaiScript/ChaiScript"
@ -25,12 +26,23 @@ matrix:
compiler: gcc
- os: linux
sudo: false
env: GCC_VER="5"
env: GCC_VER="4.9" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1
compiler: gcc
- os: linux
sudo: false
env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"
compiler: gcc
- os: linux
sudo: false
env: GCC_VER="6" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"
compiler: gcc
- os: osx
compiler: clang
osx_image: xcode8
- os: osx
compiler: clang
osx_image: xcode8
env: CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1
env:
global:
@ -40,14 +52,13 @@ env:
before_install:
- if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi
- if [ "${GCC_VER}" == "5" ]; then export CPPCHECK=1 COVERAGE=1 FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE" ; fi
- pip install --user cpp-coveralls
script:
- cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD .
- cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $CMAKE_OPTIONS .
- cmake --build . -- -j2
- ctest
- if [ ${COVERAGE} = 1 ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi
- if [ "${BUILD_ONLY}" != "1" ]; then ctest; fi
- if [ "${COVERAGE}" = "1" ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi
#after_script:
# - if [ ${CPPCHECK} = 1 ]; then contrib/codeanalysis/runcppcheck.sh ; fi

View File

@ -14,6 +14,7 @@ ELSE()
project(chaiscript)
option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE)
option(DYNLOAD_ENABLED "Dynamic Loading Support Enabled" TRUE)
option(BUILD_MODULES "Build Extra Modules (stl)" TRUE)
@ -21,6 +22,7 @@ option(BUILD_SAMPLES "Build Samples Folder" FALSE)
option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE)
option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE)
option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE)
option(BUILD_IN_CPP17_MODE "Build with C++17 flags" FALSE)
mark_as_advanced(USE_STD_MAKE_SHARED)
@ -149,12 +151,20 @@ if(CMAKE_COMPILER_IS_GNUCC)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if(GCC_VERSION VERSION_LESS 4.9)
set(CPP11_FLAG "-std=c++1y")
set(CPP14_FLAG "-std=c++1y")
else()
set(CPP11_FLAG "-std=c++14")
if (BUILD_IN_CPP17_MODE)
set(CPP14_FLAG "-std=c++1z")
else()
set(CPP14_FLAG "-std=c++14")
endif()
endif()
else()
set(CPP11_FLAG "-std=c++14")
if (BUILD_IN_CPP17_MODE)
set(CPP14_FLAG "-std=c++1z")
else()
set(CPP14_FLAG "-std=c++14")
endif()
endif()
if(MSVC)
@ -178,7 +188,7 @@ if(MSVC)
# how to workaround or fix the error. So I'm disabling it globally.
add_definitions(/wd4503)
else()
add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG})
add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP14_FLAG})
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command)
@ -196,12 +206,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if(USE_LIBCXX)
add_definitions(-stdlib=libc++)
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG} -stdlib=libc++")
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG} -stdlib=libc++")
else()
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}")
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}")
endif()
elseif(CMAKE_COMPILER_IS_GNUCC)
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}")
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}")
endif()
# limitations in MinGW require us to make an optimized build
@ -221,9 +231,15 @@ if(NOT MULTITHREAD_SUPPORT_ENABLED)
add_definitions(-DCHAISCRIPT_NO_THREADS)
endif()
if(NOT DYNLOAD_ENABLED)
add_definitions(-DCHAISCRIPT_NO_DYNLOAD)
endif()
if(CMAKE_HOST_UNIX)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
list(APPEND LIBS "dl")
if(DYNLOAD_ENABLED)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
list(APPEND LIBS "dl")
endif()
endif()
if(MULTITHREAD_SUPPORT_ENABLED)
@ -298,32 +314,17 @@ if (RUN_FUZZY_TESTS)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2016-06-29.tar.bz2
COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2017-07-20.tar.bz2
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests
)
file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*)
list(SORT FUZZY_CRASH_TESTS)
file(GLOB FUZZY_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/MINIMIZED/*)
list(SORT FUZZY_TESTS)
file(GLOB FUZZY_EXCEPTION_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/id*)
list(SORT FUZZY_EXCEPTION_TESTS)
foreach(filename ${FUZZY_CRASH_TESTS})
foreach(filename ${FUZZY_TESTS})
message(STATUS "Adding test ${filename}")
add_test(${filename} chai "-e" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
endforeach()
set_property(TEST ${FUZZY_CRASH_TESTS}
PROPERTY ENVIRONMENT
"CHAI_USE_PATH=${CMAKE_BINARY_DIR}/unittests/"
"CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/"
)
foreach(filename ${FUZZY_EXCEPTION_TESTS})
message(STATUS "Adding test ${filename}")
add_test(${filename} chai "--exception" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
add_test(fuzz.${filename} chai "-e" "--exception" "--any-exception" ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzz_unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
endforeach()
set_property(TEST ${FUZZY_EXCEPTION_TESTS}
@ -427,6 +428,9 @@ if(BUILD_TESTING)
target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS})
ADD_CATCH_TESTS(compiled_tests)
add_executable(static_chaiscript_test unittests/static_chaiscript.cpp)
target_link_libraries(static_chaiscript_test ${LIBS})
add_test(NAME Static_ChaiScript_Test COMMAND static_chaiscript_test)
add_executable(boxed_cast_test unittests/boxed_cast_test.cpp)
target_link_libraries(boxed_cast_test ${LIBS})

View File

@ -1,15 +1,17 @@
version: 5.8.x.{build}
os: Visual Studio 2015
image:
- Visual Studio 2017
environment:
matrix:
- {}
- VS_VERSION: "Visual Studio 14"
- VS_VERSION: "Visual Studio 15"
build_script:
- cmd: >-
mkdir build
cd build
cmake c:\Projects\chaiscript -G "Visual Studio 14"
cmake c:\Projects\chaiscript -G "%VS_VERSION%"
cmake --build . --config Debug
test_script:

View File

@ -15,6 +15,9 @@ chaiscript::ChaiScript chai; // loads stdlib from loadable module on file system
chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); // compiles in stdlib
```
Note that ChaiScript cannot be used as a global / static object unless it is being compiled with `CHAISCRIPT_NO_THREADS`.
# Adding Things To The Engine
## Adding a Function / Method / Member

View File

@ -832,7 +832,7 @@ namespace chaiscript
public:
ChaiScript(std::vector<std::string> t_modulepaths = {},
std::vector<std::string> t_usepaths = {},
const std::vector<Options> &t_opts = {})
const std::vector<Options> &t_opts = chaiscript::default_options())
: ChaiScript_Basic(
chaiscript::Std_Lib::library(),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),

View File

@ -93,6 +93,16 @@ namespace chaiscript {
#endif
}
template<typename B, typename D, typename ...Arg>
inline std::unique_ptr<B> make_unique(Arg && ... arg)
{
#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED
return std::make_unique<D>(std::forward<Arg>(arg)...);
#else
return std::unique_ptr<B>(static_cast<B*>(new D(std::forward<Arg>(arg)...)));
#endif
}
struct Build_Info {
static int version_major()
{
@ -216,7 +226,11 @@ namespace chaiscript {
static inline std::vector<Options> default_options()
{
#ifdef CHAISCRIPT_NO_DYNLOAD
return {Options::No_Load_Modules, Options::External_Scripts};
#else
return {Options::Load_Modules, Options::External_Scripts};
#endif
}
}
#endif

View File

@ -67,44 +67,44 @@ namespace chaiscript
class Thread_Storage
{
public:
explicit Thread_Storage(void *t_key)
: m_key(t_key)
{
}
Thread_Storage() = default;
Thread_Storage(const Thread_Storage &) = delete;
Thread_Storage(Thread_Storage &&) = delete;
Thread_Storage &operator=(const Thread_Storage &) = delete;
Thread_Storage &operator=(Thread_Storage &&) = delete;
~Thread_Storage()
{
t().erase(m_key);
t().erase(this);
}
inline const T *operator->() const
{
return &(t()[m_key]);
return &(t()[this]);
}
inline const T &operator*() const
{
return t()[m_key];
return t()[this];
}
inline T *operator->()
{
return &(t()[m_key]);
return &(t()[this]);
}
inline T &operator*()
{
return t()[m_key];
return t()[this];
}
void *m_key;
private:
static std::unordered_map<void*, T> &t()
static std::unordered_map<const void*, T> &t()
{
thread_local static std::unordered_map<void *, T> my_t;
thread_local std::unordered_map<const void *, T> my_t;
return my_t;
}
};
@ -144,7 +144,7 @@ namespace chaiscript
class Thread_Storage
{
public:
explicit Thread_Storage(void *)
explicit Thread_Storage()
{
}

View File

@ -305,13 +305,13 @@ namespace chaiscript
static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
{
const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
return pf && pf->get_parse_tree();
return bool(pf);
}
static chaiscript::AST_NodePtr get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
{
const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
if (pf && pf->get_parse_tree())
if (pf)
{
return pf->get_parse_tree();
} else {
@ -545,7 +545,7 @@ namespace chaiscript
std::vector<Boxed_Value> retval;
std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(),
std::back_inserter(retval),
&chaiscript::var<const std::shared_ptr<const chaiscript::AST_Node> &>);
&chaiscript::var<const chaiscript::AST_Node_Trace &>);
return retval;
}), "call_stack"} }
);
@ -574,7 +574,7 @@ namespace chaiscript
const auto children = t_node.get_children();
std::transform(children.begin(), children.end(),
std::back_inserter(retval),
&chaiscript::var<const std::shared_ptr<chaiscript::AST_Node> &>);
&chaiscript::var<const std::reference_wrapper<chaiscript::AST_Node> &>);
return retval;
}), "children"}
}

View File

@ -335,9 +335,24 @@ namespace chaiscript
template<typename ContainerType>
void back_insertion_sequence_type(const std::string &type, Module& m)
{
typedef typename ContainerType::reference (ContainerType::*backptr)();
m.add(fun(static_cast<backptr>(&ContainerType::back)), "back");
m.add(fun([](ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.back());
}
}
)
, "back");
m.add(fun([](const ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.back());
}
}
)
, "back");
typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &);
@ -380,13 +395,29 @@ namespace chaiscript
template<typename ContainerType>
void front_insertion_sequence_type(const std::string &type, Module& m)
{
typedef typename ContainerType::reference (ContainerType::*front_ptr)();
typedef typename ContainerType::const_reference (ContainerType::*const_front_ptr)() const;
typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference);
typedef void (ContainerType::*pop_ptr)();
m.add(fun(static_cast<front_ptr>(&ContainerType::front)), "front");
m.add(fun(static_cast<const_front_ptr>(&ContainerType::front)), "front");
m.add(fun([](ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun([](const ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)),
[&]()->std::string{
@ -577,11 +608,27 @@ namespace chaiscript
{
m.add(user_type<VectorType>(), type);
typedef typename VectorType::reference (VectorType::*frontptr)();
typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const;
m.add(fun([](VectorType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun([](const VectorType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun(static_cast<frontptr>(&VectorType::front)), "front");
m.add(fun(static_cast<constfrontptr>(&VectorType::front)), "front");
back_insertion_sequence_type<VectorType>(type, m);

View File

@ -452,7 +452,7 @@ namespace chaiscript
};
explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser)
: m_stack_holder(this),
: m_stack_holder(),
m_parser(parser)
{
}

View File

@ -181,9 +181,9 @@ namespace chaiscript
template<typename Ret>
struct Handle_Return<const Ret>
{
static Boxed_Value handle(const Ret &r)
static Boxed_Value handle(Ret r)
{
return Boxed_Value(std::cref(r));
return Boxed_Value(std::move(r));
}
};

View File

@ -41,7 +41,7 @@ namespace chaiscript
class Boxed_Number;
struct AST_Node;
typedef std::shared_ptr<AST_Node> AST_NodePtr;
typedef std::unique_ptr<AST_Node> AST_NodePtr;
namespace dispatch
{
@ -346,14 +346,15 @@ namespace chaiscript
{
public:
Dynamic_Proxy_Function(
int t_arity=-1,
AST_NodePtr t_parsenode = AST_NodePtr(),
const int t_arity,
std::shared_ptr<AST_Node> t_parsenode,
Param_Types t_param_types = Param_Types(),
Proxy_Function t_guard = Proxy_Function())
: Proxy_Function_Base(build_param_type_list(t_param_types), t_arity),
m_param_types(std::move(t_param_types)),
m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode))
{
// assert(t_parsenode);
}
@ -379,9 +380,17 @@ namespace chaiscript
return m_guard;
}
AST_NodePtr get_parse_tree() const
bool has_parse_tree() const {
return static_cast<bool>(m_parsenode);
}
const AST_Node &get_parse_tree() const
{
return m_parsenode;
if (m_parsenode) {
return *m_parsenode;
} else {
throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree");
}
}
@ -445,7 +454,7 @@ namespace chaiscript
private:
Proxy_Function m_guard;
AST_NodePtr m_parsenode;
std::shared_ptr<AST_Node> m_parsenode;
};
@ -457,7 +466,7 @@ namespace chaiscript
Dynamic_Proxy_Function_Impl(
Callable t_f,
int t_arity=-1,
AST_NodePtr t_parsenode = AST_NodePtr(),
std::shared_ptr<AST_Node> t_parsenode = AST_NodePtr(),
Param_Types t_param_types = Param_Types(),
Proxy_Function t_guard = Proxy_Function())
: Dynamic_Proxy_Function(

View File

@ -48,6 +48,7 @@ namespace chaiscript
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Signature, T>>(t));
}
template<typename Ret, typename ... Param>
Proxy_Function fun(Ret (*func)(Param...))
{
@ -77,13 +78,45 @@ namespace chaiscript
}
template<typename T, typename Class /*, typename = typename std::enable_if<std::is_member_object_pointer<T>::value>::type*/>
Proxy_Function fun(T Class::* m /*, typename std::enable_if<std::is_member_object_pointer<T>::value>::type* = 0*/ )
{
return Proxy_Function(chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Attribute_Access<T, Class>>(m));
}
// only compile this bit if noexcept is part of the type system
//
#if __cpp_noexcept_function_type >= 201510
template<typename Ret, typename ... Param>
Proxy_Function fun(Ret (*func)(Param...) noexcept)
{
auto fun_call = dispatch::detail::Fun_Caller<Ret, Param...>(func);
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Param...), decltype(fun_call)>>(fun_call));
}
template<typename Ret, typename Class, typename ... Param>
Proxy_Function fun(Ret (Class::*t_func)(Param...) const noexcept)
{
auto call = dispatch::detail::Const_Caller<Ret, Class, Param...>(t_func);
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (const Class &, Param...), decltype(call)>>(call));
}
template<typename Ret, typename Class, typename ... Param>
Proxy_Function fun(Ret (Class::*t_func)(Param...) noexcept)
{
auto call = dispatch::detail::Caller<Ret, Class, Param...>(t_func);
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Class &, Param...), decltype(call)>>(call));
}
#endif

View File

@ -338,9 +338,7 @@ namespace chaiscript
: m_mutex(),
m_conversions(),
m_convertableTypes(),
m_num_types(0),
m_thread_cache(this),
m_conversion_saves(this)
m_num_types(0)
{
}

View File

@ -124,8 +124,10 @@ namespace chaiscript
/// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree
typedef std::shared_ptr<AST_Node> AST_NodePtr;
typedef std::shared_ptr<const AST_Node> AST_NodePtr_Const;
typedef std::unique_ptr<AST_Node> AST_NodePtr;
typedef std::unique_ptr<const AST_Node> AST_NodePtr_Const;
struct AST_Node_Trace;
/// \brief Classes which may be thrown during error cases when ChaiScript is executing.
@ -168,7 +170,7 @@ namespace chaiscript
File_Position start_position;
std::string filename;
std::string detail;
std::vector<AST_NodePtr_Const> call_stack;
std::vector<AST_Node_Trace> call_stack;
eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname,
const std::vector<Boxed_Value> &t_parameters, const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
@ -228,26 +230,26 @@ namespace chaiscript
template<typename T>
static AST_Node_Type id(const T& t)
{
return t->identifier;
return t.identifier;
}
template<typename T>
static std::string pretty(const T& t)
{
return t->pretty_print();
return t.pretty_print();
}
template<typename T>
static const std::string &fname(const T& t)
{
return t->filename();
return t.filename();
}
template<typename T>
static std::string startpos(const T& t)
{
std::ostringstream oss;
oss << t->start().line << ", " << t->start().column;
oss << t.start().line << ", " << t.start().column;
return oss.str();
}
@ -260,6 +262,7 @@ namespace chaiscript
bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss)
{
assert(t_func);
int arity = t_func->get_arity();
std::vector<Type_Info> types = t_func->get_param_types();
@ -308,14 +311,14 @@ namespace chaiscript
std::shared_ptr<const dispatch::Dynamic_Proxy_Function> dynfun
= std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_func);
if (dynfun)
if (dynfun && dynfun->has_parse_tree())
{
Proxy_Function f = dynfun->get_guard();
if (f)
{
auto dynfunguard = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(f);
if (dynfunguard)
if (dynfunguard && dynfunguard->has_parse_tree())
{
retval += " : " + format_guard(dynfunguard->get_parse_tree());
}
@ -330,20 +333,15 @@ namespace chaiscript
template<typename T>
static std::string format_guard(const T &t)
{
return t->pretty_print();
return t.pretty_print();
}
template<typename T>
static std::string format_location(const T &t)
{
if (t) {
std::ostringstream oss;
oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")";
return oss.str();
} else {
return "(internal)";
}
std::ostringstream oss;
oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")";
return oss.str();
}
static std::string format_detail(const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
@ -353,6 +351,7 @@ namespace chaiscript
std::stringstream ss;
if (t_functions.size() == 1)
{
assert(t_functions[0]);
ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n';
} else {
ss << " " << t_functions.size() << " overloads available:\n";
@ -492,7 +491,7 @@ namespace chaiscript
/// \brief Struct that doubles as both a parser ast_node and an AST node.
struct AST_Node : std::enable_shared_from_this<AST_Node> {
struct AST_Node {
public:
const AST_Node_Type identifier;
const std::string text;
@ -516,14 +515,14 @@ namespace chaiscript
oss << text;
for (auto & elem : this->get_children()) {
oss << elem->pretty_print() << ' ';
for (auto & elem : get_children()) {
oss << elem.get().pretty_print() << ' ';
}
return oss.str();
}
virtual std::vector<AST_NodePtr> get_children() const = 0;
virtual std::vector<std::reference_wrapper<AST_Node>> get_children() const = 0;
virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0;
@ -534,8 +533,8 @@ namespace chaiscript
oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") "
<< this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n';
for (auto & elem : this->get_children()) {
oss << elem->to_string(t_prepend + " ");
for (auto & elem : get_children()) {
oss << elem.get().to_string(t_prepend + " ");
}
return oss.str();
}
@ -568,12 +567,60 @@ namespace chaiscript
};
struct AST_Node_Trace
{
const AST_Node_Type identifier;
const std::string text;
Parse_Location location;
const std::string &filename() const {
return *location.filename;
}
const File_Position &start() const {
return location.start;
}
const File_Position &end() const {
return location.end;
}
std::string pretty_print() const
{
std::ostringstream oss;
oss << text;
for (const auto & elem : children) {
oss << elem.pretty_print() << ' ';
}
return oss.str();
}
std::vector<AST_Node_Trace> get_children(const AST_Node &node)
{
const auto node_children = node.get_children();
return std::vector<AST_Node_Trace>(node_children.begin(), node_children.end());
}
AST_Node_Trace(const AST_Node &node)
: identifier(node.identifier), text(node.text),
location(node.location), children(get_children(node))
{
}
std::vector<AST_Node_Trace> children;
};
namespace parser {
class ChaiScript_Parser_Base
{
public:
virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0;
virtual void debug_print(AST_NodePtr t, std::string prepend = "") const = 0;
virtual void debug_print(const AST_Node &t, std::string prepend = "") const = 0;
virtual void *get_tracer_ptr() = 0;
virtual ~ChaiScript_Parser_Base() = default;
ChaiScript_Parser_Base() = default;

View File

@ -36,12 +36,13 @@
#include <unistd.h>
#endif
#if defined(_POSIX_VERSION) && !defined(__CYGWIN__)
#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
#include <dlfcn.h>
#endif
#ifdef CHAISCRIPT_WINDOWS
#if defined(CHAISCRIPT_NO_DYNLOAD)
#include "chaiscript_unknown.hpp"
#elif defined(CHAISCRIPT_WINDOWS)
#include "chaiscript_windows.hpp"
#elif _POSIX_VERSION
#include "chaiscript_posix.hpp"
@ -185,7 +186,7 @@ namespace chaiscript
}
m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval");
m_engine.add(fun([this](const AST_NodePtr &t_ast){ return eval(t_ast); }), "eval");
m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval");
m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse");
m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse");
@ -242,7 +243,7 @@ namespace chaiscript
m_parser(std::move(parser)),
m_engine(*m_parser)
{
#if defined(_POSIX_VERSION) && !defined(__CYGWIN__)
#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
// If on Unix, add the path of the current executable to the module search path
// as windows would do
@ -278,6 +279,7 @@ namespace chaiscript
build_eval_system(t_lib, t_opts);
}
#ifndef CHAISCRIPT_NO_DYNLOAD
/// \brief Constructor for ChaiScript.
///
/// This version of the ChaiScript constructor attempts to find the stdlib module to load
@ -307,16 +309,22 @@ namespace chaiscript
throw;
}
}
#else // CHAISCRIPT_NO_DYNLOAD
explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
std::vector<std::string> t_module_paths = {},
std::vector<std::string> t_use_paths = {},
const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options()) = delete;
#endif
parser::ChaiScript_Parser_Base &get_parser()
{
return *m_parser;
}
const Boxed_Value eval(const AST_NodePtr &t_ast)
const Boxed_Value eval(const AST_Node &t_ast)
{
try {
return t_ast->eval(chaiscript::detail::Dispatch_State(m_engine));
return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine));
} catch (const exception::eval_error &t_ee) {
throw Boxed_Value(t_ee);
}
@ -324,9 +332,9 @@ namespace chaiscript
AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false)
{
const auto ast = m_parser->parse(t_input, "PARSE");
auto ast = m_parser->parse(t_input, "PARSE");
if (t_debug_print) {
m_parser->debug_print(ast);
m_parser->debug_print(*ast);
}
return ast;
}
@ -548,6 +556,10 @@ namespace chaiscript
/// \throw chaiscript::exception::load_module_error In the event that no matching module can be found.
std::string load_module(const std::string &t_module_name)
{
#ifdef CHAISCRIPT_NO_DYNLOAD
(void)t_module_name; // -Wunused-parameter
throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
#else
std::vector<exception::load_module_error> errors;
std::string version_stripped_name = t_module_name;
size_t version_pos = version_stripped_name.find("-" + Build_Info::version());
@ -581,6 +593,7 @@ namespace chaiscript
}
throw chaiscript::exception::load_module_error(t_module_name, errors);
#endif
}
/// \brief Load a binary module from a dynamic library. Works on platforms that support

View File

@ -47,13 +47,13 @@ namespace chaiscript
{
template<typename T> struct AST_Node_Impl;
template<typename T> using AST_Node_Impl_Ptr = typename std::shared_ptr<AST_Node_Impl<T>>;
template<typename T> using AST_Node_Impl_Ptr = typename std::unique_ptr<AST_Node_Impl<T>>;
namespace detail
{
/// Helper function that will set up the scope around a function call, including handling the named function parameters
template<typename T>
static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl_Ptr<T> &t_node, const std::vector<std::string> &t_param_names, const std::vector<Boxed_Value> &t_vals, const std::map<std::string, Boxed_Value> *t_locals=nullptr, bool has_this_capture = false) {
static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl<T> &t_node, const std::vector<std::string> &t_param_names, const std::vector<Boxed_Value> &t_vals, const std::map<std::string, Boxed_Value> *t_locals=nullptr, bool has_this_capture = false) {
chaiscript::detail::Dispatch_State state(t_ss);
const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{
@ -83,7 +83,7 @@ namespace chaiscript
}
try {
return t_node->eval(state);
return t_node.eval(state);
} catch (detail::Return_Value &rv) {
return std::move(rv.retval);
}
@ -106,8 +106,14 @@ namespace chaiscript
}
std::vector<AST_NodePtr> get_children() const final {
return {children.begin(), children.end()};
std::vector<std::reference_wrapper<AST_Node>> get_children() const final {
std::vector<std::reference_wrapper<AST_Node>> retval;
retval.reserve(children.size());
for (auto &&child : children) {
retval.emplace_back(*child);
}
return retval;
}
Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final
@ -116,7 +122,7 @@ namespace chaiscript
T::trace(t_e, this);
return eval_internal(t_e);
} catch (exception::eval_error &ee) {
ee.call_stack.push_back(shared_from_this());
ee.call_stack.push_back(*this);
throw;
}
}
@ -282,7 +288,9 @@ namespace chaiscript
template<typename T>
struct Fun_Call_AST_Node : AST_Node_Impl<T> {
Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { }
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) {
assert(!this->children.empty());
}
template<bool Save_Params>
Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const
@ -364,44 +372,44 @@ namespace chaiscript
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { }
static std::string get_arg_name(const AST_Node_Impl_Ptr<T> &t_node) {
if (t_node->children.empty())
static std::string get_arg_name(const AST_Node_Impl<T> &t_node) {
if (t_node.children.empty())
{
return t_node->text;
} else if (t_node->children.size() == 1) {
return t_node->children[0]->text;
return t_node.text;
} else if (t_node.children.size() == 1) {
return t_node.children[0]->text;
} else {
return t_node->children[1]->text;
return t_node.children[1]->text;
}
}
static std::vector<std::string> get_arg_names(const AST_Node_Impl_Ptr<T> &t_node) {
static std::vector<std::string> get_arg_names(const AST_Node_Impl<T> &t_node) {
std::vector<std::string> retval;
for (const auto &node : t_node->children)
for (const auto &node : t_node.children)
{
retval.push_back(get_arg_name(node));
retval.push_back(get_arg_name(*node));
}
return retval;
}
static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl_Ptr<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss)
static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss)
{
if (t_node->children.size() < 2)
if (t_node.children.size() < 2)
{
return {};
} else {
return {t_node->children[0]->text, t_ss->get_type(t_node->children[0]->text, false)};
return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)};
}
}
static dispatch::Param_Types get_arg_types(const AST_Node_Impl_Ptr<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
static dispatch::Param_Types get_arg_types(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
std::vector<std::pair<std::string, Type_Info>> retval;
for (const auto &child : t_node->children)
for (const auto &child : t_node.children)
{
retval.push_back(get_arg_type(child, t_ss));
retval.push_back(get_arg_type(*child, t_ss));
}
return dispatch::Param_Types(std::move(retval));
@ -621,9 +629,15 @@ namespace chaiscript
template<typename T>
struct Lambda_AST_Node final : AST_Node_Impl<T> {
Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), std::move(t_children)),
m_param_names(Arg_List_AST_Node<T>::get_arg_names(this->children[1])),
m_this_capture(has_this_capture(this->children[0]->children))
AST_Node_Impl<T>(t_ast_node_text,
AST_Node_Type::Lambda,
std::move(t_loc),
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::make_move_iterator(std::prev(t_children.end())))
),
m_param_names(Arg_List_AST_Node<T>::get_arg_names(*this->children[1])),
m_this_capture(has_this_capture(this->children[0]->children)),
m_lambda_node(std::move(t_children.back()))
{ }
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
@ -637,18 +651,18 @@ namespace chaiscript
}();
const auto numparams = this->children[1]->children.size();
const auto param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[1], t_ss);
const auto param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
const auto &lambda_node = this->children.back();
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
return Boxed_Value(
dispatch::make_dynamic_proxy_function(
[engine, lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture](const std::vector<Boxed_Value> &t_params)
[engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures,
this_capture = this->m_this_capture] (const std::vector<Boxed_Value> &t_params)
{
return detail::eval_function(engine, lambda_node, param_names, t_params, &captures, this_capture);
return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture);
},
static_cast<int>(numparams), lambda_node, param_types
static_cast<int>(numparams), m_lambda_node, param_types
)
);
}
@ -664,7 +678,7 @@ namespace chaiscript
private:
const std::vector<std::string> m_param_names;
const bool m_this_capture = false;
const std::shared_ptr<AST_Node_Impl<T>> m_lambda_node;
};
template<typename T>
@ -699,55 +713,81 @@ namespace chaiscript
template<typename T>
struct Def_AST_Node final : AST_Node_Impl<T> {
std::shared_ptr<AST_Node_Impl<T>> m_body_node;
std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), std::move(t_children)) { }
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc),
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1)))
),
m_body_node(get_body_node(std::move(t_children))),
m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2))
{ }
static std::shared_ptr<AST_Node_Impl<T>> get_guard_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec, bool has_guard)
{
if (has_guard) {
return std::move(*std::prev(vec.end(), 2));
} else {
return {};
}
}
static std::shared_ptr<AST_Node_Impl<T>> get_body_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec)
{
return std::move(vec.back());
}
static bool has_guard(const std::vector<AST_Node_Impl_Ptr<T>> &t_children, const std::size_t offset)
{
if ((t_children.size() > 2 + offset) && (t_children[1+offset]->identifier == AST_Node_Type::Arg_List)) {
if (t_children.size() > 3 + offset) {
return true;
}
}
else {
if (t_children.size() > 2 + offset) {
return true;
}
}
return false;
}
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
std::vector<std::string> t_param_names;
size_t numparams = 0;
AST_Node_Impl_Ptr<T> guardnode;
dispatch::Param_Types param_types;
if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) {
if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) {
numparams = this->children[1]->children.size();
t_param_names = Arg_List_AST_Node<T>::get_arg_names(this->children[1]);
param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[1], t_ss);
if (this->children.size() > 3) {
guardnode = this->children[2];
}
}
else {
//no parameters
numparams = 0;
if (this->children.size() > 2) {
guardnode = this->children[1];
}
t_param_names = Arg_List_AST_Node<T>::get_arg_names(*this->children[1]);
param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
}
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
std::shared_ptr<dispatch::Proxy_Function_Base> guard;
if (guardnode) {
if (m_guard_node) {
guard = dispatch::make_dynamic_proxy_function(
[engine, guardnode, t_param_names](const std::vector<Boxed_Value> &t_params)
[engine, guardnode = m_guard_node, t_param_names](const std::vector<Boxed_Value> &t_params)
{
return detail::eval_function(engine, guardnode, t_param_names, t_params);
return detail::eval_function(engine, *guardnode, t_param_names, t_params);
},
static_cast<int>(numparams), guardnode);
static_cast<int>(numparams), m_guard_node);
}
try {
const std::string & l_function_name = this->children[0]->text;
const auto & func_node = this->children.back();
t_ss->add(
dispatch::make_dynamic_proxy_function(
[engine, guardnode, func_node, t_param_names](const std::vector<Boxed_Value> &t_params)
[engine, func_node = m_body_node, t_param_names](const std::vector<Boxed_Value> &t_params)
{
return detail::eval_function(engine, func_node, t_param_names, t_params);
return detail::eval_function(engine, *func_node, t_param_names, t_params);
},
static_cast<int>(numparams), this->children.back(),
static_cast<int>(numparams), m_body_node,
param_types, guard), l_function_name);
} catch (const exception::name_conflict_error &e) {
throw exception::eval_error("Function redefined '" + e.name() + "'");
@ -1230,32 +1270,32 @@ namespace chaiscript
}
for (size_t i = 1; i < end_point; ++i) {
chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss);
AST_Node_Impl_Ptr<T> catch_block = this->children[i];
auto &catch_block = *this->children[i];
if (catch_block->children.size() == 1) {
if (catch_block.children.size() == 1) {
//No variable capture, no guards
retval = catch_block->children[0]->eval(t_ss);
retval = catch_block.children[0]->eval(t_ss);
break;
} else if (catch_block->children.size() == 2 || catch_block->children.size() == 3) {
const auto name = Arg_List_AST_Node<T>::get_arg_name(catch_block->children[0]);
} else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) {
const auto name = Arg_List_AST_Node<T>::get_arg_name(*catch_block.children[0]);
if (dispatch::Param_Types(
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(catch_block->children[0], t_ss)}
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(*catch_block.children[0], t_ss)}
).match(std::vector<Boxed_Value>{t_except}, t_ss.conversions()).first)
{
t_ss.add_object(name, t_except);
if (catch_block->children.size() == 2) {
if (catch_block.children.size() == 2) {
//Variable capture, no guards
retval = catch_block->children[1]->eval(t_ss);
retval = catch_block.children[1]->eval(t_ss);
break;
}
else if (catch_block->children.size() == 3) {
else if (catch_block.children.size() == 3) {
//Variable capture, guards
bool guard = false;
try {
guard = boxed_cast<bool>(catch_block->children[1]->eval(t_ss));
guard = boxed_cast<bool>(catch_block.children[1]->eval(t_ss));
} catch (const exception::bad_boxed_cast &) {
if (this->children.back()->identifier == AST_Node_Type::Finally) {
this->children.back()->children[0]->eval(t_ss);
@ -1263,7 +1303,7 @@ namespace chaiscript
throw exception::eval_error("Guard condition not boolean");
}
if (guard) {
retval = catch_block->children[2]->eval(t_ss);
retval = catch_block.children[2]->eval(t_ss);
break;
}
}
@ -1335,8 +1375,18 @@ namespace chaiscript
template<typename T>
struct Method_AST_Node final : AST_Node_Impl<T> {
std::shared_ptr<AST_Node_Impl<T>> m_body_node;
std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), std::move(t_children)) { }
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc),
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node<T>::has_guard(t_children, 1)?2:1)))
),
m_body_node(Def_AST_Node<T>::get_body_node(std::move(t_children))),
m_guard_node(Def_AST_Node<T>::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2))
{
}
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
@ -1348,39 +1398,27 @@ namespace chaiscript
std::vector<std::string> t_param_names{"this"};
dispatch::Param_Types param_types;
if ((this->children.size() > 3)
if ((this->children.size() > 2)
&& (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
auto args = Arg_List_AST_Node<T>::get_arg_names(this->children[2]);
auto args = Arg_List_AST_Node<T>::get_arg_names(*this->children[2]);
t_param_names.insert(t_param_names.end(), args.begin(), args.end());
param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[2], t_ss);
if (this->children.size() > 4) {
guardnode = this->children[3];
}
}
else {
//no parameters
if (this->children.size() > 3) {
guardnode = this->children[2];
}
param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[2], t_ss);
}
const size_t numparams = t_param_names.size();
std::shared_ptr<dispatch::Proxy_Function_Base> guard;
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
if (guardnode) {
if (m_guard_node) {
guard = dispatch::make_dynamic_proxy_function(
[engine, t_param_names, guardnode](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, guardnode, t_param_names, t_params);
[engine, t_param_names, guardnode = m_guard_node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params);
},
static_cast<int>(numparams), guardnode);
static_cast<int>(numparams), m_guard_node);
}
try {
const std::string & function_name = this->children[1]->text;
auto node = this->children.back();
if (function_name == class_name) {
param_types.push_front(class_name, Type_Info());
@ -1388,10 +1426,10 @@ namespace chaiscript
t_ss->add(
std::make_shared<dispatch::detail::Dynamic_Object_Constructor>(class_name,
dispatch::make_dynamic_proxy_function(
[engine, t_param_names, node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params);
[engine, t_param_names, node = m_body_node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
},
static_cast<int>(numparams), node, param_types, guard
static_cast<int>(numparams), m_body_node, param_types, guard
)
),
function_name);
@ -1402,15 +1440,17 @@ namespace chaiscript
auto type = t_ss->get_type(class_name, false);
param_types.push_front(class_name, type);
t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Function>(class_name,
t_ss->add(
std::make_shared<dispatch::detail::Dynamic_Object_Function>(class_name,
dispatch::make_dynamic_proxy_function(
[engine, t_param_names, node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params);
[engine, t_param_names, node = m_body_node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
},
static_cast<int>(numparams), node, param_types, guard), type),
static_cast<int>(numparams), m_body_node, param_types, guard), type),
function_name);
}
} catch (const exception::name_conflict_error &e) {
std::cout << "Method!!" << std::endl;
throw exception::eval_error("Method redefined '" + e.name() + "'");
}
return void_var();

View File

@ -24,17 +24,26 @@ namespace chaiscript {
template<typename Tracer>
auto optimize(eval::AST_Node_Impl_Ptr<Tracer> p) {
(void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(p), 0)... };
(void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(std::move(p)), 0)... };
return p;
}
};
template<typename T>
auto child_at(const eval::AST_Node_Impl_Ptr<T> &node, const size_t offset) {
if (node->children[offset]->identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node->children[offset]).m_original_node;
eval::AST_Node_Impl<T> &child_at(eval::AST_Node_Impl<T> &node, const size_t offset) {
if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
return *(dynamic_cast<eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
} else {
return node->children[offset];
return *node.children[offset];
}
}
template<typename T>
const eval::AST_Node_Impl<T> &child_at(const eval::AST_Node_Impl<T> &node, const size_t offset) {
if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
return *(dynamic_cast<const eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
} else {
return *node.children[offset];
}
@ -48,24 +57,24 @@ namespace chaiscript {
}
template<typename T>
auto child_count(const eval::AST_Node_Impl_Ptr<T> &node) {
if (node->identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node).m_original_node->children.size();
auto child_count(const eval::AST_Node_Impl<T> &node) {
if (node.identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(node).m_original_node->children.size();
} else {
return node->children.size();
return node.children.size();
}
}
template<typename T, typename Callable>
auto make_compiled_node(const eval::AST_Node_Impl_Ptr<T> &original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
auto make_compiled_node(eval::AST_Node_Impl_Ptr<T> original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
{
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(original_node, std::move(children), std::move(callable));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(std::move(original_node), std::move(children), std::move(callable));
}
struct Return {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &p)
auto optimize(eval::AST_Node_Impl_Ptr<T> p)
{
if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda)
&& !p->children.empty())
@ -75,7 +84,7 @@ namespace chaiscript {
auto &block_last_child = last_child->children.back();
if (block_last_child->identifier == AST_Node_Type::Return) {
if (block_last_child->children.size() == 1) {
last_child->children.back() = block_last_child->children[0];
last_child->children.back() = std::move(block_last_child->children[0]);
}
}
}
@ -86,9 +95,9 @@ namespace chaiscript {
};
template<typename T>
bool contains_var_decl_in_scope(const T &node)
bool contains_var_decl_in_scope(const eval::AST_Node_Impl<T> &node)
{
if (node->identifier == AST_Node_Type::Var_Decl) {
if (node.identifier == AST_Node_Type::Var_Decl) {
return true;
}
@ -96,8 +105,8 @@ namespace chaiscript {
for (size_t i = 0; i < num; ++i) {
const auto &child = child_at(node, i);
if (child->identifier != AST_Node_Type::Block
&& child->identifier != AST_Node_Type::For
if (child.identifier != AST_Node_Type::Block
&& child.identifier != AST_Node_Type::For
&& contains_var_decl_in_scope(child)) {
return true;
}
@ -108,15 +117,16 @@ namespace chaiscript {
struct Block {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Block)
{
if (!contains_var_decl_in_scope(node))
if (!contains_var_decl_in_scope(*node))
{
if (node->children.size() == 1) {
return node->children[0];
return std::move(node->children[0]);
} else {
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location, node->children);
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location,
std::move(node->children));
}
}
}
@ -127,7 +137,7 @@ namespace chaiscript {
struct Dead_Code {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Block)
{
std::vector<size_t> keepers;
@ -135,10 +145,10 @@ namespace chaiscript {
keepers.reserve(num_children);
for (size_t i = 0; i < num_children; ++i) {
auto child = node->children[i];
if ( (child->identifier != AST_Node_Type::Id
&& child->identifier != AST_Node_Type::Constant
&& child->identifier != AST_Node_Type::Noop)
const auto &child = *node->children[i];
if ( (child.identifier != AST_Node_Type::Id
&& child.identifier != AST_Node_Type::Constant
&& child.identifier != AST_Node_Type::Noop)
|| i == num_children - 1) {
keepers.push_back(i);
}
@ -147,12 +157,16 @@ namespace chaiscript {
if (keepers.size() == num_children) {
return node;
} else {
std::vector<eval::AST_Node_Impl_Ptr<T>> new_children;
for (const auto x : keepers)
{
new_children.push_back(node->children[x]);
}
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children);
const auto new_children = [&](){
std::vector<eval::AST_Node_Impl_Ptr<T>> retval;
for (const auto x : keepers)
{
retval.push_back(std::move(node->children[x]));
}
return retval;
};
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children());
}
} else {
return node;
@ -162,29 +176,30 @@ namespace chaiscript {
struct Unused_Return {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if ((node->identifier == AST_Node_Type::Block
|| node->identifier == AST_Node_Type::Scopeless_Block)
&& !node->children.empty())
{
for (size_t i = 0; i < node->children.size()-1; ++i) {
auto child = node->children[i];
auto child = node->children[i].get();
if (child->identifier == AST_Node_Type::Fun_Call) {
node->children[i] = chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location, std::move(child->children));
node->children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location,
std::move(child->children));
}
}
} else if ((node->identifier == AST_Node_Type::For
|| node->identifier == AST_Node_Type::While)
&& child_count(node) > 0) {
auto child = child_at(node, child_count(node) - 1);
if (child->identifier == AST_Node_Type::Block
|| child->identifier == AST_Node_Type::Scopeless_Block)
&& child_count(*node) > 0) {
auto &child = child_at(*node, child_count(*node) - 1);
if (child.identifier == AST_Node_Type::Block
|| child.identifier == AST_Node_Type::Scopeless_Block)
{
auto num_sub_children = child_count(child);
for (size_t i = 0; i < num_sub_children; ++i) {
auto sub_child = child_at(child, i);
if (sub_child->identifier == AST_Node_Type::Fun_Call) {
child->children[i] = chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(sub_child->text, sub_child->location, std::move(sub_child->children));
auto &sub_child = child_at(child, i);
if (sub_child.identifier == AST_Node_Type::Fun_Call) {
child.children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(sub_child.text, sub_child.location, std::move(sub_child.children));
}
}
}
@ -195,17 +210,17 @@ namespace chaiscript {
struct If {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if ((node->identifier == AST_Node_Type::If)
&& node->children.size() >= 2
&& node->children[0]->identifier == AST_Node_Type::Constant)
{
const auto condition = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto condition = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
if (condition.get_type_info().bare_equal_type_info(typeid(bool))) {
if (boxed_cast<bool>(condition)) {
return node->children[1];
return std::move(node->children[1]);
} else if (node->children.size() == 3) {
return node->children[2];
return std::move(node->children[2]);
}
}
}
@ -216,7 +231,7 @@ namespace chaiscript {
struct Partial_Fold {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
// Fold right side
if (node->identifier == AST_Node_Type::Binary
@ -228,9 +243,10 @@ namespace chaiscript {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper);
if (parsed != Operators::Opers::invalid) {
const auto rhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1])->m_value;
const auto rhs = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[1].get())->m_value;
if (rhs.get_type_info().is_arithmetic()) {
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location, node->children, rhs);
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location,
std::move(node->children), rhs);
}
}
} catch (const std::exception &) {
@ -244,7 +260,7 @@ namespace chaiscript {
struct Constant_Fold {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Prefix
&& node->children.size() == 1
@ -253,14 +269,14 @@ namespace chaiscript {
try {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper, true);
const auto lhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
const auto match = oper + node->children[0]->text;
if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) {
const auto val = Boxed_Number::do_oper(parsed, lhs);
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
} else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") {
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(!boxed_cast<bool>(lhs)));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(!boxed_cast<bool>(lhs)));
}
} catch (const std::exception &) {
//failure to fold, that's OK
@ -271,8 +287,8 @@ namespace chaiscript {
&& node->children[1]->identifier == AST_Node_Type::Constant)
{
try {
const auto lhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto rhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1])->m_value;
const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) {
const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text;
const auto val = [lhs_val = boxed_cast<bool>(lhs), rhs_val = boxed_cast<bool>(rhs), id = node->identifier] {
@ -280,7 +296,7 @@ namespace chaiscript {
else { return Boxed_Value(lhs_val || rhs_val); }
}();
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
}
} catch (const std::exception &) {
//failure to fold, that's OK
@ -294,12 +310,12 @@ namespace chaiscript {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper);
if (parsed != Operators::Opers::invalid) {
const auto lhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto rhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1])->m_value;
const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) {
const auto val = Boxed_Number::do_oper(parsed, lhs, rhs);
const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text;
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
}
}
} catch (const std::exception &) {
@ -312,13 +328,13 @@ namespace chaiscript {
&& node->children[1]->children.size() == 1
&& node->children[1]->children[0]->identifier == AST_Node_Type::Constant) {
const auto arg = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1]->children[0])->m_value;
const auto arg = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]->children[0]).m_value;
if (arg.get_type_info().is_arithmetic()) {
const auto &fun_name = node->children[0]->text;
const auto make_constant = [&node, &fun_name](auto val){
const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")";
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(val));
};
if (fun_name == "double") {
@ -344,35 +360,36 @@ namespace chaiscript {
struct For_Loop {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &for_node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> for_node) {
if (for_node->identifier != AST_Node_Type::For) {
return for_node;
}
const auto eq_node = child_at(for_node, 0);
const auto binary_node = child_at(for_node, 1);
const auto prefix_node = child_at(for_node, 2);
const auto &eq_node = child_at(*for_node, 0);
const auto &binary_node = child_at(*for_node, 1);
const auto &prefix_node = child_at(*for_node, 2);
if (eq_node->identifier == AST_Node_Type::Equation
if (child_count(*for_node) == 4
&& eq_node.identifier == AST_Node_Type::Equation
&& child_count(eq_node) == 2
&& child_at(eq_node, 0)->identifier == AST_Node_Type::Var_Decl
&& child_at(eq_node, 1)->identifier == AST_Node_Type::Constant
&& binary_node->identifier == AST_Node_Type::Binary
&& binary_node->text == "<"
&& child_at(eq_node, 0).identifier == AST_Node_Type::Var_Decl
&& child_at(eq_node, 1).identifier == AST_Node_Type::Constant
&& binary_node.identifier == AST_Node_Type::Binary
&& binary_node.text == "<"
&& child_count(binary_node) == 2
&& child_at(binary_node, 0)->identifier == AST_Node_Type::Id
&& child_at(binary_node, 0)->text == child_at(child_at(eq_node,0), 0)->text
&& child_at(binary_node, 1)->identifier == AST_Node_Type::Constant
&& prefix_node->identifier == AST_Node_Type::Prefix
&& prefix_node->text == "++"
&& child_at(binary_node, 0).identifier == AST_Node_Type::Id
&& child_at(binary_node, 0).text == child_at(child_at(eq_node,0), 0).text
&& child_at(binary_node, 1).identifier == AST_Node_Type::Constant
&& prefix_node.identifier == AST_Node_Type::Prefix
&& prefix_node.text == "++"
&& child_count(prefix_node) == 1
&& child_at(prefix_node, 0)->identifier == AST_Node_Type::Id
&& child_at(prefix_node, 0)->text == child_at(child_at(eq_node,0), 0)->text)
&& child_at(prefix_node, 0).identifier == AST_Node_Type::Id
&& child_at(prefix_node, 0).text == child_at(child_at(eq_node,0), 0).text)
{
const Boxed_Value &begin = std::dynamic_pointer_cast<const eval::Constant_AST_Node<T>>(child_at(eq_node, 1))->m_value;
const Boxed_Value &end = std::dynamic_pointer_cast<const eval::Constant_AST_Node<T>>(child_at(binary_node, 1))->m_value;
const std::string &id = child_at(prefix_node, 0)->text;
const Boxed_Value &begin = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(eq_node, 1)).m_value;
const Boxed_Value &end = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(binary_node, 1)).m_value;
const std::string &id = child_at(prefix_node, 0).text;
if (begin.get_type_info().bare_equal(user_type<int>())
&& end.get_type_info().bare_equal(user_type<int>())) {
@ -380,9 +397,14 @@ namespace chaiscript {
const auto start_int = boxed_cast<int>(begin);
const auto end_int = boxed_cast<int>(end);
const auto body = child_at(for_node, 3);
// note that we are moving the last element out, then popping the empty shared_ptr
// from the vector
std::vector<eval::AST_Node_Impl_Ptr<T>> body_vector;
auto body_child = std::move(for_node->children[3]);
for_node->children.pop_back();
body_vector.emplace_back(std::move(body_child));
return make_compiled_node(for_node, {body},
return make_compiled_node(std::move(for_node), std::move(body_vector),
[id, start_int, end_int](const std::vector<eval::AST_Node_Impl_Ptr<T>> &children, const chaiscript::detail::Dispatch_State &t_ss) {
assert(children.size() == 1);
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);

View File

@ -397,7 +397,7 @@ namespace chaiscript
return m_optimizer;
}
ChaiScript_Parser(const ChaiScript_Parser &) = default;
ChaiScript_Parser(const ChaiScript_Parser &) = delete;
ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete;
ChaiScript_Parser(ChaiScript_Parser &&) = default;
ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete;
@ -406,10 +406,10 @@ namespace chaiscript
bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast<uint8_t>(c)]; }
/// Prints the parsed ast_nodes as a tree
void debug_print(AST_NodePtr t, std::string prepend = "") const override {
std::cout << prepend << "(" << ast_node_type_to_string(t->identifier) << ") " << t->text << " : " << t->start().line << ", " << t->start().column << '\n';
for (const auto &node : t->get_children()) {
debug_print(node, prepend + " ");
void debug_print(const AST_Node &t, std::string prepend = "") const override {
std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " << t.start().column << '\n';
for (const auto &node : t.get_children()) {
debug_print(node.get(), prepend + " ");
}
}
@ -452,7 +452,7 @@ namespace chaiscript
/// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position
m_match_stack.push_back(
m_optimizer.optimize(
chaiscript::make_shared<chaiscript::eval::AST_Node_Impl<Tracer>, NodeType>(
chaiscript::make_unique<chaiscript::eval::AST_Node_Impl<Tracer>, NodeType>(
std::move(t_text),
std::move(filepos),
std::move(new_children)))
@ -779,9 +779,9 @@ namespace chaiscript
}
template<typename T, typename ... Param>
std::shared_ptr<eval::AST_Node_Impl<Tracer>> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param)
std::unique_ptr<eval::AST_Node_Impl<Tracer>> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param)
{
return chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward<Param>(param)...);
return chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward<Param>(param)...);
}
/// Reads a number from the input, detecting if it's an integer or floating point
@ -1051,23 +1051,30 @@ namespace chaiscript
Char_Parser &operator=(const Char_Parser &) = delete;
~Char_Parser(){
if (is_octal) {
process_octal();
}
try {
if (is_octal) {
process_octal();
}
if (is_hex) {
process_hex();
}
if (is_hex) {
process_hex();
}
if (is_unicode) {
process_unicode();
if (is_unicode) {
process_unicode();
}
} catch (const std::invalid_argument &) {
// escape sequence was invalid somehow, we'll pick this
// up in the next part of parsing
}
}
void process_hex()
{
auto val = stoll(hex_matches, nullptr, 16);
match.push_back(char_type(val));
if (!hex_matches.empty()) {
auto val = stoll(hex_matches, nullptr, 16);
match.push_back(char_type(val));
}
hex_matches.clear();
is_escaped = false;
is_hex = false;
@ -1076,8 +1083,10 @@ namespace chaiscript
void process_octal()
{
auto val = stoll(octal_matches, nullptr, 8);
match.push_back(char_type(val));
if (!octal_matches.empty()) {
auto val = stoll(octal_matches, nullptr, 8);
match.push_back(char_type(val));
}
octal_matches.clear();
is_escaped = false;
is_octal = false;
@ -1086,9 +1095,11 @@ namespace chaiscript
void process_unicode()
{
auto val = stoll(hex_matches, nullptr, 16);
hex_matches.clear();
match += detail::Char_Parser_Helper<string_type>::str_from_ll(val);
if (!hex_matches.empty()) {
auto val = stoll(hex_matches, nullptr, 16);
hex_matches.clear();
match += detail::Char_Parser_Helper<string_type>::str_from_ll(val);
}
is_escaped = false;
is_unicode = false;
}
@ -1254,6 +1265,7 @@ namespace chaiscript
cparser.saw_interpolation_marker = false;
} else {
cparser.parse(*s, start.line, start.col, *m_filename);
++s;
}
}
@ -1759,7 +1771,7 @@ namespace chaiscript
if ((is_if_init && num_children == 3)
|| (!is_if_init && num_children == 2)) {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
if (!is_if_init) {
@ -1849,7 +1861,7 @@ namespace chaiscript
{
return false;
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
}
@ -1859,13 +1871,13 @@ namespace chaiscript
{
return false;
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Constant_AST_Node<Tracer>>(Boxed_Value(true)));
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Constant_AST_Node<Tracer>>(Boxed_Value(true)));
}
}
if (!Equation())
{
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
return true;
@ -1896,9 +1908,17 @@ namespace chaiscript
throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename);
}
const auto num_children = m_match_stack.size() - prev_stack_top;
if (classic_for) {
if (num_children != 4) {
throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
}
build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
} else {
if (num_children != 3) {
throw exception::eval_error("Incomplete ranged-for expression", File_Position(m_position.line, m_position.col), *m_filename);
}
build_match<eval::Ranged_For_AST_Node<Tracer>>(prev_stack_top);
}
}
@ -2005,7 +2025,7 @@ namespace chaiscript
}
if (m_match_stack.size() == prev_stack_top) {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
@ -2029,7 +2049,7 @@ namespace chaiscript
}
if (m_match_stack.size() == prev_stack_top) {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
@ -2105,13 +2125,13 @@ namespace chaiscript
}
if (m_match_stack.back()->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
}
auto dot_access = m_match_stack.back()->children[0];
auto func_call = m_match_stack.back();
auto dot_access = std::move(m_match_stack.back()->children[0]);
auto func_call = std::move(m_match_stack.back());
m_match_stack.pop_back();
func_call->children.erase(func_call->children.begin());
if (dot_access->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
}
func_call->children.insert(func_call->children.begin(), dot_access->children.back());
func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back()));
dot_access->children.pop_back();
dot_access->children.push_back(std::move(func_call));
if (dot_access->children.size() != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
@ -2172,7 +2192,7 @@ namespace chaiscript
throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename);
}
} else if (Keyword("GLOBAL") || Keyword("global")) {
} else if (Keyword("global")) {
retval = true;
if (!(Reference() || Id(true))) {
@ -2517,24 +2537,23 @@ namespace chaiscript
AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override
{
ChaiScript_Parser<Tracer, Optimizer> parser(*this);
parser.m_match_stack.clear();
ChaiScript_Parser<Tracer, Optimizer> parser(m_tracer, m_optimizer);
return parser.parse_internal(t_input, t_fname);
}
eval::AST_Node_Impl_Ptr<Tracer> parse_instr_eval(const std::string &t_input)
{
const auto last_position = m_position;
const auto last_filename = m_filename;
const auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){});
auto last_position = m_position;
auto last_filename = m_filename;
auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){});
const auto retval = parse_internal(t_input, "instr eval");
auto retval = parse_internal(t_input, "instr eval");
m_position = std::move(last_position);
m_filename = std::move(last_filename);
m_match_stack = std::move(last_match_stack);
return std::dynamic_pointer_cast<eval::AST_Node_Impl<Tracer>>(retval);
return eval::AST_Node_Impl_Ptr<Tracer>(dynamic_cast<eval::AST_Node_Impl<Tracer>*>(retval.release()));
}
/// Parses the given input string, tagging parsed ast_nodes with the given m_filename.
@ -2546,20 +2565,21 @@ namespace chaiscript
while (m_position.has_more() && (!Eol())) {
++m_position;
}
/// \todo respect // -*- coding: utf-8 -*- on line 1 or 2 see: http://evanjones.ca/python-utf8.html)
}
if (Statements(true)) {
if (m_position.has_more()) {
throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), t_fname);
throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), *m_filename);
} else {
build_match<eval::File_AST_Node<Tracer>>(0);
}
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
return m_match_stack.front();
AST_NodePtr retval(std::move(m_match_stack.front()));
m_match_stack.clear();
return retval;
}
};
}

View File

@ -16,7 +16,11 @@ namespace chaiscript
{
Loadable_Module(const std::string &, const std::string &)
{
#ifdef CHAISCRIPT_NO_DYNLOAD
throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
#else
throw chaiscript::exception::load_module_error("Loadable module support not available for your platform");
#endif
}
ModulePtr m_moduleptr;

View File

@ -463,7 +463,7 @@ struct JSONParser {
}
static void consume_ws( const std::string &str, size_t &offset ) {
while( isspace( str[offset] ) && offset <= str.size() ) { ++offset; }
while( isspace( str.at(offset) ) && offset <= str.size() ) { ++offset; }
}
static JSON parse_object( const std::string &str, size_t &offset ) {
@ -471,29 +471,29 @@ struct JSONParser {
++offset;
consume_ws( str, offset );
if( str[offset] == '}' ) {
if( str.at(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");
if( str.at(offset) != ':' ) {
throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str.at(offset) + "'\n");
}
consume_ws( str, ++offset );
JSON Value = parse_next( str, offset );
Object[Key.to_string()] = Value;
consume_ws( str, offset );
if( str[offset] == ',' ) {
if( str.at(offset) == ',' ) {
++offset; continue;
}
else if( str[offset] == '}' ) {
else if( str.at(offset) == '}' ) {
++offset; break;
}
else {
throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str[offset] + "'\n");
throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str.at(offset) + "'\n");
}
}
@ -506,7 +506,7 @@ struct JSONParser {
++offset;
consume_ws( str, offset );
if( str[offset] == ']' ) {
if( str.at(offset) == ']' ) {
++offset; return Array;
}
@ -514,14 +514,14 @@ struct JSONParser {
Array[index++] = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] == ',' ) {
if( str.at(offset) == ',' ) {
++offset; continue;
}
else if( str[offset] == ']' ) {
else if( str.at(offset) == ']' ) {
++offset; break;
}
else {
throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'\n");
throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str.at(offset) + "'\n");
}
}
@ -530,9 +530,9 @@ struct JSONParser {
static JSON parse_string( const std::string &str, size_t &offset ) {
std::string val;
for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) {
for( char c = str.at(++offset); c != '\"' ; c = str.at(++offset) ) {
if( c == '\\' ) {
switch( str[ ++offset ] ) {
switch( str.at(++offset) ) {
case '\"': val += '\"'; break;
case '\\': val += '\\'; break;
case '/' : val += '/' ; break;
@ -544,7 +544,7 @@ struct JSONParser {
case 'u' : {
val += "\\u" ;
for( size_t i = 1; i <= 4; ++i ) {
c = str[offset+i];
c = str.at(offset+i);
if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) {
val += c;
} else {
@ -567,12 +567,17 @@ struct JSONParser {
std::string val, exp_str;
char c = '\0';
bool isDouble = false;
bool isNegative = false;
long exp = 0;
if( offset < str.size() && str.at(offset) == '-' ) {
isNegative = true;
++offset;
}
for (; offset < str.size() ;) {
c = str[offset++];
if( (c == '-') || (c >= '0' && c <= '9') ) {
c = str.at(offset++);
if( c >= '0' && c <= '9' ) {
val += c;
} else if( c == '.' ) {
} else if( c == '.' && !isDouble ) {
val += c;
isDouble = true;
} else {
@ -580,7 +585,7 @@ struct JSONParser {
}
}
if( offset < str.size() && (c == 'E' || c == 'e' )) {
c = str[ offset++ ];
c = str.at(offset++);
if( c == '-' ) {
exp_str += '-';
} else if( c == '+' ) {
@ -590,7 +595,7 @@ struct JSONParser {
}
for (; offset < str.size() ;) {
c = str[ offset++ ];
c = str.at(offset++);
if( c >= '0' && c <= '9' ) {
exp_str += c;
} else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
@ -608,12 +613,12 @@ struct JSONParser {
--offset;
if( isDouble ) {
return JSON(chaiscript::parse_num<double>( val ) * std::pow( 10, exp ));
return JSON((isNegative?-1:1) * chaiscript::parse_num<double>( val ) * std::pow( 10, exp ));
} else {
if( !exp_str.empty() ) {
return JSON(static_cast<double>(chaiscript::parse_num<long>( val )) * std::pow( 10, exp ));
return JSON((isNegative?-1:1) * static_cast<double>(chaiscript::parse_num<long>( val )) * std::pow( 10, exp ));
} else {
return JSON(chaiscript::parse_num<long>( val ));
return JSON((isNegative?-1:1) * chaiscript::parse_num<long>( val ));
}
}
}
@ -641,7 +646,7 @@ struct JSONParser {
static JSON parse_next( const std::string &str, size_t &offset ) {
char value;
consume_ws( str, offset );
value = str[offset];
value = str.at(offset);
switch( value ) {
case '[' : return parse_array( str, offset );
case '{' : return parse_object( str, offset );

View File

@ -63,7 +63,11 @@ namespace chaiscript
static Boxed_Value from_json(const std::string &t_json)
{
return from_json( json::JSON::Load(t_json) );
try {
return from_json( json::JSON::Load(t_json) );
} catch (const std::out_of_range& ) {
throw std::runtime_error("Unparsed JSON input");
}
}
static std::string to_json(const Boxed_Value &t_bv)

View File

@ -68,6 +68,7 @@ std::vector<std::string> default_search_paths()
{
std::vector<std::string> paths;
#ifndef CHAISCRIPT_NO_DYNLOAD
#ifdef CHAISCRIPT_WINDOWS // force no unicode
CHAR path[4096];
int size = GetModuleFileNameA(0, path, sizeof(path) - 1);
@ -137,6 +138,7 @@ std::vector<std::string> default_search_paths()
paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
}
#endif
#endif // ifndef CHAISCRIPT_NO_DYNLOAD
return paths;
}
@ -250,7 +252,7 @@ void interactive(chaiscript::ChaiScript& chai)
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.what();
if (ee.call_stack.size() > 0) {
std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")";
std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
}
std::cout << std::endl;
}

342
src/libfuzzer_client.cpp Normal file
View File

@ -0,0 +1,342 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <iostream>
#include <list>
#include <regex>
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <chaiscript/chaiscript.hpp>
#include "../static_libs/chaiscript_parser.hpp"
#include "../static_libs/chaiscript_stdlib.hpp"
#ifdef READLINE_AVAILABLE
#include <readline/readline.h>
#include <readline/history.h>
#else
char *mystrdup (const char *s) {
size_t len = strlen(s); // Space for length plus nul
char *d = static_cast<char*>(malloc (len+1));
if (d == nullptr) { return nullptr; } // No memory
#ifdef CHAISCRIPT_MSVC
strcpy_s(d, len+1, s); // Copy the characters
#else
strncpy(d,s,len); // Copy the characters
#endif
d[len] = '\0';
return d; // Return the new string
}
char* readline(const char* p)
{
std::string retval;
std::cout << p ;
std::getline(std::cin, retval);
return std::cin.eof() ? nullptr : mystrdup(retval.c_str());
}
void add_history(const char* /*unused*/){}
void using_history(){}
#endif
void *cast_module_symbol(std::vector<std::string> (*t_path)())
{
union cast_union
{
std::vector<std::string> (*in_ptr)();
void *out_ptr;
};
cast_union c;
c.in_ptr = t_path;
return c.out_ptr;
}
std::vector<std::string> default_search_paths()
{
std::vector<std::string> paths;
#ifndef CHAISCRIPT_NO_DYNLOAD
#ifdef CHAISCRIPT_WINDOWS // force no unicode
CHAR path[4096];
int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1);
std::string exepath(path, size);
size_t lastslash = exepath.rfind('\\');
size_t secondtolastslash = exepath.rfind('\\', lastslash - 1);
if (lastslash != std::string::npos)
{
paths.push_back(exepath.substr(0, lastslash));
}
if (secondtolastslash != std::string::npos)
{
return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"};
}
#else
std::string exepath;
std::vector<char> buf(2048);
ssize_t size = -1;
if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0)
{
exepath = std::string(&buf.front(), static_cast<size_t>(size));
}
if (exepath.empty())
{
if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0)
{
exepath = std::string(&buf.front(), static_cast<size_t>(size));
}
}
if (exepath.empty())
{
if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0)
{
exepath = std::string(&buf.front(), static_cast<size_t>(size));
}
}
if (exepath.empty())
{
Dl_info rInfo;
memset( &rInfo, 0, sizeof(rInfo) );
if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) {
return paths;
}
exepath = std::string(rInfo.dli_fname);
}
size_t lastslash = exepath.rfind('/');
size_t secondtolastslash = exepath.rfind('/', lastslash - 1);
if (lastslash != std::string::npos)
{
paths.push_back(exepath.substr(0, lastslash+1));
}
if (secondtolastslash != std::string::npos)
{
paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
}
#endif
#endif // ifndef CHAISCRIPT_NO_DYNLOAD
return paths;
}
void help(int n) {
if ( n >= 0 ) {
std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press <enter>.\n";
std::cout << "Additionally, you can inspect the runtime system using:\n";
std::cout << " dump_system() - outputs all functions registered to the system\n";
std::cout << " dump_object(x) - dumps information about the given symbol\n";
} else {
std::cout << "usage : chai [option]+\n";
std::cout << "option:" << '\n';
std::cout << " -h | --help" << '\n';
std::cout << " -i | --interactive" << '\n';
std::cout << " -c | --command cmd" << '\n';
std::cout << " -v | --version" << '\n';
std::cout << " - --stdin" << '\n';
std::cout << " filepath" << '\n';
}
}
bool throws_exception(const std::function<void ()> &f)
{
try {
f();
} catch (...) {
return true;
}
return false;
}
chaiscript::exception::eval_error get_eval_error(const std::function<void ()> &f)
{
try {
f();
} catch (const chaiscript::exception::eval_error &e) {
return e;
}
throw std::runtime_error("no exception throw");
}
std::string get_next_command() {
std::string retval("quit");
if ( ! std::cin.eof() ) {
char *input_raw = readline("eval> ");
if ( input_raw != nullptr ) {
add_history(input_raw);
std::string val(input_raw);
size_t pos = val.find_first_not_of("\t \n");
if (pos != std::string::npos)
{
val.erase(0, pos);
}
pos = val.find_last_not_of("\t \n");
if (pos != std::string::npos)
{
val.erase(pos+1, std::string::npos);
}
retval = val;
::free(input_raw);
}
}
if( retval == "quit"
|| retval == "exit"
|| retval == "help"
|| retval == "version")
{
retval += "(0)";
}
return retval;
}
// We have to wrap exit with our own because Clang has a hard time with
// function pointers to functions with special attributes (system exit being marked NORETURN)
void myexit(int return_val) {
exit(return_val);
}
void interactive(chaiscript::ChaiScript_Basic& chai)
{
using_history();
for (;;) {
std::string input = get_next_command();
try {
// evaluate input
chaiscript::Boxed_Value val = chai.eval(input);
//Then, we try to print the result of the evaluation to the user
if (!val.get_type_info().bare_equal(chaiscript::user_type<void>())) {
try {
std::cout << chai.eval<std::function<std::string (const chaiscript::Boxed_Value &bv)> >("to_string")(val) << '\n';
}
catch (...) {} //If we can't, do nothing
}
}
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.what();
if ( !ee.call_stack.empty() ) {
std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
}
std::cout << '\n';
}
catch (const std::exception &e) {
std::cout << e.what();
std::cout << '\n';
}
}
}
double now()
{
using namespace std::chrono;
auto now = high_resolution_clock::now();
return duration_cast<duration<double>>(now.time_since_epoch()).count();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
chaiscript::ChaiScript chai;
chai.eval( R"chaiscript(
def assert_equal(x, y)
{
if (x == y)
{
// Passes
} else {
// Fails
print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'");
// exit(-1);
}
}
def assert_false(f)
{
if (f)
{
print("assert_false failure");
// exit(-1);
}
}
def assert_true(f)
{
if (!f)
{
print("assert_true failure");
// exit(-1);
}
}
def assert_not_equal(x, y)
{
if (!(x == y))
{
// Passes
} else {
// Fails
print("assert_not_equal failure: got " + to_string(y) + " which was not expected.");
// exit(-1);
}
}
def assert_throws(desc, x)
{
if (throws_exception(x))
{
// Passes
} else {
// Fails
print("assert_throws failure, function did not throw exception: " + to_string(desc));
// exit(-1);
}
})chaiscript");
try {
chai.eval(std::string(reinterpret_cast<const char *>(data), size));
} catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.pretty_print();
std::cout << '\n';
} catch (const chaiscript::Boxed_Value &e) {
std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n';
} catch (const chaiscript::exception::load_module_error &e) {
std::cout << "Unhandled module load error\n" << e.what() << '\n';
} catch (const std::exception &e) {
std::cout << "unhandled unknown exception: " << e.what() << '\n';
}
return 0;
}

View File

@ -71,6 +71,7 @@ std::vector<std::string> default_search_paths()
{
std::vector<std::string> paths;
#ifndef CHAISCRIPT_NO_DYNLOAD
#ifdef CHAISCRIPT_WINDOWS // force no unicode
CHAR path[4096];
int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1);
@ -140,6 +141,7 @@ std::vector<std::string> default_search_paths()
paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
}
#endif
#endif // ifndef CHAISCRIPT_NO_DYNLOAD
return paths;
}
@ -245,7 +247,7 @@ void interactive(chaiscript::ChaiScript_Basic& chai)
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.what();
if ( !ee.call_stack.empty() ) {
std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")";
std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
}
std::cout << '\n';
}
@ -306,6 +308,7 @@ int main(int argc, char *argv[])
bool eval_error_ok = false;
bool boxed_exception_ok = false;
bool any_exception_ok = false;
for (int i = 0; i < argc; ++i) {
if ( i == 0 && argc > 1 ) {
@ -342,6 +345,9 @@ int main(int argc, char *argv[])
} else if ( arg == "--exception" ) {
boxed_exception_ok = true;
continue;
} else if ( arg == "--any-exception" ) {
any_exception_ok = true;
continue;
} else if ( arg == "-i" || arg == "--interactive" ) {
mode = eInteractive ;
} else if ( arg.find('-') == 0 ) {
@ -381,11 +387,18 @@ int main(int argc, char *argv[])
catch (const chaiscript::exception::load_module_error &e) {
std::cout << "Unhandled module load error\n" << e.what() << '\n';
}
// catch (std::exception &e) {
// std::cout << e.what() << '\n';
// return EXIT_FAILURE;
// }
catch (std::exception &e) {
std::cout << "Unhandled standard exception: " << e.what() << '\n';
if (!any_exception_ok) {
throw;
}
}
catch (...) {
std::cout << "Unhandled unknown exception" << '\n';
if (!any_exception_ok) {
throw;
}
}
}
return EXIT_SUCCESS;

View File

@ -12,6 +12,8 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wparentheses"
// This one is necessary for the const return non-reference test
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#endif
@ -1270,3 +1272,54 @@ TEST_CASE("Test reference member being registered")
}
const int add_3(const int &i)
{
return i + 3;
}
TEST_CASE("Test returning by const non-reference")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
// Note, C++ will not allow us to do this:
// chai.add(chaiscript::fun(&Reference_MyClass::x) , "x");
chai.add(chaiscript::fun(&add_3), "add_3");
auto v = chai.eval<int>("add_3(12)");
CHECK(v == 15);
}
struct MyException : std::runtime_error
{
using std::runtime_error::runtime_error;
int value = 5;
};
void throws_a_thing()
{
throw MyException("Hello World");
}
TEST_CASE("Test throwing and catching custom exception")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
chai.add(chaiscript::user_type<MyException>(), "MyException");
chai.add(chaiscript::base_class<std::runtime_error, MyException>()); // be sure to register base class relationship
chai.add(chaiscript::fun(&throws_a_thing), "throws_a_thing");
chai.add(chaiscript::fun(&MyException::value), "value");
const auto s = chai.eval<std::string>("fun(){ try { throws_a_thing(); } catch (MyException ex) { return ex.what(); } }()");
CHECK(s == "Hello World");
// this has an explicit clone to prevent returning a pointer to the `value` from inside of MyException
const auto i = chai.eval<int>("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()");
CHECK(i == 5);
}
TEST_CASE("Test ability to get 'use' function from default construction")
{
chaiscript::ChaiScript chai;
const auto use_function = chai.eval<std::function<chaiscript::Boxed_Value (const std::string &)>>("use");
}

View File

@ -0,0 +1,53 @@
def assert_equal(x, y)
{
if (x == y)
{
// Passes
} else {
// Fails
print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'");
// exit(-1);
}
}
def assert_false(f)
{
if (f)
{
print("assert_false failure");
// exit(-1);
}
}
def assert_true(f)
{
if (!f)
{
print("assert_true failure");
// exit(-1);
}
}
def assert_not_equal(x, y)
{
if (!(x == y))
{
// Passes
} else {
// Fails
print("assert_not_equal failure: got " + to_string(y) + " which was not expected.");
// exit(-1);
}
}
def assert_throws(desc, x)
{
if (throws_exception(x))
{
// Passes
} else {
// Fails
print("assert_throws failure, function did not throw exception: " + to_string(desc));
// exit(-1);
}
}

Binary file not shown.

View File

@ -1,12 +1,12 @@
// Test global
GLOBAL g = 3;
global g = 3;
assert_true(g == 3);
var v := g;
assert_true(v == 3);
GLOBAL g = 2;
global g = 2;
assert_true(g == 2);
assert_true(v == 2);

View File

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

View File

@ -1 +1,22 @@
assert_equal(from_json("1.234"), 1.234)
assert_equal(from_json("-1.234"), -1.234)
auto caught = false;
try {
from_json("-1-5.3");
}
catch(e) {
assert_equal("JSON ERROR: Number: unexpected character '-'", e.what());
caught = true;
}
assert_equal(caught, true);
caught = false;
try {
from_json("-15.3.2");
}
catch(e) {
assert_equal("JSON ERROR: Number: unexpected character '.'", e.what());
caught = true;
}
assert_equal(caught, true);

View File

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

View File

@ -2,6 +2,9 @@
#include <algorithm>
#ifdef CHAISCRIPT_NO_DYNLOAD
#include <chaiscript/chaiscript.hpp>
#endif
#include <chaiscript/chaiscript_basic.hpp>
#include <chaiscript/language/chaiscript_parser.hpp>
@ -57,18 +60,22 @@ int main()
}
std::vector<std::string> modulepaths;
#ifdef CHAISCRIPT_NO_DYNLOAD
chaiscript::ChaiScript chai(/* unused */modulepaths, usepaths);
#else
modulepaths.push_back("");
if (modulepath)
{
modulepaths.push_back(modulepath);
}
// For this test we are going to load the dynamic stdlib
// to make sure it continues to work
chaiscript::ChaiScript_Basic chai(
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>(),
modulepaths,usepaths);
#endif
std::vector<std::shared_ptr<std::thread> > threads;

View File

@ -0,0 +1,11 @@
#define CHAISCRIPT_NO_THREADS
/// ChaiScript as a static is unsupported with thread support enabled
///
#include <chaiscript/chaiscript.hpp>
static chaiscript::ChaiScript chai;
int main() {}