From 11fec25112a92f085c92f402acb8ab5e90250b8f Mon Sep 17 00:00:00 2001 From: leftibot Date: Sat, 11 Apr 2026 16:42:40 -0600 Subject: [PATCH 1/3] Fix #625: function_less_than comparator violates strict-weak ordering (#654) The function_less_than comparator used by std::stable_sort violated the strict-weak ordering requirement in two ways: (1) functions with different arities but matching overlapping parameters were treated as equivalent, breaking transitivity, and (2) the dynamic_object_type_name comparison silently fell through when one side had an empty type name. Fixed by ordering by arity when overlapping parameters match, and imposing a total order on dynamic object type names. Co-authored-by: leftibot Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Jason Turner --- .../chaiscript/dispatchkit/dispatchkit.hpp | 22 ++++++++++++++----- unittests/compiled_tests.cpp | 19 ++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 95627dbc..be43905d 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1087,13 +1087,21 @@ namespace chaiscript { // overridden methods in derived classes are tried first during dispatch const auto &lhs_dotn = lhs->dynamic_object_type_name(); const auto &rhs_dotn = rhs->dynamic_object_type_name(); - if (!lhs_dotn.empty() && !rhs_dotn.empty() && lhs_dotn != rhs_dotn) { - if (dispatch::Dynamic_Object::type_matches(lhs_dotn, rhs_dotn)) { - return true; // lhs is derived from rhs, so lhs is more specific + if (lhs_dotn != rhs_dotn) { + if (!lhs_dotn.empty() && !rhs_dotn.empty()) { + if (dispatch::Dynamic_Object::type_matches(lhs_dotn, rhs_dotn)) { + return true; // lhs is derived from rhs, so lhs is more specific + } + if (dispatch::Dynamic_Object::type_matches(rhs_dotn, lhs_dotn)) { + return false; // rhs is derived from lhs, so rhs is more specific + } } - if (dispatch::Dynamic_Object::type_matches(rhs_dotn, lhs_dotn)) { - return false; // rhs is derived from lhs, so rhs is more specific + // Impose a total order on type names to maintain strict-weak ordering: + // non-empty names sort before empty names, then lexicographically + if (lhs_dotn.empty() != rhs_dotn.empty()) { + return !lhs_dotn.empty(); } + return lhs_dotn < rhs_dotn; } const auto &lhsparamtypes = lhs->get_param_types(); @@ -1143,7 +1151,9 @@ namespace chaiscript { return lt < rt; } - return false; + // When all overlapping parameters match, order by arity to maintain + // strict-weak ordering (transitivity of equivalence) + return lhssize < rhssize; } /// Implementation detail for adding a function. diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 5a3f09d1..c4094da1 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1437,6 +1437,25 @@ TEST_CASE("Issue #421 - Switch with type_conversion does not compare destroyed o })") == 0); } +// Issue #625: function_less_than comparator must satisfy strict-weak ordering. +// Registering overloaded functions with different arities triggered a +// std::stable_sort assertion on macOS 15.2 (hardened libc++) because the +// comparator violated transitivity of equivalence. +TEST_CASE("Issue 625: function_less_than strict-weak ordering with different arities") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + // Register overloaded functions with varying arities under the same name. + // If the comparator doesn't order by arity when overlapping params match, + // std::stable_sort may exhibit undefined behavior. + CHECK_NOTHROW(chai.add(chaiscript::fun([](int x, const chaiscript::Boxed_Value &) { return x; }), "overloaded")); + CHECK_NOTHROW(chai.add(chaiscript::fun([](int x, double y) { return x + static_cast(y); }), "overloaded")); + CHECK_NOTHROW(chai.add(chaiscript::fun([](int x) { return x; }), "overloaded")); + + // Verify dispatch still works correctly + CHECK(chai.eval("overloaded(5)") == 5); + CHECK(chai.eval("overloaded(3, 2.0)") == 5); +} + // Regression test: push_back() on script-created vector has no effect when // vector_conversion is in effect. The bug occurs because dispatch selects // the C++ push_back for the converted type over the built-in one, operating From 005f18feb2cd0bb684f1989904e0988c2ae595f4 Mon Sep 17 00:00:00 2001 From: leftibot Date: Sat, 11 Apr 2026 16:44:06 -0600 Subject: [PATCH 2/3] Fix #524: Support std::vector of non-copyable types like std::unique_ptr (#648) Several STL bootstrap functions unconditionally instantiated copy-dependent operations (copy constructor, assignment, push_back/push_front by const ref, insert_at, and resize with fill value), causing compilation failures when registering containers of move-only types like std::unique_ptr. Guard these operations with if constexpr(std::is_copy_constructible_v) so they are only compiled when the element type supports copying. Co-authored-by: leftibot Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Jason Turner --- .../chaiscript/dispatchkit/bootstrap_stl.hpp | 115 ++++++++++-------- unittests/compiled_tests.cpp | 25 ++++ 2 files changed, 88 insertions(+), 52 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index f56304d7..5bbc17e0 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -170,18 +171,22 @@ namespace chaiscript::bootstrap::standard_library { /// http://www.sgi.com/tech/stl/Assignable.html template void assignable_type(const std::string &type, Module &m) { - copy_constructor(type, m); - operators::assign(m); + if constexpr (std::is_copy_constructible_v) { + copy_constructor(type, m); + operators::assign(m); + } } /// Add container resize concept to the given ContainerType /// http://www.cplusplus.com/reference/stl/ template void resizable_type(const std::string & /*type*/, Module &m) { - m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) { - return a->resize(n, val); - }), - "resize"); + if constexpr (std::is_copy_constructible_v) { + m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) { + return a->resize(n, val); + }), + "resize"); + } m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); }), "resize"); } @@ -213,13 +218,15 @@ namespace chaiscript::bootstrap::standard_library { /// http://www.sgi.com/tech/stl/Sequence.html template void sequence_type(const std::string & /*type*/, Module &m) { - m.add(fun(&detail::insert_at), []() -> std::string { - if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { - return "insert_ref_at"; - } else { - return "insert_at"; - } - }()); + if constexpr (std::is_copy_constructible_v) { + m.add(fun(&detail::insert_at), []() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + return "insert_ref_at"; + } else { + return "insert_at"; + } + }()); + } m.add(fun(&detail::erase_at), "erase_at"); } @@ -245,27 +252,29 @@ namespace chaiscript::bootstrap::standard_library { }), "back"); - using push_back = void (ContainerType::*)(const typename ContainerType::value_type &); - m.add(fun(static_cast(&ContainerType::push_back)), [&]() -> std::string { - if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { - m.eval("# Pushes the second value onto the container while making a clone of the value\n" - "def push_back(" - + type - + " container, x)\n" - "{ \n" - " if (x.is_var_return_value()) {\n" - " x.reset_var_return_value() \n" - " container.push_back_ref(x) \n" - " } else { \n" - " container.push_back_ref(clone(x)); \n" - " }\n" - "} \n"); + if constexpr (std::is_copy_constructible_v) { + using push_back = void (ContainerType::*)(const typename ContainerType::value_type &); + m.add(fun(static_cast(&ContainerType::push_back)), [&]() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + m.eval("# Pushes the second value onto the container while making a clone of the value\n" + "def push_back(" + + type + + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_back_ref(x) \n" + " } else { \n" + " container.push_back_ref(clone(x)); \n" + " }\n" + "} \n"); - return "push_back_ref"; - } else { - return "push_back"; - } - }()); + return "push_back_ref"; + } else { + return "push_back"; + } + }()); + } m.add(fun(&ContainerType::pop_back), "pop_back"); } @@ -295,25 +304,27 @@ namespace chaiscript::bootstrap::standard_library { }), "front"); - m.add(fun(static_cast(&ContainerType::push_front)), [&]() -> std::string { - if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { - m.eval("# Pushes the second value onto the front of container while making a clone of the value\n" - "def push_front(" - + type - + " container, x)\n" - "{ \n" - " if (x.is_var_return_value()) {\n" - " x.reset_var_return_value() \n" - " container.push_front_ref(x) \n" - " } else { \n" - " container.push_front_ref(clone(x)); \n" - " }\n" - "} \n"); - return "push_front_ref"; - } else { - return "push_front"; - } - }()); + if constexpr (std::is_copy_constructible_v) { + m.add(fun(static_cast(&ContainerType::push_front)), [&]() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + m.eval("# Pushes the second value onto the front of container while making a clone of the value\n" + "def push_front(" + + type + + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_front_ref(x) \n" + " } else { \n" + " container.push_front_ref(clone(x)); \n" + " }\n" + "} \n"); + return "push_front_ref"; + } else { + return "push_front"; + } + }()); + } m.add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); } diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index c4094da1..0a2d252b 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1437,6 +1437,31 @@ TEST_CASE("Issue #421 - Switch with type_conversion does not compare destroyed o })") == 0); } +// Issue #524: A std::vector of std::unique_ptrs can't be added +// vector_type should compile with non-copyable value types by +// skipping copy-dependent operations via if constexpr. +struct Issue524_Foo { + int value = 42; +}; + +TEST_CASE("Issue #524 - vector of unique_ptr can be registered") { + using VecType = std::vector>; + + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + // This should compile and not throw - previously failed to compile + // because vector_type tried to instantiate copy constructor, assignment, + // push_back(const T&), insert_at(const T&), and resize(n, const T&) + // for the non-copyable std::unique_ptr. + chaiscript::ModulePtr m = std::make_shared(); + chaiscript::bootstrap::standard_library::vector_type("UniqueVec", *m); + CHECK_NOTHROW(chai.add(m)); + + // Verify basic operations still work + CHECK(chai.eval("var v = UniqueVec(); v.size()") == 0); + CHECK(chai.eval("var v2 = UniqueVec(); v2.empty()") == true); +} + // Issue #625: function_less_than comparator must satisfy strict-weak ordering. // Registering overloaded functions with different arities triggered a // std::stable_sort assertion on macOS 15.2 (hardened libc++) because the From 08281a9d693bdb14b6489d469feca8d72dae82ba Mon Sep 17 00:00:00 2001 From: leftibot Date: Sat, 11 Apr 2026 16:49:13 -0600 Subject: [PATCH 3/3] Fix #146: Add configuration to bypass the registering of 'built-in' functions. (#642) * Fix #146: Add configuration options to selectively disable built-in functions Add new Options enum values (No_Stdlib, No_IO, No_Prelude, No_JSON) that allow users to control which parts of the standard library are registered. Std_Lib::library() now accepts an options vector, and the ChaiScript convenience class forwards its options to the library builder. This enables use cases where only custom functions should be exposed to script users. Co-Authored-By: Claude Opus 4.6 (1M context) * Address review: split Options into Options and Library_Options enums Separate system-level options (No_Load_Modules, Load_Modules, No_External_Scripts, External_Scripts) from library-level options (No_Stdlib, No_IO, No_Prelude, No_JSON) into two distinct enum types. Add Library_Options as a parameter to the ChaiScript constructor. Update tests to demonstrate both ChaiScript_Basic (explicit Std_Lib::library call) and ChaiScript (library options via constructor parameter) usage. Requested by @lefticus in PR #642 review. Co-Authored-By: Claude Opus 4.6 (1M context) * Add cheatsheet documentation for Options and Library_Options Documents the two-enum configuration system: Options (engine-level: load_module, use, eval_file) and Library_Options (stdlib-level: No_Stdlib, No_IO, No_Prelude, No_JSON), with usage examples for both ChaiScript and ChaiScript_Basic constructors. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: leftibot Co-authored-by: Claude Opus 4.6 (1M context) --- cheatsheet.md | 61 +++++++++ include/chaiscript/chaiscript.hpp | 5 +- include/chaiscript/chaiscript_defines.hpp | 7 ++ include/chaiscript/chaiscript_stdlib.hpp | 21 +++- include/chaiscript/dispatchkit/bootstrap.hpp | 10 +- unittests/compiled_tests.cpp | 126 +++++++++++++++++++ 6 files changed, 220 insertions(+), 10 deletions(-) diff --git a/cheatsheet.md b/cheatsheet.md index 1819ddc0..d28241ec 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -16,6 +16,67 @@ chaiscript::ChaiScript chai; // initializes ChaiScript, adding the standard Chai Note that ChaiScript cannot be used as a global / static object unless it is being compiled with `CHAISCRIPT_NO_THREADS`. +## Engine Options (`Options`) + +Engine-level options control which scripting capabilities are exposed. These are passed as a `std::vector` to the `ChaiScript` or `ChaiScript_Basic` constructor. + +| Option | Effect | +|--------|--------| +| `Options::Load_Modules` | Enables `load_module()` in scripts (default) | +| `Options::No_Load_Modules` | Disables `load_module()` | +| `Options::External_Scripts` | Enables `use()` and `eval_file()` in scripts (default) | +| `Options::No_External_Scripts` | Disables `use()` and `eval_file()` | + +```cpp +// Sandboxed engine: no dynamic module loading, no external script evaluation +chaiscript::ChaiScript chai({}, {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); +``` + +## Library Options (`Library_Options`) + +Library-level options control which parts of the standard library are registered. These are passed as a `std::vector`. + +| Option | Effect | +|--------|--------| +| `Library_Options::No_Stdlib` | Disables the entire standard library (types, I/O, prelude, JSON — everything) | +| `Library_Options::No_IO` | Disables `print_string` and `println_string` (and the prelude's `print`/`puts` wrappers) | +| `Library_Options::No_Prelude` | Disables the ChaiScript prelude (`print`, `puts`, `filter`, `map`, `foldl`, `join`, etc.) | +| `Library_Options::No_JSON` | Disables `from_json` and `to_json` | + +With the `ChaiScript` convenience class, pass library options as the fourth constructor parameter: + +```cpp +// No I/O functions +chaiscript::ChaiScript chai({}, {}, chaiscript::default_options(), + {chaiscript::Library_Options::No_IO}); + +// No JSON support +chaiscript::ChaiScript chai({}, {}, chaiscript::default_options(), + {chaiscript::Library_Options::No_JSON}); + +// Completely bare engine — no stdlib at all +chaiscript::ChaiScript chai({}, {}, chaiscript::default_options(), + {chaiscript::Library_Options::No_Stdlib}); + +// Combine both: no external scripts and no I/O +chaiscript::ChaiScript chai({}, {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}, + {chaiscript::Library_Options::No_IO}); +``` + +With `ChaiScript_Basic`, pass library options directly to `Std_Lib::library()`: + +```cpp +chaiscript::ChaiScript_Basic chai( + chaiscript::Std_Lib::library({chaiscript::Library_Options::No_IO}), + create_chaiscript_parser(), + {}, {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); +``` + +Note: `No_Prelude` disables the prelude script which defines convenience functions like `print` (which wraps `print_string`). If you disable the prelude but not I/O, `print_string` and `println_string` are still available. + # Adding Things To The Engine ## Adding a Function / Method / Member diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 871b353b..1be2f0b4 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -824,8 +824,9 @@ namespace chaiscript { public: ChaiScript(std::vector t_modulepaths = {}, std::vector t_usepaths = {}, - std::vector t_opts = chaiscript::default_options()) - : ChaiScript_Basic(chaiscript::Std_Lib::library(), + std::vector t_opts = chaiscript::default_options(), + std::vector t_lib_opts = {}) + : ChaiScript_Basic(chaiscript::Std_Lib::library(t_lib_opts), std::make_unique>(), std::move(t_modulepaths), std::move(t_usepaths), diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index e01aadd3..f50510f0 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -211,6 +211,13 @@ namespace chaiscript { External_Scripts }; + enum class Library_Options { + No_Stdlib, + No_IO, + No_Prelude, + No_JSON + }; + template struct is_nothrow_forward_constructible : std::bool_constant()})> { }; diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index 0daac71e..73b11542 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -38,9 +38,18 @@ namespace chaiscript { class Std_Lib { public: - [[nodiscard]] static ModulePtr library() { + [[nodiscard]] static ModulePtr library(const std::vector &t_opts = {}) { + if (std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Stdlib) != t_opts.end()) { + return std::make_shared(); + } + auto lib = std::make_shared(); - bootstrap::Bootstrap::bootstrap(*lib); + + const bool no_io = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_IO) != t_opts.end(); + const bool no_prelude = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Prelude) != t_opts.end(); + const bool no_json = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_JSON) != t_opts.end(); + + bootstrap::Bootstrap::bootstrap(*lib, no_io); bootstrap::standard_library::vector_type>("Vector", *lib); bootstrap::standard_library::string_type("string", *lib); @@ -53,9 +62,13 @@ namespace chaiscript { // with thread tracking to prevent heap-use-after-free on engine destruction. #endif - json_wrap::library(*lib); + if (!no_json) { + json_wrap::library(*lib); + } - lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/); + if (!no_prelude) { + lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/); + } return lib; } diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index d9b3e23e..e5d18255 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -268,8 +268,8 @@ namespace chaiscript::bootstrap { public: /// \brief perform all common bootstrap functions for std::string, void and POD types /// \param[in,out] m Module to add bootstrapped functions to - /// \returns passed in Module - static void bootstrap(Module &m) { + /// \param[in] t_no_io If true, skip registering print_string and println_string + static void bootstrap(Module &m, const bool t_no_io = false) { m.add(user_type(), "void"); m.add(user_type(), "bool"); m.add(user_type(), "Object"); @@ -437,8 +437,10 @@ namespace chaiscript::bootstrap { m.add(fun(&Build_Info::compiler_id), "compiler_id"); m.add(fun(&Build_Info::debug_build), "debug_build"); - m.add(fun(&print), "print_string"); - m.add(fun(&println), "println_string"); + if (!t_no_io) { + m.add(fun(&print), "print_string"); + m.add(fun(&println), "println_string"); + } m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 0a2d252b..3cdef6cc 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1343,6 +1343,132 @@ TEST_CASE("Test if non copyable/movable types can be registered") { chai.add(chaiscript::constructor(), "Nothing"); } +// Tests for issue #146: configuration to bypass registering built-in functions +// Tests through ChaiScript_Basic (library options passed explicitly to Std_Lib::library) + +TEST_CASE("ChaiScript_Basic No_Stdlib option disables all standard library functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_Stdlib}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + CHECK_NOTHROW(chai.eval("var x = 5")); + CHECK_THROWS(chai.eval("print(\"hello\")")); + CHECK_THROWS(chai.eval("var v = Vector()")); + CHECK_THROWS(chai.eval("\"hello\".trim()")); + CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); +} + +TEST_CASE("ChaiScript_Basic No_IO option disables print functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_IO}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + CHECK_THROWS(chai.eval("print_string(\"hello\")")); + CHECK_THROWS(chai.eval("println_string(\"hello\")")); + CHECK(chai.eval("5 + 3") == 8); + CHECK_NOTHROW(chai.eval("var v = Vector()")); +} + +TEST_CASE("ChaiScript_Basic No_Prelude option disables prelude functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_Prelude}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + CHECK_THROWS(chai.eval("print(\"hello\")")); + CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); + CHECK_THROWS(chai.eval("filter([1,2,3], fun(x) { x > 1 })")); + CHECK(chai.eval("5 + 3") == 8); +} + +TEST_CASE("ChaiScript_Basic No_JSON option disables JSON support") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_JSON}), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); + CHECK(chai.eval("5 + 3") == 8); + CHECK_NOTHROW(chai.eval("print(\"hello\")")); +} + +TEST_CASE("ChaiScript_Basic default library has all functions") { + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); + + CHECK_NOTHROW(chai.eval("print(\"hello\")")); + CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); + CHECK_NOTHROW(chai.eval("var v = Vector()")); + CHECK(chai.eval("5 + 3") == 8); +} + +// Tests through ChaiScript (library options passed as constructor parameter) + +TEST_CASE("ChaiScript No_Stdlib option via library options parameter") { + chaiscript::ChaiScript chai({}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}, + {chaiscript::Library_Options::No_Stdlib}); + + CHECK_NOTHROW(chai.eval("var x = 5")); + CHECK_THROWS(chai.eval("print(\"hello\")")); + CHECK_THROWS(chai.eval("var v = Vector()")); + CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); +} + +TEST_CASE("ChaiScript No_IO option via library options parameter") { + chaiscript::ChaiScript chai({}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}, + {chaiscript::Library_Options::No_IO}); + + CHECK_THROWS(chai.eval("print_string(\"hello\")")); + CHECK_THROWS(chai.eval("println_string(\"hello\")")); + CHECK(chai.eval("5 + 3") == 8); + CHECK_NOTHROW(chai.eval("var v = Vector()")); +} + +TEST_CASE("ChaiScript No_Prelude option via library options parameter") { + chaiscript::ChaiScript chai({}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}, + {chaiscript::Library_Options::No_Prelude}); + + CHECK_THROWS(chai.eval("print(\"hello\")")); + CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); + CHECK_THROWS(chai.eval("filter([1,2,3], fun(x) { x > 1 })")); + CHECK(chai.eval("5 + 3") == 8); +} + +TEST_CASE("ChaiScript No_JSON option via library options parameter") { + chaiscript::ChaiScript chai({}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}, + {chaiscript::Library_Options::No_JSON}); + + CHECK_THROWS(chai.eval("from_json(\"[1,2,3]\")")); + CHECK(chai.eval("5 + 3") == 8); + CHECK_NOTHROW(chai.eval("print(\"hello\")")); +} + +TEST_CASE("ChaiScript default has all functions") { + chaiscript::ChaiScript chai; + + CHECK_NOTHROW(chai.eval("print(\"hello\")")); + CHECK_NOTHROW(chai.eval("print_string(\"hello\")")); + CHECK_NOTHROW(chai.eval("var v = Vector()")); + CHECK(chai.eval("5 + 3") == 8); +} + // Issue #421: Class with type_conversion from int and "==" operator // causes switch statement to compare destroyed objects. // The switch case comparison must use Function_Push_Pop to properly