AppleClang's libc++ does not support std::from_chars for floating-point
types. Add a compile-time detection macro (CHAISCRIPT_HAS_FLOAT_FROM_CHARS)
and fall back to strtod/strtof/strtold on libc++ platforms. Centralize the
fallback in parse_num and route bootstrap.hpp and json.hpp through it to
keep the workaround in one place.
Requested by @lefticus in PR #669 review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- buildInt: parse once as unsigned long long (input is never negative),
strip suffixes with remove_suffix, eliminate two-pass fallback path
- json.hpp parse_number: scan number boundaries in-place, call from_chars
directly on the string range — no intermediate strings or std::pow
- parse_num: remove dead integral overload, collapse to single template
- Remove unused <cmath> includes from both files
Requested by @lefticus in PR #669 review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The custom parse_num floating-point implementation produced slightly
different results from the C++ compiler's own literal parsing (e.g.
1.1e-4 was off by a few ULPs). Replace it with std::from_chars which
is locale-independent and matches compiler precision exactly. Also
migrate integer parsing in buildInt from std::stoll/stoull to
std::from_chars, fix the JSON number parser to pass the full numeric
string to from_chars instead of splitting mantissa/exponent and using
std::pow, and update parse_string to use from_chars for arithmetic types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Return() parser function called Operator() to parse the return value,
which only handles arithmetic/logical operators but not assignments. Changed
it to call Equation(), which wraps Operator() and adds assignment parsing.
This is consistent with how If, For, and function argument parsing already
work. Enables `return foo = 5`, `return x += 1`, etc.
Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 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>
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<value_type>) so they
are only compiled when the element type supports copying.
Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jason Turner <jason@emptycrate.com>
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 <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jason Turner <jason@emptycrate.com>
* Fix#655: Join async threads before engine destruction to prevent heap-use-after-free
Issues #632 and #636 (PRs #651 and #653) both stem from the same root cause: async
threads spawned via async() can outlive the Dispatch_Engine, accessing shared state
(global objects map, type maps) after it has been destroyed. The fix moves async()
registration from the stdlib module into ChaiScript_Basic, where spawned threads are
tracked via Dispatch_Engine. The engine's destructor now joins all outstanding async
threads before destroying shared data structures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review: follow rule of 5, explicitly default move operations
Requested by @lefticus in PR #656 review.
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>
Previously, to_<type> only accepted a string or the same type (identity),
and <type>() constructors only accepted Boxed_Number. This left gaps such as
to_int(char), to_char(int), char(string), int(string), and to_double(char).
Add two overloads to bootstrap_pod_type: to_<name>(Boxed_Number) enables
cross-type numeric conversions (e.g. to_int('A') → 65, to_char(65) → 'A'),
and <name>(string) via parse_string enables construction from strings
(e.g. char("A") → 'A', int("65") → 65, double("3.5") → 3.5).
Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix#405: push_back() on script-created vector has no effect with vector_conversion
When both vector_type<T> and vector_conversion<T> are registered, the
dispatch scoring treats all parameter mismatches equally. This causes
the C++ push_back (for the converted type) to be selected over the
built-in one when both have the same number of type differences. The
converted function operates on a temporary copy of the vector, silently
discarding the mutation. The fix deprioritizes overloads that require
type conversion on the first parameter (the object/receiver), ensuring
functions matching the receiver type exactly are always tried first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review: remove issue references from comments, add round-trip conversion tests
Requested by @lefticus in PR #663 review.
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>
The forward declarations of AST_Node_Trace and eval_error at the top of
chaiscript_common.hpp introduced these types as incomplete before the standard
library headers were fully processed. On clang/libc++ in C++20 mode, this
caused compilation errors because std::vector<AST_Node_Trace> was instantiated
while the type was still incomplete. Since the full definitions of both types
appear later in the same file (and nothing between the forward declarations
and the definitions references them), these forward declarations are unnecessary
and removing them prevents the incomplete type issue.
Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The saw_eol variable in Class_Statements() was initialized to true and
only ever set back to true, making the "missing line separator" check
unreachable. The fix separates Def (block statement ending with }) from
Var_Decl (simple statement) so that Var_Decl sets saw_eol to false,
matching the pattern used in Statements() for simple expressions.
Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix#256: Expose get_function_objects() and get_scripting_objects() in public API
The ChaiScript_Basic public API only exposed get_locals() for inspecting
runtime state from C++, requiring users to work around this via
chai.eval("get_functions()") to access function objects. Added
get_function_objects() and get_scripting_objects() as public methods on
ChaiScript_Basic, delegating to the existing Dispatch_Engine methods,
matching the pattern already used by get_locals().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review: add tests for get_scripting_objects()
Requested by @lefticus in PR #640 review.
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>
* Fix#17: Add const local variable support to ChaiScript
Adds `const var`, `const auto`, and `const` as variable declaration
syntax that creates immutable local variables. A const_override flag
on Boxed_Value enables script-level constness without changing the
C++ type system integration. The parser, optimizer, and evaluator
are extended with Const_Var_Decl and Const_Assign_Decl AST nodes
that mirror their non-const counterparts but mark the value as const
after initialization.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review: remove const_override, set const flag directly on Type_Info
Replace the m_const_override bool on Boxed_Value::Data with a
Type_Info::make_const() method that sets the const bit in m_flags
directly. This ensures constness is visible everywhere consistently,
including places that check get_type_info().is_const() directly.
Requested by @lefticus in PR #643 review.
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>
Convert \u escape sequences to proper UTF-8 characters instead of
passing through the literal \u notation. Supports the full BMP range
with correct 1, 2, 3, and 4-byte UTF-8 encoding.
Based on PR #483 by @olikraus, rebased onto current develop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix#201: Add class inheritance support with Derived : Base syntax
Classes can now inherit methods and attributes from a base class using
C++-style syntax: `class Derived : Base { ... }`. Base class methods and
attributes are automatically available on derived objects. Derived classes
can override base methods by defining a method with the same name.
Inheritance relationships are tracked to support proper type matching
in the dispatch system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review: use implicit derived-to-base matching instead of copying base class functions
Instead of copying all base class methods/attributes into derived classes,
make the type matching system recognize inheritance relationships. Base class
methods now naturally match derived objects through dynamic_object_typename_match,
and dispatch ordering ensures derived overrides are preferred over base methods.
This is simpler (net -25 lines) and avoids duplicating function registrations.
Requested by @lefticus in PR #641 review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add tests for passing derived objects to functions expecting Base
Tests cover: free functions calling base methods on derived objects,
polymorphic dispatch through containers, base attribute access on
derived objects, and multi-level inheritance (GrandChild : Derived : Base).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add typed parameter tests for class inheritance
Use typed function signatures (e.g., `def call_do_something(Base obj)`)
instead of untyped parameters to test that derived objects are accepted
by functions expecting a base type, with correct polymorphic dispatch.
Requested by @lefticus in PR #641 review.
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>
Add Function_Push_Pop to Switch_AST_Node case comparison to properly
manage the lifetime of temporaries created during type conversions.
Make Binary_Operator_AST_Node::do_oper static and public so it can be
reused by Switch_AST_Node for case equality checks, ensuring consistent
lifetime management across all operator invocations.
Original-PR: #422
Co-Authored-By: dinghram <don.inghram@gmail.com>
Add C++11-style raw string literals (R"delimiter(content)delimiter") to
ChaiScript. Raw strings do not process escape sequences or perform string
interpolation, solving the issue where base85 encoded data containing ${
sequences would trigger unwanted instring_eval. The implementation adds
Raw_String_() and Raw_String() parser functions and hooks them into the
expression parser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive reflection documentation to cheatsheet.md covering type
inspection, object methods, Type_Info, function introspection, system
introspection, and Dynamic_Object reflection. Add missing global reflection
functions (type_name, is_type, function_exists, get_functions, get_objects,
type, dump_system, dump_object) and is_type_arithmetic to the Doxygen
prelude docs. Include a thorough test exercising all documented features.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Using GCC 13.3.0 I get warnings like the following:
```
In file included from .../ChaiScript/static_libs/../include/chaiscript/language/chaiscript_optimizer.hpp:10,
from .../ChaiScript/static_libs/../include/chaiscript/language/chaiscript_parser.hpp:26,
from .../ChaiScript/static_libs/chaiscript_parser.cpp:1:
.../ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp: In instantiation of ‘chaiscript::Boxed_Value chaiscript::eval::Global_Decl_AST_Node<T>::eval_internal(const chaiscript::detail::Dispatch_State&) const [with T = chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>]’:
.../ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp:503:19: required from here
.../ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp:504:28: warning: possibly dangling reference to a temporary [-Wdangling-reference]
504 | const std::string &idname = [&]() -> const std::string & {
| ^~~~~~
.../ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp:510:10: note: the temporary was destroyed at the end of the full expression ‘<lambda closure object>chaiscript::eval::Global_Decl_AST_Node<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail> >::eval_internal(const chaiscript::detail::Dispatch_State&) const::<lambda()>{((const chaiscript::eval::Global_Decl_AST_Node<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail> >*)this)}.chaiscript::eval::Global_Decl_AST_Node<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail> >::eval_internal(const chaiscript::detail::Dispatch_State&) const::<lambda()>()’
504 | const std::string &idname = [&]() -> const std::string & {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
505 | if (this->children[0]->identifier == AST_Node_Type::Reference) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
506 | return this->children[0]->children[0]->text;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
507 | } else {
| ~~~~~~~~
508 | return this->children[0]->text;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
509 | }
| ~
510 | }();
| ~^~
```
Fix this by replacing the lambda with a simple ternary operator
expression.
ChaiScript inherits from ChaiScript_Basic, so this is good practice.
I also need to be able to inherit from ChaiScript and dynamic cast, which is impossible without this destructor.
I initially tried to use the existing .clang-format file,
but it does not match the code style (at least with clang-format 11)
and the formatting is not consistent across files.
Therefore, I decided to rewrite the .clang-format with some personal
preferences.
Used command
find . -iname "*.hpp" -o -iname "*.cpp" | xargs clang-format -i -style=file
is_pod_v was deprecated in C++20, is_pod_v can be
replaced with is_trivial_v && is_standard_layout_v.
I don't see any benefit from is_standard_layout_v,
but I could have missed something.