Skip to content

Fix #552: Feature-request: nested namespaces#675

Merged
lefticus merged 7 commits intoChaiScript:developfrom
leftibot:fix/issue-552-feature-request-nested-namespaces
Apr 14, 2026
Merged

Fix #552: Feature-request: nested namespaces#675
lefticus merged 7 commits intoChaiScript:developfrom
leftibot:fix/issue-552-feature-request-nested-namespaces

Conversation

@leftibot
Copy link
Copy Markdown
Contributor

Automated fix by @leftibot.

What changed

Fix #552: Support nested namespaces via dotted names
Namespaces can now be nested using dotted name syntax, both from C++
(register_namespace(gen, "constants.si")) and from script
(namespace("constants.si")). Parent namespaces are auto-registered when
absent, and child namespaces are automatically nested into their parent
on import. This allows clean hierarchical organization like
constants.si.mu_B instead of flat names like constants_si.
Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]

Files

 include/chaiscript/language/chaiscript_engine.hpp | 60 ++++++++++++++++++-----
 unittests/compiled_tests.cpp                      | 35 +++++++++++++
 unittests/nested_namespaces.chai                  | 25 ++++++++++
 3 files changed, 108 insertions(+), 12 deletions(-)

Closes #552

Triggered by @lefticus.

Namespaces can now be nested using dotted name syntax, both from C++
(register_namespace(gen, "constants.si")) and from script
(namespace("constants.si")). Parent namespaces are auto-registered when
absent, and child namespaces are automatically nested into their parent
on import. This allows clean hierarchical organization like
constants.si.mu_B instead of flat names like constants_si.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@lefticus
Copy link
Copy Markdown
Member

@leftibot consider :: instead of .. Investigate why :: wasn't used in the first place. See how consistent we can make this with C++. Maybe we can even do:

namespace x::y {
  def func() {}
}

like C++

Switch from dotted names (e.g. "constants.si") to C++-style ::
separator (e.g. "constants::si") for nested namespace declarations,
both in the C++ API (register_namespace) and in script (namespace()).

The original implementation used . because namespace members are
accessed via dot notation at runtime (constants.si.mu_B), making the
declaration separator match the access syntax. However, :: is more
consistent with C++ namespace conventions and aligns with ChaiScript's
existing use of :: for method (def Class::method) and attribute
(attr Class::attr) declarations.

Member access in scripts remains dot-based (constants.si.mu_B) since
that is ChaiScript's member access operator.

Requested by @lefticus in PR ChaiScript#675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@leftibot
Copy link
Copy Markdown
Contributor Author

Addressed review feedback from @lefticus. New commits:

dd9afc83 Address review: use :: instead of . as nested namespace separator

@lefticus
Copy link
Copy Markdown
Member

@leftibot attempt to make namespaces in ChaiScript behave just like those in C++. Both for scoped access (namespace::func) and for declaring objects inside of namespaces.

Add :: scope resolution operator for member access (ns::func works
like ns.func). Add block namespace declarations:
  namespace x::y { def func() { ... } }
Functions and variables declared inside a namespace block are added
as members of the namespace, accessible via :: or dot notation.
Namespaces can be reopened to add more members, matching C++ behavior.

Requested by @lefticus in PR ChaiScript#675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@leftibot
Copy link
Copy Markdown
Contributor Author

Addressed review feedback from @lefticus. New commits:

8f463078 Address review: C++-style namespace scoping and block declarations

: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Namespace_Block, std::move(t_loc), std::move(t_children)) {
}

Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leftibot this code feels like it must be duplicating code that already exists somewhere else and feels potentially fragile if things get updated. Cannot we use the same code that was already used for the namespace() function like declaration previously?

Namespace_Block_AST_Node was duplicating the entire proxy function
creation logic from Def_AST_Node::eval_internal. Extract a static
make_proxy_function helper so both nodes share the same code path,
eliminating fragile duplication that would drift if Def handling changes.

Requested by @lefticus in PR ChaiScript#675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@leftibot
Copy link
Copy Markdown
Contributor Author

Addressed review feedback from @lefticus. New commits:

7aaceab4 Address review: extract shared make_proxy_function from Def_AST_Node

@lefticus
Copy link
Copy Markdown
Member

@leftibot ensure that only declarations are allowed inside of namespace blocks

…ocks

Only def, var, auto, and global declarations are now allowed inside
namespace { } blocks. Arbitrary expressions, assignments, and function
calls are rejected with an eval_error. Added compiled tests verifying
that expressions, function calls, and assignments are rejected.

Requested by @lefticus in PR ChaiScript#675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@leftibot
Copy link
Copy Markdown
Contributor Author

Addressed review feedback from @lefticus. New commits:

ff06e1f0 Address review: reject non-declaration statements inside namespace blocks

@lefticus
Copy link
Copy Markdown
Member

