mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-04-30 19:09:26 +08:00
* 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) <noreply@anthropic.com> * 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) <noreply@anthropic.com> * 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) <noreply@anthropic.com> --------- Co-authored-by: leftibot <leftibot@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
005f18feb2
commit
08281a9d69
@ -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");
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -1343,6 +1343,132 @@ 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 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<int>("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<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});
|
||||
|
||||
CHECK_THROWS(chai.eval("print_string(\"hello\")"));
|
||||
CHECK_THROWS(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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user