ChaiScript/unittests/nested_namespaces.chai
leftibot 9ff56426e0
Fix #552: Feature-request: nested namespaces (#675)
* 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) <noreply@anthropic.com>

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

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 #675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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

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 #675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: extract shared make_proxy_function from Def_AST_Node

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 #675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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

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 #675 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: remove -j parameter from unix builds

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 #675 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>
2026-04-14 11:49:00 -06:00

56 lines
1.1 KiB
ChaiScript

// Test C++-style block namespace declarations
namespace constants::si {
def mu_B() { return 1.0 }
}
namespace constants::mm {
def mu_B() { return 2.0 }
}
assert_equal(1.0, constants::si::mu_B())
assert_equal(2.0, constants::mm::mu_B())
// Test deeper nesting with block syntax
namespace a::b::c {
def val() { return 42 }
}
assert_equal(42, a::b::c::val())
// Test reopening a namespace to add more members
namespace math {
def square(x) { x * x }
}
namespace math::trig {
def double_angle(x) { 2.0 * x }
}
assert_equal(16, math::square(4))
assert_equal(6.0, math::trig::double_angle(3.0))
// Test reopening a namespace (C++ allows this)
namespace math {
def cube(x) { x * x * x }
}
assert_equal(27, math::cube(3))
// Test that :: scope resolution works the same as . for access
assert_equal(16, math.square(4))
assert_equal(6.0, math.trig.double_angle(3.0))
// Test namespace with var declarations
namespace config {
var pi = 3.14159
var name = "test"
}
assert_equal(3.14159, config::pi)
assert_equal("test", config::name)
// Test function-call style still works
namespace("compat")
compat.legacy = 99
assert_equal(99, compat::legacy)