@leftibot resolve merge conflicts

Requested by @lefticus in PR ChaiScript#675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@leftibot
Copy link
Copy Markdown
Contributor Author

Addressed review feedback from @lefticus. New commits:

59198d04 Address review: resolve merge conflicts with develop
e61be765 Fix #601: Allow operator functions to accept any type with an add() method (#686)
d4c5bdb3 Fix #61: Comprehensive exception test suite and fix for silently swallowed exceptions (#681)
362e93fb Fix #499: Add object_from_json, map_to_object, and object_to_map functions (#676)

@lefticus
Copy link
Copy Markdown
Member

@leftibot please remove the -j parameter from the unix builds. We moved to Ninja so that it would do something intelligent and I think we're running out of memory / swapping / something on the asan+ubsan release builds for linux.

Ninja handles parallelism intelligently on its own; the explicit -j flag
was causing memory pressure on sanitizer builds. Windows (non-Ninja)
build retains -j.

Requested by @lefticus in PR ChaiScript#675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@leftibot
Copy link
Copy Markdown
Contributor Author

Addressed review feedback from @lefticus. New commits:

6a6e90aa Address review: remove -j parameter from unix builds

@lefticus lefticus merged commit 9ff5642 into ChaiScript:develop Apr 14, 2026
21 of 22 checks passed
leftibot added a commit to leftibot/ChaiScript that referenced this pull request Apr 14, 2026
Resolve merge conflicts with ChaiScript:develop. Upstream added
nested namespace support (ChaiScript#675), grammar railroad diagrams (ChaiScript#673),
and WASM exception support (ChaiScript#689). Conflicts in chaiscript_common.hpp,
chaiscript_eval.hpp, and chaiscript_parser.hpp resolved by keeping
both Using and Namespace_Block AST node types.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
lefticus pushed a commit that referenced this pull request Apr 15, 2026
* Fix #677: Add strong typedefs via 'using Type = BaseType' syntax

Strong typedefs create distinct types backed by Dynamic_Object, so
'using Meters = int' makes Meters a type that is not interchangeable
with int or other typedefs of int. The constructor Meters(val) wraps
the base value, and function dispatch enforces the type distinction.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Address review: add to_underlying function for strong typedefs

Registers a to_underlying() function for each strong typedef that
returns the wrapped base value from the Dynamic_Object's __value attr.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Address review: expose strongly-typed operators for strong typedefs

Register forwarding binary operators at typedef creation time via a
custom Proxy_Function_Base subclass (Strong_Typedef_Binary_Op). Each
operator unwraps __value from both operands, dispatches on the
underlying types, and re-wraps arithmetic results in the typedef.
Comparison operators return the raw bool.

- Arithmetic: +, -, *, /, % → Meters + Meters -> Meters
- Comparison: <, >, <=, >=, ==, != → Meters < Meters -> bool
- Operators that don't exist on the base type error at call time
  (e.g. StrongString * StrongString -> error)
- Users can extend typedefs with their own operations using
  to_underlying() for unwrapping

Tests cover int-based arithmetic, string-based concatenation, string
multiplication error, comparison ops, type safety of results, and
user-defined operator extensions.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Address review: conditionally register operators based on underlying type support

Only register strong typedef operators that actually exist for the
underlying type. Previously all operators were added unconditionally,
causing confusing reflection entries (e.g. * for StrongString) that
would fail at runtime. Now each operator is probed via call_match
against default-constructed base type values before registration.

Also adds bitwise/shift operators (&, |, ^, <<, >>) for types that
support them, and expands test coverage for unsupported operator
rejection.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Address review: register all operators unconditionally and add compound assignment operators

Remove conditional operator registration (op_exists_for_base_type check)
since users could add underlying operators later, and the runtime check was
expensive. Operators that fail on the underlying type now error at call time
instead of being absent. Add compound assignment operators (*=, +=, -=, /=,
%=, <<=, >>=, &=, |=, ^=) via Strong_Typedef_Compound_Assign_Op which
computes the base operation and stores the result back in __value.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Merge upstream/develop into fix/issue-677-add-strong-typedefs

Resolve merge conflicts with ChaiScript:develop. Upstream added
nested namespace support (#675), grammar railroad diagrams (#673),
and WASM exception support (#689). Conflicts in chaiscript_common.hpp,
chaiscript_eval.hpp, and chaiscript_parser.hpp resolved by keeping
both Using and Namespace_Block AST node types.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Address review: add strong typedef documentation to cheatsheet

Add a new "Strong Typedefs" section to the cheatsheet covering:
- Basic usage with `using Type = BaseType` syntax
- Arithmetic and comparison operator forwarding
- String-based strong typedefs
- Accessing the underlying value via to_underlying
- Extending strong typedefs with custom operations

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

---------

Co-authored-by: leftibot <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature-request: nested namespaces

2 participants