Lines
100 %
Functions
Branches
//! ADR-0029 colon-namespace resolution at the compile/eval boundary.
//!
//! Reader-level grammar is covered by `reader_tests.rs`; these tests pin the
//! *resolution* contract: a namespaced `defun` defines + resolves under its
//! canonical `NS:NAME` key, resolution is strict-lexical (no ambient current
//! namespace, no cross-namespace fallback), and qualified self-recursion works.
use super::common::{compile_and_validate, compile_expect_error, wrap_with_runtime_i32};
#[test]
fn namespaced_defun_defines_and_resolves() {
// Define `finance:double` and call it qualified — resolves to the
// canonical FINANCE:DOUBLE key.
compile_and_validate("(defun finance:double (x) (* x 2)) (finance:double 21)");
}
fn double_colon_call_resolves_to_same_binding() {
// `finance::double` folds to the same FINANCE:DOUBLE key as the single
// colon used to define it.
compile_and_validate("(defun finance:double (x) (* x 2)) (finance::double 21)");
fn qualified_self_recursion_resolves() {
// A namespaced fn recurses only when it calls itself QUALIFIED — bare `f`
// would resolve to a global `F`, not `NS:F`. The arg is a RUNTIME index
// (`IDX`), so the call can't const-fold and must lower through the
// monomorph runtime-call path, which keys recursion on the canonical name.
compile_and_validate(&wrap_with_runtime_i32(
"(defun ns:countdown (n) (if (= n 0) 0 (ns:countdown (- n 1)))) (ns:countdown IDX)",
));
fn unqualified_body_symbol_does_not_resolve_into_namespace() {
// Inside `(defun ns:f ...)`, a bare `helper` is global, NOT `ns:helper`.
// Only `ns:helper` is defined, so the unqualified call is undefined.
let err = compile_expect_error("(defun ns:helper (x) x) (defun ns:f (x) (helper x)) (ns:f 1)");
let lower = err.to_lowercase();
assert!(
lower.contains("undefined") && lower.contains("helper"),
"expected 'Undefined symbol: HELPER' for the bare helper, got: {err}"
);
fn bare_self_call_in_namespaced_defun_is_not_the_namespaced_fn() {
// Bare `countdown` inside `(defun ns:countdown ...)` resolves to global
// COUNTDOWN (undefined here), proving no implicit self-qualification.
let err = compile_expect_error(
"(defun ns:countdown (n) (if (= n 0) 0 (countdown (- n 1)))) (ns:countdown 3)",
lower.contains("undefined") && lower.contains("countdown"),
"expected 'Undefined symbol: COUNTDOWN' for the bare self-call, got: {err}"
fn universal_prelude_helpers_are_available() {
// ADR-0029: the universal prelude (math:* / list:*) is auto-loaded into
// every table, so a script can call its helpers without defining them.
compile_and_validate("(math:square 7)");
compile_and_validate("(list:second (list 1 2 3))");
fn namespaced_and_global_same_base_coexist() {
// A namespaced `m:inc` and a global `inc` are distinct bindings; each
// resolves to its own definition.
compile_and_validate(
"(defun inc (x) (+ x 1)) (defun m:inc (x) (+ x 100)) (+ (inc 1) (m:inc 1))",