diff --git a/CMakeLists.txt b/CMakeLists.txt index 28b9288..65989e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,36 +39,44 @@ configure_file(Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile) include(CTest) include(CPack) -FIND_LIBRARY(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib) +find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib) enable_testing() -MESSAGE(STATUS "Detecting readline support") +message(STATUS "Detecting readline support") if (READLINE_LIBRARY) - MESSAGE(STATUS "Found: ${READLINE_LIBRARY}") - SET (READLINE_LIB readline) - ADD_DEFINITIONS(/DREADLINE_AVAILABLE) + message(STATUS "Found: ${READLINE_LIBRARY}") + set (READLINE_LIB readline) + add_definitions(/DREADLINE_AVAILABLE) else(READLINE_LIBRARY) - MESSAGE(STATUS "Not Found") - SET (READLINE_LIB ) - SET (READLINE_FLAG ) + message(STATUS "Not Found") + set (READLINE_LIB ) + set (READLINE_FLAG ) endif(READLINE_LIBRARY) -IF(MSVC) - ADD_DEFINITIONS(/W4) - IF(CMAKE_CL_64) - ADD_DEFINITIONS(/bigobj) - ENDIF() -ELSE() - # -Wno-missing-field-initializers is for boost on macos - ADD_DEFINITIONS(-Wall -Wextra -Wno-missing-field-initializers -Wshadow) -ENDIF() +if(MSVC) + add_definitions(/W4) + if(CMAKE_CL_64) + add_definitions(/bigobj) + endif() +else() + add_definitions(-Wall -Wextra -Wshadow -pedantic) + + if (APPLE) + # -Wno-missing-field-initializers is for boost on macos + add_definitions(-Wno-missing-field-initializers -Wno-sign-compare) + endif() +endif() include_directories(include) - -SET(Boost_ADDITIONAL_VERSIONS "1.44" "1.44.0" "1.43" "1.43.0" "1.42" "1.42.0" "1.41") -SET(Boost_USE_MULTITHREADED ON) + +set(Boost_ADDITIONAL_VERSIONS "1.44" "1.44.0" "1.43" "1.43.0" "1.42" "1.42.0" "1.41") +set(Boost_USE_MULTITHREADED ON) + +set (Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.hpp include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp) + +set_source_files_properties(${Chai_INCLUDES} PROPERTIES HEADER_FILE_ONLY TRUE) if (MULTITHREAD_SUPPORT_ENABLED) find_package(Boost 1.36.0 COMPONENTS thread) @@ -79,18 +87,18 @@ if (MULTITHREAD_SUPPORT_ENABLED) message(FATAL_ERROR "Can not find Boost") endif(Boost_FOUND) else() - ADD_DEFINITIONS(-DCHAISCRIPT_NO_THREADS) + add_definitions(-DCHAISCRIPT_NO_THREADS) endif() if (CMAKE_HOST_UNIX) - SET(DYNAMIC_LOADER "dl") + set(DYNAMIC_LOADER "dl") endif(CMAKE_HOST_UNIX) if (MSVC) # Boost on MSVC does automatic linking - SET(LIBS ${DYNAMIC_LOADER} ${READLINE_LIB}) + set(LIBS ${DYNAMIC_LOADER} ${READLINE_LIB}) else() - SET(LIBS ${DYNAMIC_LOADER} ${Boost_LIBRARIES} ${READLINE_LIB}) + set(LIBS ${DYNAMIC_LOADER} ${Boost_LIBRARIES} ${READLINE_LIB}) endif() if (CMAKE_COMPILER_2005) @@ -100,7 +108,7 @@ endif() include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIR}) -add_executable(chai src/main.cpp) +add_executable(chai src/main.cpp ${Chai_INCLUDES}) target_link_libraries(chai ${LIBS}) if (BUILD_SAMPLES) @@ -124,7 +132,7 @@ file(GLOB UNIT_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/unittests/ ${CMAKE_CUR list(SORT UNIT_TESTS) -IF(BUILD_TESTING) +if(BUILD_TESTING) option(UNIT_TEST_LIGHT "Unit tests light (expect module loading failures)" FALSE) foreach(filename ${UNIT_TESTS}) @@ -132,13 +140,13 @@ IF(BUILD_TESTING) add_test(${filename} chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/unit_test.inc ${CMAKE_CURRENT_SOURCE_DIR}/unittests/${filename}) endforeach(filename) - SET_PROPERTY(TEST ${UNIT_TESTS} + set_property(TEST ${UNIT_TESTS} PROPERTY ENVIRONMENT "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" ) - IF (NOT UNIT_TEST_LIGHT) + if (NOT UNIT_TEST_LIGHT) add_executable(utility_test unittests/utility_test.cpp) target_link_libraries(utility_test ${LIBS}) add_test(NAME Utility_Test COMMAND utility_test) @@ -154,7 +162,7 @@ IF(BUILD_TESTING) add_executable(functor_cast_test unittests/functor_cast_test.cpp) target_link_libraries(functor_cast_test ${LIBS}) add_test(NAME Functor_Cast_Test COMMAND functor_cast_test) - + add_executable(boxed_cast_test unittests/boxed_cast_test.cpp) target_link_libraries(boxed_cast_test ${LIBS}) add_test(NAME Boxed_Cast_Test COMMAND boxed_cast_test) @@ -175,6 +183,11 @@ IF(BUILD_TESTING) target_link_libraries(eval_catch_exception_test ${LIBS}) add_test(NAME Eval_Catch_Exception_Test COMMAND eval_catch_exception_test) + add_executable(short_comparison_test unittests/short_comparison_test.cpp) + target_link_libraries(short_comparison_test ${LIBS}) + add_test(NAME Short_Comparison_Test COMMAND short_comparison_test) + + add_executable(multifile_test unittests/multifile_test_main.cpp unittests/multifile_test_chai.cpp unittests/multifile_test_module.cpp) target_link_libraries(multifile_test ${LIBS}) @@ -184,29 +197,28 @@ IF(BUILD_TESTING) target_link_libraries(test_module ${LIBS}) install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript) - ENDIF() -ENDIF(BUILD_TESTING) + endif() +endif(BUILD_TESTING) install(TARGETS chai ${MODULES} RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript ) -install(DIRECTORY include/chaiscript DESTINATION include +install(DIRECTORY include/chaiscript DESTINATION include PATTERN "*.hpp" PATTERN "*/.svn*" EXCLUDE PATTERN "*/.git*" EXCLUDE PATTERN "*~" EXCLUDE) -install(DIRECTORY unittests DESTINATION share/chaiscript +install(DIRECTORY unittests DESTINATION share/chaiscript PATTERN "*.chai" PATTERN "*.inc" PATTERN "*/.svn*" EXCLUDE PATTERN "*/.git*" EXCLUDE PATTERN "*~" EXCLUDE) -install(DIRECTORY samples DESTINATION share/chaiscript +install(DIRECTORY samples DESTINATION share/chaiscript PATTERN "*.chai" PATTERN "*/.svn*" EXCLUDE PATTERN "*/.git*" EXCLUDE PATTERN "*~" EXCLUDE) - configure_file(contrib/pkgconfig/chaiscript.pc.in lib/pkgconfig/chaiscript.pc @ONLY) -install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc" - DESTINATION lib/pkgconfig) +install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc" + DESTINATION lib/pkgconfig) diff --git a/contrib/geshi/chaiscript.php b/contrib/geshi/chaiscript.php index 99847ec..2235c8c 100644 --- a/contrib/geshi/chaiscript.php +++ b/contrib/geshi/chaiscript.php @@ -48,7 +48,7 @@ $language_data = array ( 'ESCAPE_CHAR' => '\\', 'KEYWORDS' => array( 1 => array( - 'break', 'else', 'else if', 'eval', 'for', 'if', 'return', 'while', 'try', 'catch', 'finally', + 'break', 'else', 'else if', 'eval', 'for', 'if', 'return', 'while', 'try', 'catch', 'finally', 'case', 'switch', 'default', ), 2 => array( 'def', 'false', 'fun', 'true', 'var', 'attr', diff --git a/contrib/vim/syntax/chaiscript.vim b/contrib/vim/syntax/chaiscript.vim index 5a64bdb..4a55262 100644 --- a/contrib/vim/syntax/chaiscript.vim +++ b/contrib/vim/syntax/chaiscript.vim @@ -7,6 +7,9 @@ if exists("b:current_syntax") finish end +let s:cpo_save = &cpo +set cpo&vim + syn case match " syncing method @@ -16,11 +19,11 @@ syn sync fromstart syn region chaiscriptString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=chaiscriptSpecial,chaiscriptEval,@Spell " Escape characters -syn match chaiscriptSpecial contained "\\[\\abfnrtv\'\"]\|\\\d\{,3}" +syn match chaiscriptSpecial contained "\\[\\abfnrtv\'\"]\|\\\d\{,3}" " String evals -syn region chaiscriptEval contained start="${" end="}" - +syn region chaiscriptEval contained start="${" end="}" + " integer number syn match chaiscriptNumber "\<\d\+\>" @@ -42,7 +45,7 @@ syn match chaiscriptNumber "\<0b[01]\+\>" " Various language features syn keyword chaiscriptCond if else syn keyword chaiscriptRepeat while for do -syn keyword chaiscriptStatement break continue return +syn keyword chaiscriptStatement break continue return switch case default syn keyword chaiscriptExceptions try catch throw "Keyword @@ -91,4 +94,6 @@ hi def link chaiscriptEval Special let b:current_syntax = "chaiscript" +let &cpo = s:cpo_save +unlet s:cpo_save " vim: nowrap sw=2 sts=2 ts=8 noet diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 0f17284..4f97996 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -192,8 +192,8 @@ /// Overloaded methods will need some help, to hint the compiler as to which overload you want: /// /// \code -/// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod")); -/// chai.add(fun(&MyClass::overloadedmethod, "overloadedmethod")); +/// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); +/// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); /// \endcode /// /// There are also shortcuts built into chaiscript::fun for binding up to the first two parameters of the function. diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index f4f75e5..22c3301 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp index ef26d0b..8fdf94c 100644 --- a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/bind_first.hpp b/include/chaiscript/dispatchkit/bind_first.hpp index ae468f1..e00a53c 100644 --- a/include/chaiscript/dispatchkit/bind_first.hpp +++ b/include/chaiscript/dispatchkit/bind_first.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index d61f338..97338bd 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -422,9 +422,6 @@ namespace chaiscript m->add(fun(&Type_Info::name), "cpp_name"); m->add(fun(&Type_Info::bare_name), "cpp_bare_name"); m->add(fun(&Type_Info::bare_equal), "bare_equal"); - typedef bool (Type_Info::*typeinfocompare)(const Type_Info &) const; - m->add(fun(typeinfocompare(&Type_Info::operator==)), "=="); - m->add(fun(&Type_Info::bare_equal), "bare_equal"); basic_constructors("bool", m); diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 96710c0..d7bff65 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index e082ce3..4344095 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp index ceb0178..44d38e0 100644 --- a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index d76c7a9..628d6e3 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -281,7 +281,7 @@ namespace chaiscript } else if (inp_ == typeid(boost::int8_t)) { return oper_rhs(t_oper, t_lhs, t_rhs); } else if (inp_ == typeid(boost::int16_t)) { - return oper_rhs(t_oper, t_lhs, t_rhs); + return oper_rhs(t_oper, t_lhs, t_rhs); } else if (inp_ == typeid(boost::int32_t)) { return oper_rhs(t_oper, t_lhs, t_rhs); } else if (inp_ == typeid(boost::int64_t)) { @@ -302,19 +302,15 @@ namespace chaiscript public: + Boxed_Number() + : bv(Boxed_Value(0)) + { + } + Boxed_Number(const Boxed_Value &v) : bv(v) { - const Type_Info &inp_ = v.get_type_info(); - if (inp_ == typeid(bool)) - { - throw boost::bad_any_cast(); - } - - if (!inp_.is_arithmetic()) - { - throw boost::bad_any_cast(); - } + validate_boxed_number(v); } @@ -383,6 +379,27 @@ namespace chaiscript return oper(Operators::assign_bitwise_and, this->bv, t_rhs.bv); } + void validate_boxed_number(const Boxed_Value &v) + { + const Type_Info &inp_ = v.get_type_info(); + if (inp_ == typeid(bool)) + { + throw boost::bad_any_cast(); + } + + if (!inp_.is_arithmetic()) + { + throw boost::bad_any_cast(); + } + } + + Boxed_Number operator=(const Boxed_Value &v) + { + validate_boxed_number(v); + bv = v; + return *this; + } + Boxed_Number operator=(const Boxed_Number &t_rhs) const { return oper(Operators::assign, this->bv, t_rhs.bv); diff --git a/include/chaiscript/dispatchkit/boxed_value.hpp b/include/chaiscript/dispatchkit/boxed_value.hpp index d49931f..59e7f90 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -71,7 +71,6 @@ namespace chaiscript void *m_data_ptr; const void *m_const_data_ptr; bool m_is_ref; - std::vector > m_dependencies; }; struct Object_Data @@ -242,24 +241,6 @@ namespace chaiscript return !is_ref(); } - void clear_dependencies() - { - m_data->m_dependencies.clear(); - } - - template - void add_dependencies(InItr begin, const InItr &end) - { - while (begin != end) - { - if (begin->m_data != m_data) - { - m_data->m_dependencies.push_back(begin->m_data); - } - ++begin; - } - } - void *get_ptr() const { return m_data->m_data_ptr; diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 550a9fb..9269ff7 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -32,6 +32,71 @@ namespace chaiscript { + namespace exception + { + /** + * Exception thrown in the case that an object name is invalid because it is a reserved word + */ + class reserved_word_error : public std::runtime_error + { + public: + reserved_word_error(const std::string &t_word) throw() + : std::runtime_error("Reserved word not allowed in object name: " + t_word), m_word(t_word) + { + } + + virtual ~reserved_word_error() throw() {} + + std::string word() const + { + return m_word; + } + + private: + std::string m_word; + + }; + + /** + * Exception thrown in the case that an object name is invalid because it already exists in current context + */ + class name_conflict_error : public std::runtime_error + { + public: + name_conflict_error(const std::string &t_name) throw() + : std::runtime_error("Name already exists in current context " + t_name), m_name(t_name) + { + } + + virtual ~name_conflict_error() throw() {} + + std::string name() const + { + return m_name; + } + + private: + std::string m_name; + + }; + + + /** + * Exception thrown in the case that a non-const object was added as a shared object + */ + class global_non_const : public std::runtime_error + { + public: + global_non_const() throw() + : std::runtime_error("a global object must be const") + { + } + + virtual ~global_non_const() throw() {} + }; + } + + /// \brief Holds a collection of ChaiScript settings which can be applied to the ChaiScript runtime. /// Used to implement loadable module support. class Module @@ -55,6 +120,17 @@ namespace chaiscript return *this; } + Module &add_global_const(const Boxed_Value &t_bv, const std::string &t_name) + { + if (!t_bv.is_const()) + { + throw exception::global_non_const(); + } + + m_globals.push_back(std::make_pair(t_bv, t_name)); + return *this; + } + //Add a bit of chaiscript to eval during module implementation Module &eval(const std::string &str) @@ -76,11 +152,13 @@ namespace chaiscript apply(m_funcs.begin(), m_funcs.end(), t_engine); apply_eval(m_evals.begin(), m_evals.end(), t_eval); apply_single(m_conversions.begin(), m_conversions.end(), t_engine); + apply_globals(m_globals.begin(), m_globals.end(), t_engine); } private: std::vector > m_typeinfos; std::vector > m_funcs; + std::vector > m_globals; std::vector m_evals; std::vector m_conversions; @@ -89,7 +167,22 @@ namespace chaiscript { while (begin != end) { - t.add(begin->first, begin->second); + try { + t.add(begin->first, begin->second); + } catch (const exception::name_conflict_error &) { + /// \todo Should we throw an error if there's a name conflict + /// while applying a module? + } + ++begin; + } + } + + template + void apply_globals(InItr begin, InItr end, T &t) const + { + while (begin != end) + { + t.add_global_const(begin->first, begin->second); ++begin; } } @@ -266,48 +359,6 @@ namespace chaiscript }; } - namespace exception - { - /** - * Exception thrown in the case that a multi method dispatch fails - * because no matching function was found - * at runtime due to either an arity_error, a guard_error or a bad_boxed_cast - * exception - */ - class reserved_word_error : public std::runtime_error - { - public: - reserved_word_error(const std::string &t_word) throw() - : std::runtime_error("Reserved word not allowed in object name: " + t_word), m_word(t_word) - { - } - - virtual ~reserved_word_error() throw() {} - - std::string word() const - { - return m_word; - } - - private: - std::string m_word; - - }; - - /** - * Exception thrown in the case that a non-const object was added as a shared object - */ - class global_non_const : public std::runtime_error - { - public: - global_non_const() throw() - : std::runtime_error("a global object must be const") - { - } - - virtual ~global_non_const() throw() {} - }; - } namespace detail { @@ -326,6 +377,7 @@ namespace chaiscript struct State { std::map > m_functions; + std::map m_function_objects; std::map m_global_objects; Type_Name_Map m_types; std::set m_reserved_words; @@ -353,10 +405,10 @@ namespace chaiscript /** * Add a new named Proxy_Function to the system */ - bool add(const Proxy_Function &f, const std::string &name) + void add(const Proxy_Function &f, const std::string &name) { validate_object_name(name); - return add_function(f, name); + add_function(f, name); } /** @@ -380,6 +432,7 @@ namespace chaiscript add_object(name, obj); } + /** * Adds a named object to the current scope @@ -388,7 +441,15 @@ namespace chaiscript { StackData &stack = get_stack_data(); validate_object_name(name); - stack.back()[name] = obj; + + Scope &scope = stack.back(); + Scope::iterator itr = scope.find(name); + if (itr != stack.back().end()) + { + throw exception::name_conflict_error(name); + } else { + stack.back().insert(std::make_pair(name, obj)); + } } /** @@ -404,7 +465,12 @@ namespace chaiscript chaiscript::detail::threading::unique_lock l(m_global_object_mutex); - m_state.m_global_objects[name] = obj; + if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) + { + throw exception::name_conflict_error(name); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } } /** @@ -430,28 +496,24 @@ namespace chaiscript } } - /** - * Swaps out the stack with a new stack - * \returns the old stack - * \param[in] s The new stack - */ - Stack set_stack(const Stack &s) - { - Stack old = m_stack_holder->stack; - m_stack_holder->stack = s; - return old; - } - Stack new_stack() const + /// Pushes a new stack on to the list of stacks + void new_stack() { Stack s(new Stack::element_type()); s->push_back(Scope()); - return s; + m_stack_holder->stacks.push_back(s); } + void pop_stack() + { + m_stack_holder->stacks.pop_back(); + } + + /// \returns the current stack Stack get_stack() const { - return m_stack_holder->stack; + return m_stack_holder->stacks.back(); } /** @@ -491,21 +553,7 @@ namespace chaiscript } // If all that failed, then check to see if it's a function - std::vector funcs = get_function(name); - - if (funcs.empty()) - { - throw std::range_error("Object not known: " + name); - } else { - if (funcs.size() == 1) - { - // Return the first item if there is only one, - // no reason to take the cast of the extra level of dispatch - return const_var(*funcs.begin()); - } else { - return Boxed_Value(Const_Proxy_Function(new Dispatch_Function(funcs))); - } - } + return get_function_object(name); } /** @@ -588,9 +636,26 @@ namespace chaiscript } else { return std::vector(); } - } + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + Boxed_Value get_function_object(const std::string &t_name) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const std::map &funs = get_function_objects_int(); + + std::map::const_iterator itr = funs.find(t_name); + + if (itr != funs.end()) + { + return const_var(itr->second); + } else { + throw std::range_error("Object not found: " + t_name); + } + } + /** * Return true if a function exists */ @@ -602,6 +667,57 @@ namespace chaiscript return functions.find(name) != functions.end(); } + + /// + /// Get a map of all objects that can be seen from the current scope in a scripting context + /// + std::map get_scripting_objects() const + { + // We don't want the current context, but one up if it exists + StackData &stack = (m_stack_holder->stacks.size()==1)?(*(m_stack_holder->stacks.back())):(*m_stack_holder->stacks[m_stack_holder->stacks.size()-2]); + + std::map retval; + + // note: map insert doesn't overwrite existing values, which is why this works + + for (StackData::reverse_iterator itr = stack.rbegin(); itr != stack.rend(); ++itr) + { + retval.insert(itr->begin(), itr->end()); + } + + // add the global values + { + chaiscript::detail::threading::shared_lock l(m_global_object_mutex); + + retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); + } + + return retval; + } + + + /// + /// Get a map of all functions that can be seen from a scripting context + /// + std::map get_function_objects() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const std::map &funs = get_function_objects_int(); + + std::map objs; + + for (std::map::const_iterator itr = funs.begin(); + itr != funs.end(); + ++itr) + { + objs.insert(std::make_pair(itr->first, const_var(itr->second))); + } + + return objs; + } + + /** * Get a vector of all registered functions */ @@ -781,6 +897,29 @@ namespace chaiscript m_state = t_state; } + void save_function_params(const std::vector &t_params) + { + m_stack_holder->call_params.insert(m_stack_holder->call_params.begin(), t_params.begin(), t_params.end()); + } + + void new_function_call() + { + ++m_stack_holder->call_depth; + } + + void pop_function_call() + { + --m_stack_holder->call_depth; + + assert(m_stack_holder->call_depth >= 0); + + if (m_stack_holder->call_depth == 0) + { + /// \todo Critical: this needs to be smarter, memory can expand quickly + /// in tight loops involving function calls + m_stack_holder->call_params.clear(); + } + } private: /** @@ -789,7 +928,17 @@ namespace chaiscript */ StackData &get_stack_data() const { - return *(m_stack_holder->stack); + return *(m_stack_holder->stacks.back()); + } + + const std::map &get_function_objects_int() const + { + return m_state.m_function_objects; + } + + std::map &get_function_objects_int() + { + return m_state.m_function_objects; } const std::map > &get_functions_int() const @@ -911,11 +1060,10 @@ namespace chaiscript } /** - * Implementation detail for adding a function. Returns - * true if the function was added, false if a function with the - * same signature and name already exists. + * Implementation detail for adding a function. + * \throws exception::name_conflict_error if there's a function matching the given one being added */ - bool add_function(const Proxy_Function &t_f, const std::string &t_name) + void add_function(const Proxy_Function &t_f, const std::string &t_name) { chaiscript::detail::threading::unique_lock l(m_mutex); @@ -924,6 +1072,8 @@ namespace chaiscript std::map >::iterator itr = funcs.find(t_name); + std::map &func_objs = get_function_objects_int(); + if (itr != funcs.end()) { std::vector &vec = itr->second; @@ -933,19 +1083,21 @@ namespace chaiscript { if ((*t_f) == *(*itr2)) { - return false; + throw exception::name_conflict_error(t_name); } } vec.push_back(t_f); std::stable_sort(vec.begin(), vec.end(), &function_less_than); + func_objs[t_name] = Proxy_Function(new Dispatch_Function(vec)); } else { std::vector vec; vec.push_back(t_f); funcs.insert(std::make_pair(t_name, vec)); + func_objs[t_name] = t_f; } - return true; + } mutable chaiscript::detail::threading::shared_mutex m_mutex; @@ -954,12 +1106,17 @@ namespace chaiscript struct Stack_Holder { Stack_Holder() - : stack(new StackData()) + : call_depth(0) { - stack->push_back(Scope()); + Stack s(new StackData()); + s->push_back(Scope()); + stacks.push_back(s); } - Stack stack; + std::deque stacks; + + std::list call_params; + int call_depth; }; std::vector m_conversions; diff --git a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp b/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp index dd14ff8..cfade60 100644 --- a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp +++ b/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 0269088..43a1cad 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -44,20 +44,6 @@ namespace chaiscript namespace detail { - struct Dynamic_Object_Attribute - { - static Boxed_Value func(const std::string &t_type_name, const std::string &t_attr_name, - Dynamic_Object &t_do) - { - if (t_do.get_type_name() != t_type_name) - { - throw exception::bad_boxed_cast("Dynamic object type mismatch"); - } - - return t_do.get_attr(t_attr_name); - } - }; - /** * A Proxy_Function implementation designed for calling a function * that is automatically guarded based on the first param based on the diff --git a/include/chaiscript/dispatchkit/exception_specification.hpp b/include/chaiscript/dispatchkit/exception_specification.hpp index 4c0d19a..74841ed 100644 --- a/include/chaiscript/dispatchkit/exception_specification.hpp +++ b/include/chaiscript/dispatchkit/exception_specification.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/function_call_detail.hpp b/include/chaiscript/dispatchkit/function_call_detail.hpp index 49a877a..1ec79af 100644 --- a/include/chaiscript/dispatchkit/function_call_detail.hpp +++ b/include/chaiscript/dispatchkit/function_call_detail.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index 3a52eb1..9c649fb 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -34,6 +34,15 @@ namespace chaiscript } }; + template + struct Handle_Return + { + static Boxed_Value handle(Ret *p) + { + return Boxed_Value(p); + } + }; + template struct Handle_Return &> { diff --git a/include/chaiscript/dispatchkit/operators.hpp b/include/chaiscript/dispatchkit/operators.hpp index 343da1e..ee0196f 100644 --- a/include/chaiscript/dispatchkit/operators.hpp +++ b/include/chaiscript/dispatchkit/operators.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/proxy_constructors.hpp b/include/chaiscript/dispatchkit/proxy_constructors.hpp index fcd019b..265d767 100644 --- a/include/chaiscript/dispatchkit/proxy_constructors.hpp +++ b/include/chaiscript/dispatchkit/proxy_constructors.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index b6bb7e0..fe47ae5 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -74,7 +74,6 @@ namespace chaiscript Boxed_Value operator()(const std::vector ¶ms) const { Boxed_Value bv = do_call(params); - bv.add_dependencies(params.begin(), params.end()); return bv; } @@ -219,7 +218,12 @@ namespace chaiscript virtual bool operator==(const Proxy_Function_Base &rhs) const { - return this == &rhs; + const Dynamic_Proxy_Function *prhs = dynamic_cast(&rhs); + + return this == &rhs + || (prhs + && this->m_arity == prhs->m_arity + && !this->m_guard && !prhs->m_guard); } virtual bool call_match(const std::vector &vals) const diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index acd85d2..c266827 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index 83e5687..e3f6e48 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 8bd0b9a..6271b30 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/language/chaiscript_algebraic.hpp b/include/chaiscript/language/chaiscript_algebraic.hpp index 37cc0ba..13ae897 100644 --- a/include/chaiscript/language/chaiscript_algebraic.hpp +++ b/include/chaiscript/language/chaiscript_algebraic.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 7b21f52..6c71cc8 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -23,7 +23,7 @@ namespace chaiscript Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Map_Pair, Value_Range, Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or, - Logical_And, Logical_Or + Logical_And, Logical_Or, Switch, Case, Default, Ternary_Cond }; }; @@ -37,7 +37,7 @@ namespace chaiscript "Comparison", "Addition", "Subtraction", "Multiplication", "Division", "Modulus", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Map_Pair", "Value_Range", "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or", - "Logical_And", "Logical_Or"}; + "Logical_And", "Logical_Or", "Switch", "Case", "Default", "Ternary Condition"}; return ast_node_types[ast_node_type]; } @@ -213,6 +213,57 @@ namespace chaiscript chaiscript::detail::Dispatch_Engine &m_de; }; + + /// Creates a new functon call and pops it on destruction + struct Function_Push_Pop + { + Function_Push_Pop(chaiscript::detail::Dispatch_Engine &t_de) + : m_de(t_de) + { + m_de.new_function_call(); + } + + ~Function_Push_Pop() + { + m_de.pop_function_call(); + } + + void save_params(const std::vector &t_params) + { + m_de.save_function_params(t_params); + } + + + private: + // explicitly unimplemented copy and assignment + Function_Push_Pop(const Function_Push_Pop &); + Function_Push_Pop& operator=(const Function_Push_Pop &); + + chaiscript::detail::Dispatch_Engine &m_de; + }; + + /// Creates a new scope then pops it on destruction + struct Stack_Push_Pop + { + Stack_Push_Pop(chaiscript::detail::Dispatch_Engine &t_de) + : m_de(t_de) + { + m_de.new_stack(); + } + + ~Stack_Push_Pop() + { + m_de.pop_stack(); + } + + + private: + // explicitly unimplemented copy and assignment + Stack_Push_Pop(const Stack_Push_Pop &); + Stack_Push_Pop& operator=(const Stack_Push_Pop &); + + chaiscript::detail::Dispatch_Engine &m_de; + }; } } } diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 7fac714..b970d12 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -76,7 +76,7 @@ namespace chaiscript struct DLSym { DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) + : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str()))) { if (!m_symbol) { @@ -84,6 +84,19 @@ namespace chaiscript } } + static T cast_symbol(void *p) + { + union cast_union + { + T func_ptr; + void *in_ptr; + }; + + cast_union c; + c.in_ptr = p; + return c.func_ptr; + } + T m_symbol; }; @@ -261,7 +274,11 @@ namespace chaiscript const Boxed_Value internal_eval_ast(const AST_NodePtr &t_ast) { - return t_ast->eval(m_engine); + try { + return t_ast->eval(m_engine); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } } @@ -269,37 +286,14 @@ namespace chaiscript * Evaluates the given string, used during eval() inside of a script */ const Boxed_Value internal_eval(const std::string &t_e) { - return do_eval(t_e, "__EVAL__", true); - } - - void use(const std::string &t_filename) - { - for (size_t i = 0; i < m_usepaths.size(); ++i) - { - - try { - const std::string appendedpath = m_usepaths[i] + t_filename; - - chaiscript::detail::threading::lock_guard l(m_use_mutex); - chaiscript::detail::threading::shared_lock l2(m_mutex); - - if (m_used_files.count(appendedpath) == 0) - { - m_used_files.insert(appendedpath); - l2.unlock(); - eval_file(appendedpath); - } - } catch (const exception::file_not_found_error &) { - if (i == m_usepaths.size() - 1) - { - throw exception::file_not_found_error(t_filename); - } - - // failed to load, try the next path - } + try { + return do_eval(t_e, "__EVAL__", true); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); } } + /** * Returns the current evaluation m_engine */ @@ -312,6 +306,7 @@ namespace chaiscript */ void build_eval_system() { using namespace bootstrap; + m_engine.add_reserved_word("auto"); m_engine.add_reserved_word("def"); m_engine.add_reserved_word("fun"); m_engine.add_reserved_word("while"); @@ -336,6 +331,8 @@ namespace chaiscript m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::is_type, boost::ref(m_engine)), "is_type"); m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::type_name, boost::ref(m_engine)), "type_name"); m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::function_exists, boost::ref(m_engine)), "function_exists"); + m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_function_objects, boost::ref(m_engine)), "get_functions"); + m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_scripting_objects, boost::ref(m_engine)), "get_objects"); m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_type_name, boost::ref(m_engine)), "name"); @@ -404,6 +401,40 @@ namespace chaiscript build_eval_system(); } + /// \brief Loads and parses a file. If the file is already, it is not reloaded + /// The use paths specified at ChaiScript construction time are searched for the + /// requested file. + /// + /// \param[in] t_filename Filename to load and evaluate + void use(const std::string &t_filename) + { + for (size_t i = 0; i < m_usepaths.size(); ++i) + { + try { + const std::string appendedpath = m_usepaths[i] + t_filename; + + chaiscript::detail::threading::lock_guard l(m_use_mutex); + chaiscript::detail::threading::shared_lock l2(m_mutex); + + if (m_used_files.count(appendedpath) == 0) + { + m_used_files.insert(appendedpath); + l2.unlock(); + eval_file(appendedpath); + } + + return; // return, we loaded it, or it was already loaded + } catch (const exception::file_not_found_error &) { + if (i == m_usepaths.size() - 1) + { + throw exception::file_not_found_error(t_filename); + } + + // failed to load, try the next path + } + } + } + /// \brief Adds a constant object that is available in all contexts and to all threads /// \param[in] t_bv Boxed_Value to add as a global /// \param[in] t_name Name of the value to add diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index bd2ee1a..33e1c3c 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -54,6 +54,12 @@ namespace chaiscript Operators::Opers t_oper, const std::string &t_oper_string, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) { try { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + std::vector params(2); + params.push_back(t_lhs); + params.push_back(t_rhs); + fpp.save_params(params); + if (t_oper != Operators::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) { // If it's an arithmetic operation we want to short circuit dispatch @@ -64,6 +70,7 @@ namespace chaiscript } } else { + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); return t_ss.call_function(t_oper_string, t_lhs, t_rhs); } } @@ -182,6 +189,7 @@ namespace chaiscript AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } virtual ~Fun_Call_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss){ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); dispatch::Param_List_Builder plb; if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { @@ -190,36 +198,21 @@ namespace chaiscript } } - chaiscript::detail::Dispatch_Engine::Stack prev_stack = t_ss.get_stack(); - chaiscript::detail::Dispatch_Engine::Stack new_stack = t_ss.new_stack(); + fpp.save_params(plb.objects); + + Boxed_Value fn = this->children[0]->eval(t_ss); try { - Boxed_Value fn = this->children[0]->eval(t_ss); - - try { - t_ss.set_stack(new_stack); - const Boxed_Value &retval = (*boxed_cast(fn))(plb); - t_ss.set_stack(prev_stack); - return retval; - } - catch(const exception::dispatch_error &e){ - t_ss.set_stack(prev_stack); - throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); - } - catch(detail::Return_Value &rv) { - t_ss.set_stack(prev_stack); - return rv.retval; - } - catch(...) { - t_ss.set_stack(prev_stack); - throw; - } + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + const Boxed_Value &retval = (*boxed_cast(fn))(plb); + return retval; } - catch(exception::eval_error &) { - t_ss.set_stack(prev_stack); - throw; + catch(const exception::dispatch_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(detail::Return_Value &rv) { + return rv.retval; } - } }; @@ -296,7 +289,6 @@ namespace chaiscript try { if (lhs.is_undef()) { retval = t_ss.call_function("clone", retval); - retval.clear_dependencies(); } try { @@ -340,6 +332,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &) { throw exception::eval_error("Reserved word used as variable '" + this->children[0]->text + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); } return t_ss.get_object(this->children[0]->text); } @@ -419,11 +413,20 @@ namespace chaiscript AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } virtual ~Array_Call_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss){ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); Boxed_Value retval = this->children[0]->eval(t_ss); + std::vector params; + params.push_back(retval); for (size_t i = 1; i < this->children.size(); ++i) { try { - retval = t_ss.call_function("[]", retval, this->children[i]->eval(t_ss)); + Boxed_Value p1(this->children[i]->eval(t_ss)); + + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + params.push_back(p1); + fpp.save_params(params); + params.clear(); + retval = t_ss.call_function("[]", retval, p1); } catch(std::out_of_range &) { throw exception::eval_error("Out of bounds exception"); @@ -447,6 +450,7 @@ namespace chaiscript if (this->children.size() > 1) { for (size_t i = 2; i < this->children.size(); i+=2) { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); dispatch::Param_List_Builder plb; plb << retval; @@ -456,6 +460,8 @@ namespace chaiscript } } + fpp.save_params(plb.objects); + std::string fun_name; if ((this->children[i]->identifier == AST_Node_Type::Fun_Call) || (this->children[i]->identifier == AST_Node_Type::Array_Call)) { fun_name = this->children[i]->children[0]->text; @@ -464,26 +470,17 @@ namespace chaiscript fun_name = this->children[i]->text; } - chaiscript::detail::Dispatch_Engine::Stack prev_stack = t_ss.get_stack(); - chaiscript::detail::Dispatch_Engine::Stack new_stack = t_ss.new_stack(); - try { - t_ss.set_stack(new_stack); + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); retval = t_ss.call_function(fun_name, plb); - t_ss.set_stack(prev_stack); } catch(const exception::dispatch_error &e){ - t_ss.set_stack(prev_stack); throw exception::eval_error(std::string(e.what()) + " for function: " + fun_name); } catch(detail::Return_Value &rv) { - t_ss.set_stack(prev_stack); retval = rv.retval; } - catch(...) { - t_ss.set_stack(prev_stack); - throw; - } + if (this->children[i]->identifier == AST_Node_Type::Array_Call) { for (size_t j = 1; j < this->children[i]->children.size(); ++j) { try { @@ -639,6 +636,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &e) { throw exception::eval_error("Reserved word used as function name '" + e.word() + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Function redefined '" + e.name() + "'"); } return Boxed_Value(); } @@ -681,6 +680,29 @@ namespace chaiscript }; + struct Ternary_Cond_AST_Node : public AST_Node { + public: + Ternary_Cond_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::If, const boost::shared_ptr &t_fname=boost::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : + AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } + virtual ~Ternary_Cond_AST_Node() {} + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss){ + bool cond; + try { + cond = boxed_cast(this->children[0]->eval(t_ss)); + } + catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("If condition not boolean"); + } + + if (cond) { + return this->children[1]->eval(t_ss); + } + else { + return this->children[2]->eval(t_ss); + } + } + }; + struct If_AST_Node : public AST_Node { public: If_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::If, const boost::shared_ptr &t_fname=boost::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : @@ -776,20 +798,97 @@ namespace chaiscript }; + struct Switch_AST_Node : public AST_Node { + public: + Switch_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Switch, const boost::shared_ptr &t_fname=boost::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : + AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } + virtual ~Switch_AST_Node() {} + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) { + Boxed_Value match_value; + bool breaking = false; + int currentCase = 1; + bool hasMatched = false; + + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + match_value = this->children[0]->eval(t_ss); + + while (!breaking && (currentCase < this->children.size())) { + try { + 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(t_ss.call_function("==", match_value, this->children[currentCase]->children[0]->eval(t_ss)))) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; + } + } + catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("Internal error: case guard evaluation not boolean"); + } + } + else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { + this->children[currentCase]->eval(t_ss); + breaking = true; + } + } + catch (detail::Break_Loop &) { + breaking = true; + } + ++currentCase; + } + return Boxed_Value(); + } + }; + + struct Case_AST_Node : public AST_Node { + public: + Case_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Case, const boost::shared_ptr &t_fname=boost::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : + AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } + virtual ~Case_AST_Node() {} + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + this->children[1]->eval(t_ss); + + return Boxed_Value(); + } + }; + + struct Default_AST_Node : public AST_Node { + public: + Default_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Default, const boost::shared_ptr &t_fname=boost::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : + AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } + virtual ~Default_AST_Node() {} + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + this->children[0]->eval(t_ss); + + return Boxed_Value(); + } + }; + struct Inline_Array_AST_Node : public AST_Node { public: Inline_Array_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Inline_Array, const boost::shared_ptr &t_fname=boost::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } virtual ~Inline_Array_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss){ - std::vector vec; - if (this->children.size() > 0) { - for (size_t i = 0; i < this->children[0]->children.size(); ++i) { - vec.push_back(this->children[0]->children[i]->eval(t_ss)); + try { + std::vector vec; + if (this->children.size() > 0) { + for (size_t i = 0; i < this->children[0]->children.size(); ++i) { + Boxed_Value bv = t_ss.call_function("clone", this->children[0]->children[i]->eval(t_ss)); + vec.push_back(bv); + } } + return const_var(vec); + } + catch (const exception::dispatch_error &) { + throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements"); } - return const_var(vec); } }; @@ -803,13 +902,14 @@ namespace chaiscript try { std::map retval; for (size_t i = 0; i < this->children[0]->children.size(); ++i) { + Boxed_Value bv = t_ss.call_function("clone", this->children[0]->children[i]->children[1]->eval(t_ss)); retval[boxed_cast(this->children[0]->children[i]->children[0]->eval(t_ss))] - = t_ss.call_function("clone", this->children[0]->children[i]->children[1]->eval(t_ss)); + = bv; } return const_var(retval); } catch (const exception::dispatch_error &) { - throw exception::eval_error("Can not find appropriate 'Map()'"); + throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for map elements"); } } @@ -856,6 +956,7 @@ namespace chaiscript virtual ~Prefix_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss){ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); Boxed_Value bv(this->children[1]->eval(t_ss)); Operators::Opers oper = Operators::to_operator(children[0]->text, true); @@ -864,6 +965,10 @@ namespace chaiscript { return Boxed_Number::do_oper(oper, bv); } else { + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + std::vector params; + params.push_back(bv); + fpp.save_params(params); return t_ss.call_function(this->children[0]->text, bv); } } catch (const exception::dispatch_error &) { @@ -950,6 +1055,7 @@ namespace chaiscript end_point = this->children.size() - 1; } for (unsigned int i = 1; i < end_point; ++i) { + chaiscript::eval::detail::Scope_Push_Pop catchscope(t_ss); AST_NodePtr catch_block = this->children[i]; if (catch_block->children.size() == 1) { @@ -992,6 +1098,7 @@ namespace chaiscript } catch (Boxed_Value &except) { for (size_t i = 1; i < this->children.size(); ++i) { + chaiscript::eval::detail::Scope_Push_Pop catchscope(t_ss); AST_NodePtr catch_block = this->children[i]; if (catch_block->children.size() == 1) { @@ -1006,7 +1113,7 @@ namespace chaiscript break; } else if (catch_block->children.size() == 3) { - //Variable capture, no guards + //Variable capture, guards t_ss.add_object(catch_block->children[0]->text, except); bool guard; @@ -1134,6 +1241,8 @@ namespace chaiscript } catch (const exception::reserved_word_error &e) { throw exception::eval_error("Reserved word used as method name '" + e.word() + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Method redefined '" + e.name() + "'"); } return Boxed_Value(); } @@ -1147,12 +1256,22 @@ namespace chaiscript virtual ~Attr_Decl_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss){ try { - t_ss.add(fun(boost::function(boost::bind(&dispatch::detail::Dynamic_Object_Attribute::func, this->children[0]->text, - this->children[1]->text, _1))), this->children[1]->text); + + t_ss.add(Proxy_Function + (new dispatch::detail::Dynamic_Object_Function( + this->children[0]->text, + fun(boost::function(boost::bind(&dispatch::Dynamic_Object::get_attr, + _1, + this->children[1]->text + ))) + ) + ), this->children[1]->text); } catch (const exception::reserved_word_error &) { throw exception::eval_error("Reserved word used as attribute '" + this->children[1]->text + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Attribute redefined '" + e.name() + "'"); } return Boxed_Value(); } diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index c9fa7dc..3301800 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -66,6 +66,11 @@ namespace chaiscript void setup_operators() { + m_operators.push_back(AST_Node_Type::Ternary_Cond); + std::vector ternary_cond; + ternary_cond.push_back("?"); + m_operator_matches.push_back(ternary_cond); + m_operators.push_back(AST_Node_Type::Logical_Or); std::vector logical_or; logical_or.push_back("||"); @@ -131,6 +136,7 @@ namespace chaiscript m_alphabet[a][c]=false; } } + m_alphabet[detail::symbol_alphabet][static_cast('?')]=true; m_alphabet[detail::symbol_alphabet][static_cast('+')]=true; m_alphabet[detail::symbol_alphabet][static_cast('-')]=true; m_alphabet[detail::symbol_alphabet][static_cast('*')]=true; @@ -322,7 +328,6 @@ namespace chaiscript */ bool Float_() { bool retval = false; - std::string::const_iterator start = m_input_pos; if (has_more_input() && char_in_alphabet(*m_input_pos,detail::float_alphabet) ) { while (has_more_input() && char_in_alphabet(*m_input_pos,detail::int_alphabet) ) { @@ -1416,6 +1421,90 @@ namespace chaiscript return retval; } + /** + * Reads a case block from input + */ + bool Case() { + bool retval = false; + + size_t prev_stack_top = m_match_stack.size(); + + if (Keyword("case")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'case' expression", File_Position(m_line, m_col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'case' expression", File_Position(m_line, m_col), *m_filename); + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'case' block", File_Position(m_line, m_col), *m_filename); + } + + build_match(AST_NodePtr(new eval::Case_AST_Node()), prev_stack_top); + } + else if (Keyword("default")) { + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'default' block", File_Position(m_line, m_col), *m_filename); + } + + build_match(AST_NodePtr(new eval::Default_AST_Node()), prev_stack_top); + } + + return retval; + } + + /** + * Reads a switch statement from input + */ + bool Switch() { + size_t prev_stack_top = m_match_stack.size(); + + if (Keyword("switch")) { + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename); + } + + while (Eol()) {} + + if (Char('{')) { + while (Eol()) {} + + while (Case()) { + while (Eol()); + } + + while (Eol()); + + if (!Char('}')) { + throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename); + } + } + else { + throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename); + } + + build_match(AST_NodePtr(new eval::Switch_AST_Node()), prev_stack_top); + return true; + + } else { + return false; + } + + } + /** * Reads a curly-brace C-style block from input */ @@ -1478,7 +1567,6 @@ namespace chaiscript */ bool Dot_Fun_Array() { bool retval = false; - std::string::const_iterator prev_pos = m_input_pos; size_t prev_stack_top = m_match_stack.size(); if (Lambda() || Num(true) || Quoted_String(true) || Single_Quoted_String(true) || @@ -1720,6 +1808,7 @@ namespace chaiscript retval = true; if (Operator_Helper(t_precedence)) { do { + while (Eol()) {} if (!Operator(t_precedence+1)) { throw exception::eval_error("Incomplete " + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", @@ -1730,6 +1819,23 @@ namespace chaiscript case(AST_Node_Type::Comparison) : build_match(AST_NodePtr(new eval::Comparison_AST_Node()), prev_stack_top); break; + case(AST_Node_Type::Ternary_Cond) : + m_match_stack.erase(m_match_stack.begin() + m_match_stack.size() - 2, + m_match_stack.begin() + m_match_stack.size() - 1); + if (Symbol(":")) { + if (!Operator(t_precedence+1)) { + throw exception::eval_error("Incomplete " + + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", + File_Position(m_line, m_col), *m_filename); + } + build_match(AST_NodePtr(new eval::Ternary_Cond_AST_Node()), prev_stack_top); + } + else { + throw exception::eval_error("Incomplete " + + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", + File_Position(m_line, m_col), *m_filename); + } + break; case(AST_Node_Type::Addition) : oper = m_match_stack.at(m_match_stack.size()-2); m_match_stack.erase(m_match_stack.begin() + m_match_stack.size() - 2, @@ -1887,7 +1993,6 @@ namespace chaiscript bool saw_eol = true; while (has_more) { - has_more = false; int prev_line = m_line; int prev_col = m_col; if (Def()) { @@ -1930,6 +2035,14 @@ namespace chaiscript retval = true; saw_eol = true; } + else if (Switch()) { + if (!saw_eol) { + throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = true; + } else if (Return()) { if (!saw_eol) { throw exception::eval_error("Two expressions missing line separator", File_Position(prev_line, prev_col), *m_filename); diff --git a/include/chaiscript/language/chaiscript_prelude.hpp b/include/chaiscript/language/chaiscript_prelude.hpp index 04a4178..3188102 100644 --- a/include/chaiscript/language/chaiscript_prelude.hpp +++ b/include/chaiscript/language/chaiscript_prelude.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/utility/utility.hpp b/include/chaiscript/utility/utility.hpp index 80c8c5a..c719cd7 100644 --- a/include/chaiscript/utility/utility.hpp +++ b/include/chaiscript/utility/utility.hpp @@ -1,6 +1,6 @@ // This file is distributed under the BSD License. // See "license.txt" for details. -// Copyright 2009-2011, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -49,10 +49,20 @@ CHAISCRIPT_CLASS_METHODS((_module)(_class_name)(_class_name_translator)(_method_name_translator), _methods) \ } +#define CHAISCRIPT_CLASS_NO_CONSTRUCTOR_EX(_module, _class_name, _class_name_translator, _method_name_translator, _methods) \ + { \ + _module->add(chaiscript::user_type<_class_name>(), _class_name_translator (BOOST_PP_STRINGIZE(_class_name))); \ + CHAISCRIPT_CLASS_METHODS((_module)(_class_name)(_class_name_translator)(_method_name_translator), _methods) \ + } + #define CHAISCRIPT_CLASS(_module, _class_name, _constructors, _methods) \ CHAISCRIPT_CLASS_EX(_module, _class_name, chaiscript::utility::class_name_translator, \ chaiscript::utility::method_name_translator, _constructors, _methods) +#define CHAISCRIPT_CLASS_NO_CONSTRUCTOR(_module, _class_name, _methods) \ + CHAISCRIPT_CLASS_NO_CONSTRUCTOR_EX(_module, _class_name, chaiscript::utility::class_name_translator, \ + chaiscript::utility::method_name_translator, _methods) + namespace chaiscript { /// \brief Classes and functions which provide general utility to the end user. diff --git a/license.txt b/license.txt index 57cd72b..0601f2f 100644 --- a/license.txt +++ b/license.txt @@ -1,4 +1,4 @@ -Copyright 2009-2011 Jason Turner and Jonathan Turner. All Rights Reserved. +Copyright 2009-2012 Jason Turner and Jonathan Turner. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/readme.txt b/readme.txt index bddc703..ee04337 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ ChaiScript http://www.chaiscript.com -(c) 2009-2011 Jason Turner and Jonathan Turner +(c) 2009-2012 Jason Turner and Jonathan Turner Release under the BSD license, see "license.txt" for details. [Introduction] diff --git a/releasenotes.txt b/releasenotes.txt index 638e482..4a52301 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -1,3 +1,31 @@ +Changes since 3.1.0 +* svenstaro: Unused variables and CMake consistency fixes +* Added support for returning pointers from functions (#13) +* Compile with -pedantic (#9) +* Fix issues with multiple ChaiScript object types having the same attribute name (#15) +* Prevent variable redeclaration in same scope (#22) +* mgee: Boxed_Number improvements (#27) +* Support switch statements (#34) +* Fix uint16 comparions (#26) +* Add ability to add const_var globals in Module objects (#14) +* Add support for ternary operators ?: +* Add headers to CMakeLists so they show up in IDEs +* Add ability to get vector of defined objects and vector of defined functions +* Fix memory leak in cyclical references +* Clean up static analysis issues discovered +* Fix vector construction to be consistent with map construction +* Increased unit tests to 161 +* Performance enhancements + +Changes since 3.0.0 +* Numeric operations performance increased approximately 10x +* Looping operations performance increased up to 2x +* Engine start up time decreased +* Several parsing bugs related to index operators fixed +* Added full support for all C algebraic types: double, long double, float, int, long, char, + uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t +* Enhanced support for capturing of exceptions thrown from ChaiScript in C++ + Changes since 2.3.3 * Code simplifications diff --git a/samples/example.cpp b/samples/example.cpp index 51ef10a..47e2639 100644 --- a/samples/example.cpp +++ b/samples/example.cpp @@ -165,7 +165,7 @@ int main(int /*argc*/, char * /*argv*/[]) { //Dynamic objects test chai.add(chaiscript::Proxy_Function(new dispatch::detail::Dynamic_Object_Function("TestType", fun(&hello_world))), "hello_world"); chai.add(chaiscript::Proxy_Function(new dispatch::detail::Dynamic_Object_Constructor("TestType", fun(&hello_constructor))), "TestType"); - chai.add(fun(boost::function(boost::bind(&dispatch::detail::Dynamic_Object_Attribute::func, "TestType", "attr", _1))), "attr"); +// chai.add(fun(boost::function(boost::bind(&dispatch::detail::Dynamic_Object_Attribute::func, "TestType", "attr", _1))), "attr"); chai.eval("var x = TestType()"); // chai.eval("x.attr = \"hi\""); diff --git a/src/reflection.cpp b/src/reflection.cpp index 98f0dbb..2027c47 100644 --- a/src/reflection.cpp +++ b/src/reflection.cpp @@ -49,12 +49,12 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_reflect m->add(chaiscript::fun(&has_parse_tree), "has_parse_tree"); m->add(chaiscript::fun(&get_parse_tree), "get_parse_tree"); + m->add(chaiscript::base_class()); chaiscript::bootstrap::standard_library::vector_type > >("AST_NodeVector", m); - CHAISCRIPT_CLASS( m, + CHAISCRIPT_CLASS_NO_CONSTRUCTOR( m, chaiscript::exception::eval_error, - , ((reason)) ((call_stack)) ); @@ -67,9 +67,8 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_reflect ((column)) ); - CHAISCRIPT_CLASS( m, + CHAISCRIPT_CLASS_NO_CONSTRUCTOR( m, chaiscript::AST_Node, - , ((text)) ((identifier)) ((filename)) diff --git a/src/test_module.cpp b/src/test_module.cpp index 77455f6..633b9de 100644 --- a/src/test_module.cpp +++ b/src/test_module.cpp @@ -7,11 +7,22 @@ class TestBaseType public: TestBaseType() {} TestBaseType(int) {} + TestBaseType(int *) {} virtual ~TestBaseType() {} virtual int func() { return 0; } }; +enum TestEnum +{ + TestValue1 = 1 +}; + +int to_int(TestEnum t) +{ + return t; +} + class TestDerivedType : public TestBaseType { public: @@ -24,6 +35,11 @@ std::string hello_world() return "Hello World"; } +int *get_new_int() +{ + return new int(1); +} + // MSVC doesn't like that we are using C++ return types from our C declared module // but this is the best way to do it for cross platform compatibility #ifdef BOOST_MSVC @@ -44,6 +60,7 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::constructor(), "TestBaseType"); // m->add(chaiscript::constructor(), "TestBaseType"); m->add(chaiscript::constructor(), "TestBaseType"); + m->add(chaiscript::constructor(), "TestBaseType"); m->add(chaiscript::constructor(), "TestDerivedType"); m->add(chaiscript::constructor(), "TestDerivedType"); @@ -52,6 +69,15 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::fun(&TestBaseType::func), "func"); + m->add(chaiscript::fun(&get_new_int), "get_new_int"); + + m->add_global_const(chaiscript::const_var(TestValue1), "TestValue1"); + + m->add(chaiscript::user_type(), "TestEnum"); + + m->add(chaiscript::fun(&to_int), "to_int"); + + return m; } diff --git a/unittests/function_ordering_test.cpp b/unittests/function_ordering_test.cpp index 0a9652c..b4209a9 100644 --- a/unittests/function_ordering_test.cpp +++ b/unittests/function_ordering_test.cpp @@ -17,7 +17,7 @@ int main() chaiscript::ChaiScript chai; chai.eval("def test_fun(x) { return 3; }"); chai.eval("def test_fun(x) : x == \"hi\" { return 4; }"); - chai.eval("def test_fun(x) { return 5; }"); +// chai.eval("def test_fun(x) { return 5; }"); chai.add(chaiscript::fun(&test_one), "test_fun"); chai.add(chaiscript::fun(&test_two), "test_fun"); diff --git a/unittests/function_redefinition.chai b/unittests/function_redefinition.chai new file mode 100644 index 0000000..caabd3d --- /dev/null +++ b/unittests/function_redefinition.chai @@ -0,0 +1,2 @@ +assert_throws("Function already defined", fun() { def foo(x) { x + 1 }; def foo(x) { x + 1 } } ); + diff --git a/unittests/global_const_in_module.chai b/unittests/global_const_in_module.chai new file mode 100644 index 0000000..c9ca65a --- /dev/null +++ b/unittests/global_const_in_module.chai @@ -0,0 +1,7 @@ +load_module("test_module") + + +assert_equal(to_int(TestValue1), 1) + +assert_equal(TestValue1.type_name(), "TestEnum") + diff --git a/unittests/hashbang.chai b/unittests/hashbang.chai new file mode 100644 index 0000000..9490987 --- /dev/null +++ b/unittests/hashbang.chai @@ -0,0 +1,4 @@ +#!/usr/bin/env chai + +//We just have to reach this point for success +assert_equal(true, true); diff --git a/unittests/map_inplace_init.chai b/unittests/map_inplace_init.chai index b1d8221..138ef03 100644 --- a/unittests/map_inplace_init.chai +++ b/unittests/map_inplace_init.chai @@ -1,3 +1,14 @@ var x = ["bob":1, "fred":2] assert_equal(2, x.size()); + + +// Make sure vector elements are copied into place for consistency with +// map inplace construction +var i = 1; +var y = ["a":i]; + +assert_equal(1, y["a"]); +i = 3; +assert_equal(3, i); +assert_equal(1, y["a"]); diff --git a/unittests/multiline_oper.chai b/unittests/multiline_oper.chai new file mode 100644 index 0000000..1560ba4 --- /dev/null +++ b/unittests/multiline_oper.chai @@ -0,0 +1,4 @@ +var x = 3 + + 4 + +assert_equal(x, 7); diff --git a/unittests/object_attr_same_name.chai b/unittests/object_attr_same_name.chai new file mode 100644 index 0000000..fa20bac --- /dev/null +++ b/unittests/object_attr_same_name.chai @@ -0,0 +1,9 @@ +attr bob::z +def bob::bob() { this.z = 10 } + +attr bob2::z +def bob2::bob2() { this.z = 12 } + +var b = bob(); +var b2 = bob2(); + diff --git a/unittests/object_lifetime_test.cpp b/unittests/object_lifetime_test.cpp index d59957b..a58e3d2 100644 --- a/unittests/object_lifetime_test.cpp +++ b/unittests/object_lifetime_test.cpp @@ -38,19 +38,19 @@ int main() chaiscript::ChaiScript chai; chai.add(m); - chai.add(chaiscript::fun(&Test::count), "count"); +// chai.add(chaiscript::fun(&Test::count), "count"); int count = chai.eval("count()"); int count2 = chai.eval("var i = 0; { var t = Test(); } return i;"); - int count3 = chai.eval("var i = 0; { var t = Test(); i = count(); } return i;"); + int count3 = chai.eval("i = 0; { var t = Test(); i = count(); } return i;"); - int count4 = chai.eval("var i = 0; { var t = Test(); { var t2 = Test(); i = count(); } } return i;"); + int count4 = chai.eval("i = 0; { var t = Test(); { var t2 = Test(); i = count(); } } return i;"); - int count5 = chai.eval("var i = 0; { var t = Test(); { var t2 = Test(); } i = count(); } return i;"); + int count5 = chai.eval("i = 0; { var t = Test(); { var t2 = Test(); } i = count(); } return i;"); - int count6 = chai.eval("var i = 0; { var t = Test(); { var t2 = Test(); } } i = count(); return i;"); + int count6 = chai.eval("i = 0; { var t = Test(); { var t2 = Test(); } } i = count(); return i;"); if (count == 0 && count2 == 0 diff --git a/unittests/operator_scoping.chai b/unittests/operator_scoping.chai new file mode 100644 index 0000000..5718bd5 --- /dev/null +++ b/unittests/operator_scoping.chai @@ -0,0 +1,8 @@ +load_module("reflection") + +try { + eval("def `+`(x, y) \n { \n print(i); \n } \n \n var i = 10; \n \"1\" + 1;\n") + assert_false(true); // we should never get here +} catch (e) { + assert_equal("Error: \"Can not find object: i\" ", e.what()); +} diff --git a/unittests/pass_by_reference.chai b/unittests/pass_by_reference.chai new file mode 100644 index 0000000..89c7760 --- /dev/null +++ b/unittests/pass_by_reference.chai @@ -0,0 +1,19 @@ +def f(x) { x+= 2; } + +var i = 1; + +assert_equal(i, 1); + +f(i); + +assert_equal(i, 3); + +def g(x) { x+= " World"; } + +var s = "Hello"; + +assert_equal(s, "Hello"); + +g(s); + +assert_equal(s, "Hello World"); diff --git a/unittests/pointer_passed_to_constructor.chai b/unittests/pointer_passed_to_constructor.chai new file mode 100644 index 0000000..6495ee3 --- /dev/null +++ b/unittests/pointer_passed_to_constructor.chai @@ -0,0 +1,8 @@ +load_module("test_module") + +var i = 1; +var t0 = TestBaseType(i); + +var t1 = TestBaseType(get_new_int()) + + diff --git a/unittests/short_comparison_test.cpp b/unittests/short_comparison_test.cpp new file mode 100644 index 0000000..a57e39c --- /dev/null +++ b/unittests/short_comparison_test.cpp @@ -0,0 +1,29 @@ +#include + +class Test { + public: + Test() : value_(5) {} + + short get_value() { return value_; } + + short value_; +}; + +int main() +{ + + chaiscript::ChaiScript chai; + chai.add(chaiscript::user_type(), "Test"); + chai.add(chaiscript::constructor(), "Test"); + + chai.add(chaiscript::fun(&Test::get_value), "get_value"); + + chai.eval("var t := Test();"); + + if (chai.eval("t.get_value() == 5")) + { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} diff --git a/unittests/switch_break.chai b/unittests/switch_break.chai new file mode 100644 index 0000000..8d36275 --- /dev/null +++ b/unittests/switch_break.chai @@ -0,0 +1,22 @@ +var total = 0; + +switch(2) { + case (1) { + total += 1; + break; + } + case (2) { + total += 2; + break; + } + case (3) { + total += 4; + break; + } + case (4) { + total += 8; + break; + } +} + +assert_equal(total, 2) diff --git a/unittests/switch_default.chai b/unittests/switch_default.chai new file mode 100644 index 0000000..8c48aa5 --- /dev/null +++ b/unittests/switch_default.chai @@ -0,0 +1,18 @@ +var total = 0; + +switch(2) { + case (1) { + total += 1; + } + case (3) { + total += 4; + } + case (4) { + total += 8; + } + default { + total += 16; + } +} + +assert_equal(total, 16) diff --git a/unittests/switch_empty.chai b/unittests/switch_empty.chai new file mode 100644 index 0000000..8d3a166 --- /dev/null +++ b/unittests/switch_empty.chai @@ -0,0 +1,4 @@ +switch(true) { } + +// We just have to get here without error for success +assert_equal(true, true); diff --git a/unittests/switch_fallthru.chai b/unittests/switch_fallthru.chai new file mode 100644 index 0000000..627b649 --- /dev/null +++ b/unittests/switch_fallthru.chai @@ -0,0 +1,18 @@ +var total = 0; + +switch(2) { + case (1) { + total += 1; + } + case (2) { + total += 2; + } + case (3) { + total += 4; + } + case (4) { + total += 8; + } +} + +assert_equal(total, 14); diff --git a/unittests/switch_fallthru_and_break.chai b/unittests/switch_fallthru_and_break.chai new file mode 100644 index 0000000..3c93071 --- /dev/null +++ b/unittests/switch_fallthru_and_break.chai @@ -0,0 +1,19 @@ +var total = 0; + +switch(2) { + case (1) { + total += 1; + } + case (2) { + total += 2; + } + case (3) { + total += 4; + break; + } + case (4) { + total += 8; + } +} + +assert_equal(total, 6) diff --git a/unittests/system_introspection.chai b/unittests/system_introspection.chai new file mode 100644 index 0000000..fc7350e --- /dev/null +++ b/unittests/system_introspection.chai @@ -0,0 +1,18 @@ + +var funcs = get_functions(); + +assert_true(funcs.size() > 0); +assert_true(funcs["to_string"].get_type_info().bare_equal(Function_type)); + + +var i = 1; +var objs = get_objects(); + +assert_true(objs.size() > 0); +assert_true(objs["i"].get_type_info().bare_equal(int_type)); +assert_true(objs.count("j") == 0); + + + + + diff --git a/unittests/temporary_lifetime.chai b/unittests/temporary_lifetime.chai new file mode 100644 index 0000000..ab6933e --- /dev/null +++ b/unittests/temporary_lifetime.chai @@ -0,0 +1,3 @@ +for_each(range([1..10]), fun(x) {print(x);} ); + +assert_true(true); diff --git a/unittests/ternary_condition.chai b/unittests/ternary_condition.chai new file mode 100644 index 0000000..7114048 --- /dev/null +++ b/unittests/ternary_condition.chai @@ -0,0 +1,2 @@ +var x = 4; +assert_equal(x < 3 ? 4 < 1 : 5 > 3, true); diff --git a/unittests/unit_test.inc b/unittests/unit_test.inc index d746e7b..681c519 100644 --- a/unittests/unit_test.inc +++ b/unittests/unit_test.inc @@ -5,7 +5,7 @@ def assert_equal(x, y) // Passes } else { // Fails - print("assert_equal failure: got " + to_string(y) + " expected " + to_string(x)); + print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'"); exit(-1); } } @@ -23,7 +23,7 @@ def assert_true(f) { if (!f) { - print("assert_false failure"); + print("assert_true failure"); exit(-1); } } diff --git a/unittests/utility_test.cpp b/unittests/utility_test.cpp index bcd3240..43e9017 100644 --- a/unittests/utility_test.cpp +++ b/unittests/utility_test.cpp @@ -30,8 +30,8 @@ int main() chaiscript::ChaiScript chai; chai.add(m); if (chai.eval("var t = Test(); t.function2(); ") == "Function2" - && chai.eval("var t = Test(); t.functionOverload(1); ") == "int" - && chai.eval("var t = Test(); t.functionOverload(1.1); ") == "double") + && chai.eval("var t2 = Test(); t2.functionOverload(1); ") == "int" + && chai.eval("var t3 = Test(); t3.functionOverload(1.1); ") == "double") { chai.eval("t = Test();"); return EXIT_SUCCESS; diff --git a/unittests/variable_redefinition.chai b/unittests/variable_redefinition.chai new file mode 100644 index 0000000..b1600e6 --- /dev/null +++ b/unittests/variable_redefinition.chai @@ -0,0 +1,2 @@ +assert_throws("Variable already defined", fun() { var y = 10; var y = 20; }) + diff --git a/unittests/vector_inplace_init.chai b/unittests/vector_inplace_init.chai index f16c15b..e7ae124 100644 --- a/unittests/vector_inplace_init.chai +++ b/unittests/vector_inplace_init.chai @@ -1,2 +1,14 @@ var x = [1, 2, 3] assert_equal(3, x.size()) + + + +// Make sure vector elements are copied into place for consistency with +// map inplace construction +var i = 1; +var y = [i]; + +assert_equal(1, y[0]); +i = 3; +assert_equal(3, i); +assert_equal(1, y[0]);