mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-07-31 00:37:02 +08:00
Address review: fix merge conflicts with develop, ensure print_handler works with No_IO
Merged upstream changes including #642 (Library_Options: No_Stdlib, No_IO, No_Prelude, No_JSON), #524 (vector of non-copyable types), and #625 (function_less_than strict-weak ordering). When No_IO is set, bootstrap skips the static print/println registration, but print_string, println_string, and set_print_handler remain available via the handler mechanism in build_eval_system(). This ensures users can always register their own print handlers regardless of IO configuration. Requested by @lefticus in PR #657 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
258eb63fd1
@ -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<Options>` 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<Library_Options>`.
|
||||
|
||||
| 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
|
||||
|
||||
@ -824,8 +824,9 @@ namespace chaiscript {
|
||||
public:
|
||||
ChaiScript(std::vector<std::string> t_modulepaths = {},
|
||||
std::vector<std::string> t_usepaths = {},
|
||||
std::vector<Options> t_opts = chaiscript::default_options())
|
||||
: ChaiScript_Basic(chaiscript::Std_Lib::library(),
|
||||
std::vector<Options> t_opts = chaiscript::default_options(),
|
||||
std::vector<Library_Options> t_lib_opts = {})
|
||||
: ChaiScript_Basic(chaiscript::Std_Lib::library(t_lib_opts),
|
||||
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),
|
||||
std::move(t_modulepaths),
|
||||
std::move(t_usepaths),
|
||||
|
||||
@ -211,6 +211,13 @@ namespace chaiscript {
|
||||
External_Scripts
|
||||
};
|
||||
|
||||
enum class Library_Options {
|
||||
No_Stdlib,
|
||||
No_IO,
|
||||
No_Prelude,
|
||||
No_JSON
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct is_nothrow_forward_constructible : std::bool_constant<noexcept(To{std::declval<From>()})> {
|
||||
};
|
||||
|
||||
@ -38,9 +38,18 @@
|
||||
namespace chaiscript {
|
||||
class Std_Lib {
|
||||
public:
|
||||
[[nodiscard]] static ModulePtr library() {
|
||||
[[nodiscard]] static ModulePtr library(const std::vector<Library_Options> &t_opts = {}) {
|
||||
if (std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Stdlib) != t_opts.end()) {
|
||||
return std::make_shared<Module>();
|
||||
}
|
||||
|
||||
auto lib = std::make_shared<Module>();
|
||||
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<std::vector<Boxed_Value>>("Vector", *lib);
|
||||
bootstrap::standard_library::string_type<std::string>("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;
|
||||
}
|
||||
|
||||
@ -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>(), "void");
|
||||
m.add(user_type<bool>(), "bool");
|
||||
m.add(user_type<Boxed_Value>(), "Object");
|
||||
@ -438,7 +438,9 @@ namespace chaiscript::bootstrap {
|
||||
m.add(fun(&Build_Info::debug_build), "debug_build");
|
||||
|
||||
// print_string and println_string are registered in ChaiScript_Basic::build_eval_system()
|
||||
// to support per-instance IO redirection via set_print_handler
|
||||
// to support per-instance IO redirection via set_print_handler.
|
||||
// When No_IO is set, the handler-based functions are still registered there,
|
||||
// so users can provide their own print handlers even without built-in IO.
|
||||
|
||||
m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind");
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
@ -170,18 +171,22 @@ namespace chaiscript::bootstrap::standard_library {
|
||||
/// http://www.sgi.com/tech/stl/Assignable.html
|
||||
template<typename ContainerType>
|
||||
void assignable_type(const std::string &type, Module &m) {
|
||||
copy_constructor<ContainerType>(type, m);
|
||||
operators::assign<ContainerType>(m);
|
||||
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
|
||||
copy_constructor<ContainerType>(type, m);
|
||||
operators::assign<ContainerType>(m);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add container resize concept to the given ContainerType
|
||||
/// http://www.cplusplus.com/reference/stl/
|
||||
template<typename ContainerType>
|
||||
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<typename ContainerType::value_type>) {
|
||||
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<typename ContainerType>
|
||||
void sequence_type(const std::string & /*type*/, Module &m) {
|
||||
m.add(fun(&detail::insert_at<ContainerType>), []() -> 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<typename ContainerType::value_type>) {
|
||||
m.add(fun(&detail::insert_at<ContainerType>), []() -> 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<ContainerType>), "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<push_back>(&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<typename ContainerType::value_type>) {
|
||||
using push_back = void (ContainerType::*)(const typename ContainerType::value_type &);
|
||||
m.add(fun(static_cast<push_back>(&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<push_ptr>(&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<typename ContainerType::value_type>) {
|
||||
m.add(fun(static_cast<push_ptr>(&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<pop_ptr>(&ContainerType::pop_front)), "pop_front");
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -1343,6 +1343,140 @@ TEST_CASE("Test if non copyable/movable types can be registered") {
|
||||
chai.add(chaiscript::constructor<Nothing()>(), "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 still allows print_handler registration") {
|
||||
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});
|
||||
|
||||
// print_string and println_string should still be available via the handler mechanism
|
||||
CHECK_NOTHROW(chai.eval("print_string(\"hello\")"));
|
||||
CHECK_NOTHROW(chai.eval("println_string(\"hello\")"));
|
||||
CHECK(chai.eval<int>("5 + 3") == 8);
|
||||
CHECK_NOTHROW(chai.eval("var v = Vector()"));
|
||||
|
||||
// Users can set their own print handler even with No_IO
|
||||
std::string captured;
|
||||
chai.set_print_handler([&captured](const std::string &s) { captured += s; });
|
||||
chai.eval("print_string(\"redirected\")");
|
||||
CHECK(captured == "redirected");
|
||||
}
|
||||
|
||||
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<int>("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<int>("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<int>("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});
|
||||
|
||||
// print_string and println_string remain available via the handler mechanism
|
||||
CHECK_NOTHROW(chai.eval("print_string(\"hello\")"));
|
||||
CHECK_NOTHROW(chai.eval("println_string(\"hello\")"));
|
||||
CHECK(chai.eval<int>("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<int>("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<int>("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<int>("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
|
||||
@ -1437,6 +1571,50 @@ 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<std::unique_ptr<Issue524_Foo>>;
|
||||
|
||||
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<Issue524_Foo>.
|
||||
chaiscript::ModulePtr m = std::make_shared<chaiscript::Module>();
|
||||
chaiscript::bootstrap::standard_library::vector_type<VecType>("UniqueVec", *m);
|
||||
CHECK_NOTHROW(chai.add(m));
|
||||
|
||||
// Verify basic operations still work
|
||||
CHECK(chai.eval<size_t>("var v = UniqueVec(); v.size()") == 0);
|
||||
CHECK(chai.eval<bool>("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
|
||||
// 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<int>(y); }), "overloaded"));
|
||||
CHECK_NOTHROW(chai.add(chaiscript::fun([](int x) { return x; }), "overloaded"));
|
||||
|
||||
// Verify dispatch still works correctly
|
||||
CHECK(chai.eval<int>("overloaded(5)") == 5);
|
||||
CHECK(chai.eval<int>("overloaded(3, 2.0)") == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("IO redirection with set_print_handler") {
|
||||
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user