diff --git a/.travis.yml b/.travis.yml index 8c9dcd0..dc3b2ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,14 @@ notifications: - jason@emptycrate.com on_success: always on_failure: always + webhooks: + urls: + - https://webhooks.gitter.im/e/4be9a2720eaa1bb2a6c9 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false env: global: secure: eiaR6pXiiEpyB8+LLQ1NvZdl0Yylru1BLy9lMoHl+IpUNGGQGywmW/2WAn77rFfmR1OPA2qWQLfgPwgK0HxUA9HHlot9tre5QhiN2Lw8NOT8tCZ6tTm2+QntDBjBGJyal/knRvQkn/6qs6GxlXRerz4ArnnuPL1vESt3zwB0YtU= + + diff --git a/CMakeLists.txt b/CMakeLists.txt index f942eb0..2e325f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,16 @@ cmake_minimum_required(VERSION 2.8) +if(NOT ${CMAKE_VERSION} VERSION_LESS "3.1") + cmake_policy(SET CMP0054 NEW) +endif() + +IF(BIICODE) + INIT_BIICODE_BLOCK() + ADD_BIICODE_TARGETS() +ELSE() + # Your regular CMakeLists configuration here + + project(chaiscript) # MINGW does not yet support C++11's concurrency features @@ -81,7 +92,7 @@ set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/readme.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt") set(CPACK_PACKAGE_VERSION_MAJOR 5) -set(CPACK_PACKAGE_VERSION_MINOR 6) +set(CPACK_PACKAGE_VERSION_MINOR 7) set(CPACK_PACKAGE_VERSION_PATCH 0) set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval") @@ -409,3 +420,6 @@ configure_file(contrib/pkgconfig/chaiscript.pc.in lib/pkgconfig/chaiscript.pc @O install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc" DESTINATION lib/pkgconfig) + +ENDIF() + diff --git a/biicode.conf b/biicode.conf new file mode 100644 index 0000000..be89bcf --- /dev/null +++ b/biicode.conf @@ -0,0 +1,5 @@ +[paths] + include + +[parent] + ChaiScript/ChaiScript: 0 \ No newline at end of file diff --git a/cheatsheet.md b/cheatsheet.md new file mode 100644 index 0000000..b8be1a9 --- /dev/null +++ b/cheatsheet.md @@ -0,0 +1,261 @@ +# Initializing ChaiScript + +``` +chaiscript::ChaiScript chai; // loads stdlib from loadable module on file system +chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); // compiles in stdlib +``` + +# Adding Things To The Engine + +## Adding a Function / Method / Member + +### General + +``` +chai.add(chaiscript::fun(&function_name), "function_name"); +chai.add(chaiscript::fun(&Class::method_name), "method_name"); +chai.add(chaiscript::fun(&Class::member_name), "member_name"); +``` + +### With Overloads + +#### Preferred + +``` +chai.add(chaiscript::fun(&function_with_overloads), "function_name"); +``` + +#### Alternative + +``` +chai.add(chaiscript::fun(std::static_cast(&function_with_overloads)), "function_name"); +``` + +### Lambda + +``` +chai.add( + chaiscript::fun( + [](bool type) { + if (type) { return "x"; } + else { return "y"; } + }), "function_name"); +``` + +### Constructors + +``` +chai.add(chaiscript::constructor(), "MyType"); +chai.add(chaiscript::constructor(), "MyType"); +``` + +## Adding Types + +It's not strictly necessary to add types, but it helps with many things. Cloning, better errors, etc. + +``` +chai.add(chaiscript::user_type, "MyClass"); +``` + +## Adding Objects + +``` +chai.add(chaiscript::var(somevar), "somevar"); // copied in +chai.add(chaiscript::var(std::ref(somevar), "somevar"); // by reference, shared between C++ and chai +auto shareddouble = std::make_shared(4.3); +chai.add(chaiscript::var(shareddouble), "shareddouble"); // by shared_ptr, shared between c++ and chai +chai.add(chaiscript::const_var(somevar), "somevar"); // copied in and made const +chai.add_global_const(chaiscript::const_var(somevar), "somevar"); // global const. Throws if value is non-const +chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const +``` + +# Executing Script + +## General + +``` +chai.eval("print(\"Hello World\")"); +chai.eval(R"(print("Hello World"))"); +``` + +## Unboxing Return Values + +### Prefered + +``` +chai.eval("5.3 + 2.1"); // returns 7.4 as a C++ double +``` + +### Alternative + +``` +auto v = chai.eval("5.3 + 2.1"); +chai.boxed_cast(v); // extracts double value from boxed_value and applies known conversions +chaiscript::boxed_cast(v); // free function version, does not know about conversions +``` + +### Converting Between Algebraic Types + +``` +chaiscript::Boxed_Number(chai.eval("5.3 + 2.1")).get_as(); // works with any number type +// which is equivalent to, but much more automatic than: +static_cast(chai.eval("5.3+2.1")); // this version only works if we know that it's a double +``` + +## Sharing Values + +``` +double &d = chai.eval("var i = 5.2; i"); // d is now a reference to i in the script +std::shared_ptr d = chai.eval("var i = 5.2; i"); // same result but reference counted + +d = 3; +chai.eval("print(i)"); // prints 3 +``` + +## Catching Eval Errors + +``` +try { + chai.eval("2.3 + \"String\""); +} catch (const chaiscript::exception::eval_error &e) { + std::cout << "Error\n" << e.pretty_print() << '\n'; +} +``` + +## Catching Errors Thrown From Script + +``` +try { + chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); +} catch (const double e) { +} catch (int) { +} catch (float) { +} catch (const std::string &) { +} catch (const std::exception &e) { + // This is the one what will be called in the specific throw() above +} +``` + +## Sharing Functions + + +``` +auto p = chai.eval>("to_string"); +p(5); // calls chaiscript's 'to_string' function, returning std::string("5") +``` + +Note: backtick treats operators as normal functions + +``` +auto p = chai.eval>(`+`); +p(5, 6); // calls chaiscript's '+' function, returning 11 +``` + +``` +auto p = chai.eval>(fun(x,y) { to_string(x) + to_string(y); }); +p(3,4.2); // evaluates the lambda function, returning the string "34.2" to C++ +``` + +# Language Reference + +## Variables + +``` +var i; // uninitialized variable, can take any value on first assignment; +auto j; // equiv to var + +var k = 5; // initialized to 5 (integer) +var l := k; // reference to k +auto &m = k; // reference to k +``` + +## Built in Types + +``` +var v = [1,2,3u,4ll,"16", `+`]; // creates vector of heterogenous values +var m = ["a":1, "b":2]; // map of string:value pairs +``` + +## Functions + +### General + +``` +def myfun(x, y) { x + y; } // last statement in body is the return value +def myfun(x, y) { return x + y; } // equiv +``` + +### Optionally Typed + +``` +def myfun(x, int y) { x + y; } // requires y to be an int +``` + +### With Guards + +``` +def myfun(x, int y) : y > 5 { x - y; } // only called if y > 5 +``` + +### Methods + +Methods and functions are mostly equivalent + +``` +def string::add(int y) { this + to_string(y); } +def add(string s, int y) { s + to_string(y); } //equiv functionality + +// calling new function/method +"a".add(1); // returns a1 +add("a", 1); // returns a1, either calling syntax works with either def above +``` + +### Lambdas + +``` +var l = fun(x) { x * 15; } +l(2) // returns 30 + +var a = 13 +var m = fun[a](x) { x * a; } +m(3); // a was captured (by reference), returns 39 + +var n = bind(fun(x,y) { x * y; }, _, 10); +n(2); // returns 20 +``` + + + +## ChaiScript Defined Types + +Define a type called "MyType" with one member value "a" and a getter + +### Preferred + +``` +class MyType { + var value; + def MyType() { this.value = "a"; } + def get_value() { "Value Is: " + this.value; } +}; +``` + +### Alternative + +``` +attr MyType::value; +def MyType::MyType() { this.value = "a"; } +def MyType::get_value() { "Value Is: " + this.value; } +``` + +### Using + +``` +var m = MyType(); // calls constructor +print(m.get_value()); // prints "Value Is: a" +print(get_value(m)); // prints "Value Is: a" +``` + + + + diff --git a/cmake/CheckCXX11Features.cmake b/cmake/CheckCXX11Features.cmake index fc1243f..27b823d 100644 --- a/cmake/CheckCXX11Features.cmake +++ b/cmake/CheckCXX11Features.cmake @@ -1,103 +1,103 @@ -# Checks for C++11 features -# CXX11_FEATURE_LIST - a list containing all supported features -# HAS_CXX11_AUTO - auto keyword -# HAS_CXX11_NULLPTR - nullptr -# HAS_CXX11_LAMBDA - lambdas -# HAS_CXX11_STATIC_ASSERT - static_assert() -# HAS_CXX11_RVALUE_REFERENCES - rvalue references -# HAS_CXX11_DECLTYPE - decltype keyword -# HAS_CXX11_CSTDINT_H - cstdint header -# HAS_CXX11_LONG_LONG - long long signed & unsigned types -# HAS_CXX11_VARIADIC_TEMPLATES - variadic templates -# HAS_CXX11_CONSTEXPR - constexpr keyword -# HAS_CXX11_SIZEOF_MEMBER - sizeof() non-static members -# HAS_CXX11_FUNC - __func__ preprocessor constant -# -# Original script by Rolf Eike Beer -# Modifications by Andreas Weis -# -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3) - -SET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) -IF(CMAKE_COMPILER_IS_GNUCXX) - SET(CMAKE_CXX_FLAGS "-std=c++0x") -endif() - -MACRO(CXX11_CHECK_FEATURE FEATURE_NAME FEATURE_NUMBER RESULT_VAR) - IF (NOT DEFINED ${RESULT_VAR}) - SET(_bindir "${CMAKE_CURRENT_BINARY_DIR}/cxx11/cxx11_${FEATURE_NAME}") - - IF (${FEATURE_NUMBER}) - SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}-N${FEATURE_NUMBER}) - SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})") - ELSE (${FEATURE_NUMBER}) - SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}) - SET(_LOG_NAME "\"${FEATURE_NAME}\"") - ENDIF (${FEATURE_NUMBER}) - MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME}") - - SET(_SRCFILE "${_SRCFILE_BASE}.cpp") - SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp") - SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp") - - IF (CROSS_COMPILING) - try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}") - IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}") - ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - ELSE (CROSS_COMPILING) - try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR - "${_bindir}" "${_SRCFILE}") - IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) - SET(${RESULT_VAR} TRUE) - ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) - SET(${RESULT_VAR} FALSE) - ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) - IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR - "${_bindir}_fail" "${_SRCFILE_FAIL}") - IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) - SET(${RESULT_VAR} TRUE) - ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) - SET(${RESULT_VAR} FALSE) - ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) - ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - ENDIF (CROSS_COMPILING) - IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) - try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}") - IF (_TMP_RESULT) - SET(${RESULT_VAR} FALSE) - ELSE (_TMP_RESULT) - SET(${RESULT_VAR} TRUE) - ENDIF (_TMP_RESULT) - ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) - - IF (${RESULT_VAR}) - MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- works") - LIST(APPEND CXX11_FEATURE_LIST ${RESULT_VAR}) - ELSE (${RESULT_VAR}) - MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- not supported") - ENDIF (${RESULT_VAR}) - SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}") - ENDIF (NOT DEFINED ${RESULT_VAR}) -ENDMACRO(CXX11_CHECK_FEATURE) - -CXX11_CHECK_FEATURE("auto" 2546 HAS_CXX11_AUTO) -CXX11_CHECK_FEATURE("nullptr" 2431 HAS_CXX11_NULLPTR) -CXX11_CHECK_FEATURE("lambda" 2927 HAS_CXX11_LAMBDA) -CXX11_CHECK_FEATURE("static_assert" 1720 HAS_CXX11_STATIC_ASSERT) -CXX11_CHECK_FEATURE("rvalue_references" 2118 HAS_CXX11_RVALUE_REFERENCES) -CXX11_CHECK_FEATURE("decltype" 2343 HAS_CXX11_DECLTYPE) -CXX11_CHECK_FEATURE("cstdint" "" HAS_CXX11_CSTDINT_H) -CXX11_CHECK_FEATURE("long_long" 1811 HAS_CXX11_LONG_LONG) -CXX11_CHECK_FEATURE("variadic_templates" 2555 HAS_CXX11_VARIADIC_TEMPLATES) -CXX11_CHECK_FEATURE("constexpr" 2235 HAS_CXX11_CONSTEXPR) -CXX11_CHECK_FEATURE("sizeof_member" 2253 HAS_CXX11_SIZEOF_MEMBER) -CXX11_CHECK_FEATURE("__func__" 2340 HAS_CXX11_FUNC) - -SET(CXX11_FEATURE_LIST ${CXX11_FEATURE_LIST} CACHE STRING "C++11 feature support list") -MARK_AS_ADVANCED(FORCE CXX11_FEATURE_LIST) - -SET(CMAKE_CXX_FLAGS ${CHECK_CXX11_OLD_CMAKE_CXX_FLAGS}) -UNSET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS) - +# Checks for C++11 features +# CXX11_FEATURE_LIST - a list containing all supported features +# HAS_CXX11_AUTO - auto keyword +# HAS_CXX11_NULLPTR - nullptr +# HAS_CXX11_LAMBDA - lambdas +# HAS_CXX11_STATIC_ASSERT - static_assert() +# HAS_CXX11_RVALUE_REFERENCES - rvalue references +# HAS_CXX11_DECLTYPE - decltype keyword +# HAS_CXX11_CSTDINT_H - cstdint header +# HAS_CXX11_LONG_LONG - long long signed & unsigned types +# HAS_CXX11_VARIADIC_TEMPLATES - variadic templates +# HAS_CXX11_CONSTEXPR - constexpr keyword +# HAS_CXX11_SIZEOF_MEMBER - sizeof() non-static members +# HAS_CXX11_FUNC - __func__ preprocessor constant +# +# Original script by Rolf Eike Beer +# Modifications by Andreas Weis +# +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3) + +SET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +IF(CMAKE_COMPILER_IS_GNUCXX) + SET(CMAKE_CXX_FLAGS "-std=c++0x") +endif() + +MACRO(CXX11_CHECK_FEATURE FEATURE_NAME FEATURE_NUMBER RESULT_VAR) + IF (NOT DEFINED ${RESULT_VAR}) + SET(_bindir "${CMAKE_CURRENT_BINARY_DIR}/cxx11/cxx11_${FEATURE_NAME}") + + IF (${FEATURE_NUMBER}) + SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}-N${FEATURE_NUMBER}) + SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})") + ELSE (${FEATURE_NUMBER}) + SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}) + SET(_LOG_NAME "\"${FEATURE_NAME}\"") + ENDIF (${FEATURE_NUMBER}) + MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME}") + + SET(_SRCFILE "${_SRCFILE_BASE}.cpp") + SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp") + SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp") + + IF (CROSS_COMPILING) + try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}") + IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}") + ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + ELSE (CROSS_COMPILING) + try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR + "${_bindir}" "${_SRCFILE}") + IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) + SET(${RESULT_VAR} TRUE) + ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) + SET(${RESULT_VAR} FALSE) + ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) + IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR + "${_bindir}_fail" "${_SRCFILE_FAIL}") + IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) + SET(${RESULT_VAR} TRUE) + ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) + SET(${RESULT_VAR} FALSE) + ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) + ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + ENDIF (CROSS_COMPILING) + IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) + try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}") + IF (_TMP_RESULT) + SET(${RESULT_VAR} FALSE) + ELSE (_TMP_RESULT) + SET(${RESULT_VAR} TRUE) + ENDIF (_TMP_RESULT) + ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) + + IF (${RESULT_VAR}) + MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- works") + LIST(APPEND CXX11_FEATURE_LIST ${RESULT_VAR}) + ELSE (${RESULT_VAR}) + MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- not supported") + ENDIF (${RESULT_VAR}) + SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}") + ENDIF (NOT DEFINED ${RESULT_VAR}) +ENDMACRO(CXX11_CHECK_FEATURE) + +CXX11_CHECK_FEATURE("auto" 2546 HAS_CXX11_AUTO) +CXX11_CHECK_FEATURE("nullptr" 2431 HAS_CXX11_NULLPTR) +CXX11_CHECK_FEATURE("lambda" 2927 HAS_CXX11_LAMBDA) +CXX11_CHECK_FEATURE("static_assert" 1720 HAS_CXX11_STATIC_ASSERT) +CXX11_CHECK_FEATURE("rvalue_references" 2118 HAS_CXX11_RVALUE_REFERENCES) +CXX11_CHECK_FEATURE("decltype" 2343 HAS_CXX11_DECLTYPE) +CXX11_CHECK_FEATURE("cstdint" "" HAS_CXX11_CSTDINT_H) +CXX11_CHECK_FEATURE("long_long" 1811 HAS_CXX11_LONG_LONG) +CXX11_CHECK_FEATURE("variadic_templates" 2555 HAS_CXX11_VARIADIC_TEMPLATES) +CXX11_CHECK_FEATURE("constexpr" 2235 HAS_CXX11_CONSTEXPR) +CXX11_CHECK_FEATURE("sizeof_member" 2253 HAS_CXX11_SIZEOF_MEMBER) +CXX11_CHECK_FEATURE("__func__" 2340 HAS_CXX11_FUNC) + +SET(CXX11_FEATURE_LIST ${CXX11_FEATURE_LIST} CACHE STRING "C++11 feature support list") +MARK_AS_ADVANCED(FORCE CXX11_FEATURE_LIST) + +SET(CMAKE_CXX_FLAGS ${CHECK_CXX11_OLD_CMAKE_CXX_FLAGS}) +UNSET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS) + diff --git a/cmake/c++11-test-__func__-N2340.cpp b/cmake/c++11-test-__func__-N2340.cpp index c10dd18..d961df8 100644 --- a/cmake/c++11-test-__func__-N2340.cpp +++ b/cmake/c++11-test-__func__-N2340.cpp @@ -1,8 +1,8 @@ -#include - -int main() -{ - if (!__func__) { return 1; } - if(std::strlen(__func__) <= 0) { return 1; } - return 0; -} +#include + +int main() +{ + if (!__func__) { return 1; } + if(std::strlen(__func__) <= 0) { return 1; } + return 0; +} diff --git a/cmake/c++11-test-auto-N2546.cpp b/cmake/c++11-test-auto-N2546.cpp index dbff414..948648e 100644 --- a/cmake/c++11-test-auto-N2546.cpp +++ b/cmake/c++11-test-auto-N2546.cpp @@ -1,12 +1,12 @@ - -int main() -{ - auto i = 5; - auto f = 3.14159f; - auto d = 3.14159; - bool ret = ( - (sizeof(f) < sizeof(d)) && - (sizeof(i) == sizeof(int)) - ); - return ret ? 0 : 1; -} + +int main() +{ + auto i = 5; + auto f = 3.14159f; + auto d = 3.14159; + bool ret = ( + (sizeof(f) < sizeof(d)) && + (sizeof(i) == sizeof(int)) + ); + return ret ? 0 : 1; +} diff --git a/cmake/c++11-test-constexpr-N2235.cpp b/cmake/c++11-test-constexpr-N2235.cpp index 9f969e4..ed62451 100644 --- a/cmake/c++11-test-constexpr-N2235.cpp +++ b/cmake/c++11-test-constexpr-N2235.cpp @@ -1,19 +1,19 @@ -constexpr int square(int x) -{ - return x*x; -} - -constexpr int the_answer() -{ - return 42; -} - -int main() -{ - int test_arr[square(3)]; - bool ret = ( - (square(the_answer()) == 1764) && - (sizeof(test_arr)/sizeof(test_arr[0]) == 9) - ); - return ret ? 0 : 1; -} +constexpr int square(int x) +{ + return x*x; +} + +constexpr int the_answer() +{ + return 42; +} + +int main() +{ + int test_arr[square(3)]; + bool ret = ( + (square(the_answer()) == 1764) && + (sizeof(test_arr)/sizeof(test_arr[0]) == 9) + ); + return ret ? 0 : 1; +} diff --git a/cmake/c++11-test-cstdint.cpp b/cmake/c++11-test-cstdint.cpp index 58d4381..be2878f 100644 --- a/cmake/c++11-test-cstdint.cpp +++ b/cmake/c++11-test-cstdint.cpp @@ -1,10 +1,10 @@ -#include -int main() -{ - bool test = - (sizeof(int8_t) == 1) && - (sizeof(int16_t) == 2) && - (sizeof(int32_t) == 4) && - (sizeof(int64_t) == 8); - return test ? 0 : 1; -} +#include +int main() +{ + bool test = + (sizeof(int8_t) == 1) && + (sizeof(int16_t) == 2) && + (sizeof(int32_t) == 4) && + (sizeof(int64_t) == 8); + return test ? 0 : 1; +} diff --git a/cmake/c++11-test-decltype-N2343.cpp b/cmake/c++11-test-decltype-N2343.cpp index d023885..843f83a 100644 --- a/cmake/c++11-test-decltype-N2343.cpp +++ b/cmake/c++11-test-decltype-N2343.cpp @@ -1,11 +1,11 @@ - -bool check_size(int i) -{ - return sizeof(int) == sizeof(decltype(i)); -} - -int main() -{ - bool ret = check_size(42); - return ret ? 0 : 1; -} + +bool check_size(int i) +{ + return sizeof(int) == sizeof(decltype(i)); +} + +int main() +{ + bool ret = check_size(42); + return ret ? 0 : 1; +} diff --git a/cmake/c++11-test-lambda-N2927.cpp b/cmake/c++11-test-lambda-N2927.cpp index b86ad17..4c33ed5 100644 --- a/cmake/c++11-test-lambda-N2927.cpp +++ b/cmake/c++11-test-lambda-N2927.cpp @@ -1,5 +1,5 @@ -int main() -{ - int ret = 0; - return ([&ret]() -> int { return ret; })(); -} +int main() +{ + int ret = 0; + return ([&ret]() -> int { return ret; })(); +} diff --git a/cmake/c++11-test-long_long-N1811.cpp b/cmake/c++11-test-long_long-N1811.cpp index 2ae6988..0911127 100644 --- a/cmake/c++11-test-long_long-N1811.cpp +++ b/cmake/c++11-test-long_long-N1811.cpp @@ -1,7 +1,7 @@ -int main(void) -{ - long long l; - unsigned long long ul; - - return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1; -} +int main(void) +{ + long long l; + unsigned long long ul; + + return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1; +} diff --git a/cmake/c++11-test-nullptr-N2431.cpp b/cmake/c++11-test-nullptr-N2431.cpp index 6c5ae66..c78fac4 100644 --- a/cmake/c++11-test-nullptr-N2431.cpp +++ b/cmake/c++11-test-nullptr-N2431.cpp @@ -1,5 +1,5 @@ -int main() -{ - int* test = nullptr; - return test ? 1 : 0; -} +int main() +{ + int* test = nullptr; + return test ? 1 : 0; +} diff --git a/cmake/c++11-test-nullptr-N2431_fail_compile.cpp b/cmake/c++11-test-nullptr-N2431_fail_compile.cpp index 5747f1b..7ab77a2 100644 --- a/cmake/c++11-test-nullptr-N2431_fail_compile.cpp +++ b/cmake/c++11-test-nullptr-N2431_fail_compile.cpp @@ -1,5 +1,5 @@ -int main() -{ - int i = nullptr; - return 1; -} +int main() +{ + int i = nullptr; + return 1; +} diff --git a/cmake/c++11-test-rvalue_references-N2118.cpp b/cmake/c++11-test-rvalue_references-N2118.cpp index ef4e421..75fb555 100644 --- a/cmake/c++11-test-rvalue_references-N2118.cpp +++ b/cmake/c++11-test-rvalue_references-N2118.cpp @@ -1,15 +1,15 @@ -int foo(int& lvalue) -{ - return 123; -} - -int foo(int&& rvalue) -{ - return 321; -} - -int main() -{ - int i = 42; - return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1; -} +int foo(int& lvalue) +{ + return 123; +} + +int foo(int&& rvalue) +{ + return 321; +} + +int main() +{ + int i = 42; + return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1; +} diff --git a/cmake/c++11-test-sizeof_member-N2253.cpp b/cmake/c++11-test-sizeof_member-N2253.cpp index 3049ed1..a55fc09 100644 --- a/cmake/c++11-test-sizeof_member-N2253.cpp +++ b/cmake/c++11-test-sizeof_member-N2253.cpp @@ -1,14 +1,14 @@ -struct foo { - char bar; - int baz; -}; - -int main(void) -{ - bool ret = ( - (sizeof(foo::bar) == 1) && - (sizeof(foo::baz) >= sizeof(foo::bar)) && - (sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz)) - ); - return ret ? 0 : 1; -} +struct foo { + char bar; + int baz; +}; + +int main(void) +{ + bool ret = ( + (sizeof(foo::bar) == 1) && + (sizeof(foo::baz) >= sizeof(foo::bar)) && + (sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz)) + ); + return ret ? 0 : 1; +} diff --git a/cmake/c++11-test-static_assert-N1720.cpp b/cmake/c++11-test-static_assert-N1720.cpp index eae3c9a..c3d74ca 100644 --- a/cmake/c++11-test-static_assert-N1720.cpp +++ b/cmake/c++11-test-static_assert-N1720.cpp @@ -1,5 +1,5 @@ -int main() -{ - static_assert(0 < 1, "your ordering of integers is screwed"); - return 0; -} +int main() +{ + static_assert(0 < 1, "your ordering of integers is screwed"); + return 0; +} diff --git a/cmake/c++11-test-static_assert-N1720_fail_compile.cpp b/cmake/c++11-test-static_assert-N1720_fail_compile.cpp index d97b679..4cb1183 100644 --- a/cmake/c++11-test-static_assert-N1720_fail_compile.cpp +++ b/cmake/c++11-test-static_assert-N1720_fail_compile.cpp @@ -1,5 +1,5 @@ -int main() -{ - static_assert(1 < 0, "this should fail"); - return 0; -} +int main() +{ + static_assert(1 < 0, "this should fail"); + return 0; +} diff --git a/cmake/c++11-test-variadic_templates-N2555.cpp b/cmake/c++11-test-variadic_templates-N2555.cpp index 79fae84..4518e88 100644 --- a/cmake/c++11-test-variadic_templates-N2555.cpp +++ b/cmake/c++11-test-variadic_templates-N2555.cpp @@ -1,23 +1,23 @@ -int Accumulate() -{ - return 0; -} - -template -int Accumulate(T v, Ts... vs) -{ - return v + Accumulate(vs...); -} - -template -int CountElements() -{ - return sizeof...(Is); -} - -int main() -{ - int acc = Accumulate(1, 2, 3, 4, -5); - int count = CountElements<1,2,3,4,5>(); - return ((acc == 5) && (count == 5)) ? 0 : 1; -} +int Accumulate() +{ + return 0; +} + +template +int Accumulate(T v, Ts... vs) +{ + return v + Accumulate(vs...); +} + +template +int CountElements() +{ + return sizeof...(Is); +} + +int main() +{ + int acc = Accumulate(1, 2, 3, 4, -5); + int count = CountElements<1,2,3,4,5>(); + return ((acc == 5) && (count == 5)) ? 0 : 1; +} diff --git a/contrib/codeanalysis/type_conversions.chai b/contrib/codeanalysis/type_conversions.chai new file mode 100644 index 0000000..298bddf --- /dev/null +++ b/contrib/codeanalysis/type_conversions.chai @@ -0,0 +1,22 @@ +load_module("test_module") + +auto t := TestBaseType(); + +// This uses the TestBaseType to Type2 user type +// conversion which was added in the module and then calls +// "get_val()" which exists on the Type2 type +//assert_equal(t.get_val(), 10); +//print("Made it past test 1"); + +var t2 := Type2(t); + +//dump_system(); + +for (var i = 0; i < 50000; ++i) { + var str = string(get_str(t2)); + size(get_str(t2)); + t2.get_str().size(); + t.get_str().size(); +} + + diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 5fbc475..811b1bd 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -10,6 +10,9 @@ #ifdef _MSC_VER #define CHAISCRIPT_MSVC _MSC_VER #define CHAISCRIPT_HAS_DECLSPEC +#if _MSC_VER <= 1800 +#define CHAISCRIPT_MSVC_12 +#endif #endif #if defined(_WIN32) || defined(__CYGWIN__) @@ -45,7 +48,7 @@ namespace chaiscript { static const int version_major = 5; - static const int version_minor = 6; + static const int version_minor = 7; static const int version_patch = 0; } diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 4568116..15b4a64 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -158,7 +158,7 @@ namespace chaiscript if (itr != m_instances.end()) { return itr->second; } - std::shared_ptr new_instance(new T()); + std::shared_ptr new_instance(std::make_shared()); m_instances.insert(std::make_pair(std::this_thread::get_id(), new_instance)); diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index f6efe6e..d1330c9 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -303,7 +303,7 @@ namespace chaiscript ModulePtr sequence_type(const std::string &/*type*/, ModulePtr m = ModulePtr(new Module())) { m->add(fun(&detail::insert_at), - [](){ + []()->std::string{ if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { return "insert_ref_at"; } else { @@ -329,7 +329,7 @@ namespace chaiscript typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &); m->add(fun(static_cast(&ContainerType::push_back)), - [](){ + []()->std::string{ if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { return "push_back_ref"; } else { @@ -357,7 +357,7 @@ namespace chaiscript m->add(fun(static_cast(&ContainerType::front)), "front"); m->add(fun(static_cast(&ContainerType::push_front)), - [](){ + []()->std::string{ if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { return "push_front_ref"; } else { @@ -418,7 +418,7 @@ namespace chaiscript m->add(fun(&detail::insert), "insert"); m->add(fun(&detail::insert_ref), - [](){ + []()->std::string{ if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) { return "insert_ref"; } else { @@ -538,7 +538,7 @@ namespace chaiscript //Special case: add push_back to string (which doesn't support other back_insertion operations m->add(fun(&String::push_back), - [](){ + []()->std::string{ if (typeid(typename String::value_type) == typeid(Boxed_Value)) { return "push_back_ref"; } else { diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index 40b7857..d7ba410 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -74,46 +74,47 @@ namespace chaiscript template typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv, const Type_Conversions *t_conversions = nullptr) { - try { - return detail::Cast_Helper::cast(bv, t_conversions); - } catch (const chaiscript::detail::exception::bad_any_cast &) { + if (!t_conversions || bv.get_type_info().bare_equal(user_type()) || (t_conversions && !t_conversions->convertable_type())) { + try { + return detail::Cast_Helper::cast(bv, t_conversions); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + } + } #ifdef CHAISCRIPT_MSVC - //Thank you MSVC, yes we know that a constant value is being used in the if - // statment in THIS VERSION of the template instantiation + //Thank you MSVC, yes we know that a constant value is being used in the if + // statment in THIS VERSION of the template instantiation #pragma warning(push) #pragma warning(disable : 4127) #endif - if (t_conversions && t_conversions->convertable_type()) - { + if (t_conversions && t_conversions->convertable_type()) + { + try { + // std::cout << "trying an up conversion " << typeid(Type).name() << '\n'; + // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it + // either way, we are not responsible if it doesn't work + return detail::Cast_Helper::cast(t_conversions->boxed_type_conversion(bv), t_conversions); + } catch (...) { try { - // std::cout << "trying an up conversion " << typeid(Type).name() << '\n'; - // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it - // either way, we are not responsible if it doesn't work - return detail::Cast_Helper::cast(t_conversions->boxed_type_conversion(bv), t_conversions); - } catch (...) { - try { - // std::cout << "trying a down conversion " << typeid(Type).name() << '\n'; - // try going the other way - down the inheritance graph - return detail::Cast_Helper::cast(t_conversions->boxed_type_down_conversion(bv), t_conversions); - } catch (const chaiscript::detail::exception::bad_any_cast &) { - throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); - } + // std::cout << "trying a down conversion " << typeid(Type).name() << '\n'; + // try going the other way - down the inheritance graph + return detail::Cast_Helper::cast(t_conversions->boxed_type_down_conversion(bv), t_conversions); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); } - } else { - // If it's not polymorphic, just throw the error, don't waste the time on the - // attempted dynamic_cast - throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); } + } else { + // If it's not polymorphic, just throw the error, don't waste the time on the + // attempted dynamic_cast + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif - - } } } diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index e109e06..9cf53d9 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -641,6 +641,7 @@ namespace chaiscript /// \throws std::range_error if it does not Boxed_Value get_function_object(const std::string &t_name) const { +// std::cout << "Getting function object: " << t_name << '\n'; chaiscript::detail::threading::shared_lock l(m_mutex); const auto &funs = get_function_objects_int(); diff --git a/include/chaiscript/dispatchkit/proxy_constructors.hpp b/include/chaiscript/dispatchkit/proxy_constructors.hpp index f354ae5..2866d97 100644 --- a/include/chaiscript/dispatchkit/proxy_constructors.hpp +++ b/include/chaiscript/dispatchkit/proxy_constructors.hpp @@ -30,7 +30,7 @@ namespace chaiscript Proxy_Function build_constructor_(Class (*)(Params...)) { typedef std::shared_ptr (sig)(Params...); - return Proxy_Function(new Proxy_Function_Impl(std::function(&(constructor_)))); + return Proxy_Function(static_cast(new Proxy_Function_Impl(std::function(&(constructor_))))); } } } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 7c3c0a4..d03c33d 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -180,6 +180,8 @@ namespace chaiscript if (m_arity == 0) { return true; + } else if (m_arity > 1 && m_types.size() > 1) { + return compare_first_type(vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions); } else { return compare_first_type(vals[0], t_conversions); } @@ -233,14 +235,7 @@ namespace chaiscript virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const { - const auto &types = get_param_types(); - - if (types.size() < 2) - { - return true; - } - - return compare_type_to_param(types[1], bv, t_conversions); + return compare_type_to_param(m_types[1], bv, t_conversions); } static bool compare_types(const std::vector &tis, const std::vector &bvs) @@ -779,7 +774,7 @@ namespace chaiscript Boxed_Value dispatch(const Funcs &funcs, const std::vector &plist, const Type_Conversions &t_conversions) { - + //std::cout << "starting dispatch: " << funcs.size() << '\n'; std::multimap ordered_funcs; for (const auto &func : funcs) @@ -808,11 +803,17 @@ namespace chaiscript for (const auto &func : ordered_funcs ) { try { - if (func.second->filter(plist, t_conversions)) + if (func.first == 0 || func.second->filter(plist, t_conversions)) { return (*(func.second))(plist, t_conversions); } } catch (const exception::bad_boxed_cast &) { + //std::cout << "Bad Boxed Cast: " << func.second->get_arity() << '('; + //for (const auto &p : plist) { + // std::cout << p.get_type_info().name() << ','; + //} + //std::cout << ")\n"; + //parameter failed to cast, try again } catch (const exception::arity_error &) { //invalid num params, try again diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index 1ed8ebf..7c4f4a8 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -40,17 +40,25 @@ namespace chaiscript template std::function to_function(Ret (Class::*func)(Args...)) { +#ifdef CHAISCRIPT_MSVC /// \todo this std::mem_fn wrap shouldn't be necessary but type conversions for /// std::function for member function pointers seems to be broken in MSVC return std::function(std::mem_fn(func)); +#else + return std::function(func); +#endif } template std::function to_function(Ret (Class::*func)(Args...) const) { +#ifdef CHAISCRIPT_MSVC /// \todo this std::mem_fn wrap shouldn't be necessary but type conversions for /// std::function for member function pointers seems to be broken in MSVC - return std::function(std::mem_fn(func)); + return std::function(std::mem_fn(func)); +#else + return std::function(func); +#endif } template @@ -61,7 +69,7 @@ namespace chaiscript { /// \todo is it possible to reduce the number of templates generated here? return Proxy_Function( - new Proxy_Function_Impl::Signature>(to_function(t))); + static_cast(new Proxy_Function_Impl::Signature>(to_function(t)))); } }; @@ -118,7 +126,7 @@ namespace chaiscript template Proxy_Function fun(const std::function &f) { - return Proxy_Function(new dispatch::Proxy_Function_Impl(f)); + return Proxy_Function(static_cast(new dispatch::Proxy_Function_Impl(f))); } diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 1a920bd..9195fb2 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -467,14 +467,14 @@ namespace chaiscript } protected: - AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname, + AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname, int t_start_line, int t_start_col, int t_end_line, int t_end_col) : text(std::move(t_ast_node_text)), identifier(t_id), filename(t_fname), start(t_start_line, t_start_col), end(t_end_line, t_end_col) { } - AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname) : + AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname) : text(std::move(t_ast_node_text)), identifier(t_id), filename(t_fname) {} virtual ~AST_Node() {} @@ -495,7 +495,6 @@ namespace chaiscript { namespace detail { - /// Special type for returned values struct Return_Value { Boxed_Value retval; diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 77ce45a..a266bc6 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -276,7 +276,7 @@ namespace chaiscript parser::ChaiScript_Parser parser; if (parser.parse(t_input, t_filename)) { //parser.show_match_stack(); - return parser.ast()->eval(m_engine); + return parser.optimized_ast()->eval(m_engine); } else { return Boxed_Value(); } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 2e622c2..06f2b13 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -47,9 +47,13 @@ namespace chaiscript namespace detail { /// Helper function that will set up the scope around a function call, including handling the named function parameters - static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_NodePtr &t_node, const std::vector &t_param_names, const std::vector &t_vals) { + static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_NodePtr &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map &t_locals=std::map()) { chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + for (const auto &local : t_locals) { + t_ss.add_object(local.first, local.second); + } + for (size_t i = 0; i < t_param_names.size(); ++i) { t_ss.add_object(t_param_names[i], t_vals[i]); } @@ -214,6 +218,7 @@ namespace chaiscript } }; + struct Fun_Call_AST_Node : public AST_Node { public: Fun_Call_AST_Node(const std::string &t_ast_node_text = "", const std::shared_ptr &t_fname=std::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : @@ -222,14 +227,13 @@ namespace chaiscript virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE{ chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + std::vector params; if ((this->children.size() > 1)) { - const AST_Node &first_child(*(this->children[1])); - if (first_child.identifier == AST_Node_Type::Arg_List) { - for (const auto &child : first_child.children) { - params.push_back(child->eval(t_ss)); - } + params.reserve(this->children[1]->children.size()); + for (const auto &child : this->children[1]->children) { + params.push_back(child->eval(t_ss)); } } @@ -286,6 +290,96 @@ namespace chaiscript }; + struct Fun_Lookup_AST_Node : public AST_Node { + public: + Fun_Lookup_AST_Node(const std::string &t_fun_name) + : AST_Node(t_fun_name, 0, std::make_shared("")) + { + } + + virtual ~Fun_Lookup_AST_Node() {} + + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE { + try { + Boxed_Value bv = t_ss.get_object(text); + t_ss.add_object(text, bv); + std::cout << " Saved fun lookup: " << text << '\n'; + return bv; + } catch (...) { + return Boxed_Value(); + } + } + }; + + + + struct Unary_Fun_Call_AST_Node : public AST_Node { + public: + Unary_Fun_Call_AST_Node(const Fun_Call_AST_Node &t_fc) + : AST_Node(t_fc.text, t_fc.identifier, t_fc.filename, t_fc.start.line, t_fc.start.column, t_fc.end.line, t_fc.end.column) + { + this->children = t_fc.children; + } + virtual ~Unary_Fun_Call_AST_Node() {} + + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + + std::vector params{children[1]->children[0]->eval(t_ss)}; + fpp.save_params(params); + + Boxed_Value fn(this->children[0]->eval(t_ss)); + + try { + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + return (*t_ss.boxed_cast(fn))(params, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, t_ss); + } + catch(const exception::bad_boxed_cast &){ + try { + Const_Proxy_Function f = t_ss.boxed_cast(fn); + // handle the case where there is only 1 function to try to call and dispatch fails on it + throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, t_ss); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); + } + } + catch(const exception::arity_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(const exception::guard_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(detail::Return_Value &rv) { + return rv.retval; + } + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + + int count = 0; + for (const auto &child : this->children) { + oss << child->pretty_print(); + + if (count == 0) + { + oss << "("; + } + ++count; + } + + oss << ")"; + + return oss.str(); + } + + }; + + /// Used in the context of in-string ${} evals, so that no new scope is created struct Inplace_Fun_Call_AST_Node : public AST_Node { public: @@ -446,26 +540,28 @@ namespace chaiscript struct Equation_AST_Node : public AST_Node { public: Equation_AST_Node(std::string t_ast_node_text = "", const std::shared_ptr &t_fname=std::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Equation, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Equation, t_fname, t_start_line, t_start_col, t_end_line, t_end_col), + m_oper(Operators::invalid) {} + Operators::Opers m_oper; + virtual ~Equation_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - Boxed_Value rhs = this->children.back()->eval(t_ss); + Boxed_Value rhs = this->children[2]->eval(t_ss); Boxed_Value lhs = this->children[0]->eval(t_ss); - Operators::Opers oper = Operators::to_operator(this->children[1]->text); - if (oper != Operators::invalid && lhs.get_type_info().is_arithmetic() && + if (m_oper != Operators::invalid && lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { try { - return Boxed_Number::do_oper(oper, lhs, rhs); + return Boxed_Number::do_oper(m_oper, lhs, rhs); } catch (const std::exception &) { throw exception::eval_error("Error with unsupported arithmetic assignment operation"); } - } else if (oper == Operators::assign) { + } else if (m_oper == Operators::assign) { try { if (lhs.is_undef()) { if (!this->children.empty() && @@ -701,28 +797,31 @@ namespace chaiscript virtual ~Lambda_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE{ - std::vector t_param_names; - size_t numparams = 0; - dispatch::Param_Types param_types; + const auto captures = [&]()->std::map{ + std::map named_captures; + for (const auto &capture : children[0]->children) { + named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss))); + } + return named_captures; + }(); - if (!this->children.empty() && (this->children[0]->identifier == AST_Node_Type::Arg_List)) { - numparams = this->children[0]->children.size(); - t_param_names = Arg_List_AST_Node::get_arg_names(this->children[0]); - param_types = Arg_List_AST_Node::get_arg_types(this->children[0], t_ss); - } + const auto numparams = this->children[1]->children.size(); + const auto param_names = Arg_List_AST_Node::get_arg_names(this->children[1]); + const auto param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); const auto &lambda_node = this->children.back(); return Boxed_Value(Proxy_Function(new dispatch::Dynamic_Proxy_Function( - [&t_ss, lambda_node, t_param_names](const std::vector &t_params) + [&t_ss, lambda_node, param_names, captures](const std::vector &t_params) { - return detail::eval_function(t_ss, lambda_node, t_param_names, t_params); + return detail::eval_function(t_ss, lambda_node, param_names, t_params, captures); }, static_cast(numparams), lambda_node, param_types))); } + }; struct Block_AST_Node : public AST_Node { @@ -732,25 +831,14 @@ namespace chaiscript virtual ~Block_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE{ - const auto num_children = this->children.size(); - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - for (size_t i = 0; i < num_children; ++i) { - try { - if (i + 1 < num_children) - { - this->children[i]->eval(t_ss); - } else { - return this->children[i]->eval(t_ss); - } - } - catch (const chaiscript::eval::detail::Return_Value &) { - throw; - } + const auto num_children = children.size(); + for (size_t i = 0; i < num_children-1; ++i) { + children[i]->eval(t_ss); } + return children.back()->eval(t_ss); - return Boxed_Value(); } }; @@ -883,8 +971,7 @@ namespace chaiscript if (get_bool_condition(this->children[0]->eval(t_ss))) { return this->children[1]->eval(t_ss); - } - else { + } else { if (this->children.size() > 2) { size_t i = 2; bool cond = false; @@ -917,26 +1004,22 @@ namespace chaiscript virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE{ chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - // initial expression - this->children[0]->eval(t_ss); - try { - // while condition evals to true - while (get_bool_condition(this->children[1]->eval(t_ss))) { + for ( + children[0]->eval(t_ss); + get_bool_condition(children[1]->eval(t_ss)); + children[2]->eval(t_ss) + ) { try { // Body of Loop - this->children[3]->eval(t_ss); + children[3]->eval(t_ss); } catch (detail::Continue_Loop &) { // we got a continue exception, which means all of the remaining // loop implementation is skipped and we just need to continue to // the next iteration step } - - // loop expression - this->children[2]->eval(t_ss); } - } - catch (detail::Break_Loop &) { + } catch (detail::Break_Loop &) { // loop broken } @@ -1388,7 +1471,7 @@ namespace chaiscript guard = std::make_shared (std::bind(chaiscript::eval::detail::eval_function, std::ref(t_ss), guardnode, - t_param_names, std::placeholders::_1), static_cast(numparams), guardnode); + t_param_names, std::placeholders::_1, std::map()), static_cast(numparams), guardnode); } try { @@ -1399,7 +1482,7 @@ namespace chaiscript if (function_name == class_name) { param_types.push_front(class_name, Type_Info()); t_ss.add(std::make_shared(class_name, std::make_shared(std::bind(chaiscript::eval::detail::eval_function, - std::ref(t_ss), this->children.back(), t_param_names, std::placeholders::_1), + std::ref(t_ss), this->children.back(), t_param_names, std::placeholders::_1, std::map()), static_cast(numparams), this->children.back(), param_types, l_annotation, guard)), function_name); @@ -1415,7 +1498,7 @@ namespace chaiscript std::make_shared(class_name, std::make_shared(std::bind(chaiscript::eval::detail::eval_function, std::ref(t_ss), this->children.back(), - t_param_names, std::placeholders::_1), static_cast(numparams), this->children.back(), + t_param_names, std::placeholders::_1, std::map()), static_cast(numparams), this->children.back(), param_types, l_annotation, guard), type), function_name); } catch (const std::range_error &) { param_types.push_front(class_name, Type_Info()); @@ -1424,7 +1507,7 @@ namespace chaiscript std::make_shared(class_name, std::make_shared(std::bind(chaiscript::eval::detail::eval_function, std::ref(t_ss), this->children.back(), - t_param_names, std::placeholders::_1), static_cast(numparams), this->children.back(), + t_param_names, std::placeholders::_1, std::map()), static_cast(numparams), this->children.back(), param_types, l_annotation, guard)), function_name); } } diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 117f5d6..8e5bc0d 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -196,6 +196,127 @@ namespace chaiscript return m_match_stack.front(); } + static std::map count_fun_calls(const AST_NodePtr &p, bool in_loop) { + if (p->identifier == AST_Node_Type::Fun_Call) { + if (p->children[0]->identifier == AST_Node_Type::Id) { + return std::map{{p->children[0]->text, in_loop?99:1}}; + } + return std::map(); + } else { + std::map counts; + for (const auto &child : p->children) { + auto childcounts = count_fun_calls(child, in_loop || p->identifier == AST_Node_Type::For || p->identifier == AST_Node_Type::While); + for (const auto &count : childcounts) { + counts[count.first] += count.second; + } + } + return counts; + } + + } + + static void optimize_fun_lookups(AST_NodePtr &p) + { + for (auto &c : p->children) + { + + if (c->identifier == AST_Node_Type::Def + || c->identifier == AST_Node_Type::Method + || c->identifier == AST_Node_Type::Lambda) { + std::vector children_to_add; + auto counts = count_fun_calls(c, false); + for (const auto &count : counts) { + // std::cout << " Fun Call Count: " << count.first << " " << count.second << '\n'; + if (count.second > 1) { + children_to_add.push_back(std::make_shared(count.first)); + } + } + c->children.back()->children.insert(c->children.back()->children.begin(), children_to_add.begin(), children_to_add.end()); + } + optimize_fun_lookups(c); + } + } + + + static void optimize_blocks(AST_NodePtr &p) + { + for (auto &c : p->children) + { + if (c->identifier == AST_Node_Type::Block) { + if (c->children.size() == 1) { + // std::cout << "swapping out block child for block\n"; + c = c->children[0]; + } + } + optimize_blocks(c); + } + } + + static void optimize_returns(AST_NodePtr &p) + { + for (auto &c : p->children) + { + if (c->identifier == AST_Node_Type::Def && c->children.size() > 0) { + auto &lastchild = c->children.back(); + if (lastchild->identifier == AST_Node_Type::Block) { + auto &blocklastchild = lastchild->children.back(); + if (blocklastchild->identifier == AST_Node_Type::Return) { + if (blocklastchild->children.size() == 1) { + blocklastchild = blocklastchild->children[0]; + } + } + } + } + optimize_returns(c); + } + } + + static void optimize_fun_calls(AST_NodePtr &p) + { + for (auto &c : p->children) + { + if (c->identifier == AST_Node_Type::Fun_Call && c->children.size() == 2 && c->children[1]->children.size() == 1) { + c = std::make_shared(dynamic_cast(*c)); + // std::cout << "optimized unary fun call\n"; + } + optimize_fun_calls(c); + } + } + + static void fixup_opers(AST_NodePtr &p) + { + if (p->identifier == AST_Node_Type::Equation) + { + dynamic_cast(*p).m_oper = Operators::to_operator(p->children[1]->text); + } + + for (auto &c : p->children) { + fixup_opers(c); + } + } + + static int count_nodes(const AST_NodePtr &p) + { + int count = 1; + for (auto &c : p->children) { + count += count_nodes(c); + } + return count; + } + + AST_NodePtr optimized_ast(bool t_optimize_blocks = false, bool t_optimize_returns = true, bool t_optimize_fun_lookups = false, + bool t_optimize_fun_calls = false) { + AST_NodePtr p = m_match_stack.front(); + fixup_opers(p); + //Note, optimize_blocks is currently broken; it breaks stack management + if (t_optimize_blocks) { optimize_blocks(p); } + if (t_optimize_returns) { optimize_returns(p); } + if (t_optimize_fun_lookups) { optimize_fun_lookups(p); } + if (t_optimize_fun_calls) { optimize_fun_calls(p); } + return p; + } + + /// Helper function that collects ast_nodes from a starting position to the top of the stack into a new AST node void build_match(AST_NodePtr t_t, size_t t_match_start) { int pos_line_start, pos_col_start, pos_line_stop, pos_col_stop; @@ -692,7 +813,7 @@ namespace chaiscript } /// Reads an argument from input - bool Arg() { + bool Arg(const bool t_type_allowed = true) { const auto prev_stack_top = m_match_stack.size(); SkipWS(); @@ -701,7 +822,10 @@ namespace chaiscript } SkipWS(); - Id(true); + + if (t_type_allowed) { + Id(true); + } build_match(std::make_shared(), prev_stack_top); @@ -1120,6 +1244,32 @@ namespace chaiscript } } + /// Reads a comma-separated list of values from input. Id's only, no types allowed + bool Id_Arg_List() { + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Arg(false)) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Arg(false)) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_line, m_col), *m_filename); + } + } while (Char(',')); + } + } + build_match(std::make_shared(), prev_stack_top); + + SkipWS(true); + + return retval; + } + /// Reads a comma-separated list of values from input, for function declarations bool Decl_Arg_List() { SkipWS(true); @@ -1138,8 +1288,8 @@ namespace chaiscript } } while (Char(',')); } - build_match(std::make_shared(), prev_stack_top); } + build_match(std::make_shared(), prev_stack_top); SkipWS(true); @@ -1223,13 +1373,26 @@ namespace chaiscript if (Keyword("fun")) { retval = true; + if (Char('[')) { + Id_Arg_List(); + if (!Char(']')) { + throw exception::eval_error("Incomplete anonymous function bind", File_Position(m_line, m_col), *m_filename); + } + } else { + // make sure we always have the same number of nodes + build_match(std::make_shared(), prev_stack_top); + } + if (Char('(')) { Decl_Arg_List(); if (!Char(')')) { throw exception::eval_error("Incomplete anonymous function", File_Position(m_line, m_col), *m_filename); } + } else { + throw exception::eval_error("Incomplete anonymous function", File_Position(m_line, m_col), *m_filename); } + while (Eol()) {} if (!Block()) { @@ -1645,6 +1808,10 @@ namespace chaiscript throw exception::eval_error("Incomplete class block", File_Position(m_line, m_col), *m_filename); } + if (m_match_stack.size() == prev_stack_top) { + m_match_stack.push_back(std::make_shared()); + } + build_match(std::make_shared(), prev_stack_top); } @@ -1665,6 +1832,10 @@ namespace chaiscript throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename); } + if (m_match_stack.size() == prev_stack_top) { + m_match_stack.push_back(std::make_shared()); + } + build_match(std::make_shared(), prev_stack_top); } diff --git a/include/chaiscript/language/chaiscript_prelude.chai b/include/chaiscript/language/chaiscript_prelude.chai index 1db8702..88835c5 100644 --- a/include/chaiscript/language/chaiscript_prelude.chai +++ b/include/chaiscript/language/chaiscript_prelude.chai @@ -38,7 +38,24 @@ def eq(l, r) { def new(x) { eval(type_name(x))(); -} +} + +def clone(double x) { + double(x).copy_var_attrs(x) +} + +def clone(string x) { + string(x).copy_var_attrs(x) +} + +def clone(vector x) { + vector(x).copy_var_attrs(x) +} + + +def clone(int x) { + int(x).copy_var_attrs(x) +} def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) { diff --git a/unittests/lambda.chai b/unittests/lambda.chai index 9b717ec..71f7bb2 100644 --- a/unittests/lambda.chai +++ b/unittests/lambda.chai @@ -1,2 +1,6 @@ auto bob = fun(x) { x + 1 } assert_equal(4, bob(3)); + +var y=3 +auto bob2 = fun[y](x) { x + y } +assert_equal(7, bob2(4))