From 6016cb4d7b11f6f13d15c314833bce3e6bb2fe4c Mon Sep 17 00:00:00 2001 From: leftibot Date: Mon, 13 Apr 2026 20:53:14 -0600 Subject: [PATCH 1/3] Fix #13: Add math.* namespace for Lua-style access to math functions Register all math functions under a `math` namespace object using ChaiScript's Dynamic_Object system via Module::eval. Functions remain accessible as bare names (e.g., `cos(0.5)`) for backward compatibility, and are now also accessible as `math.cos(0.5)`, similar to Lua's math library. Also fixes Catch2 SIGSTKSZ compatibility with newer glibc. Co-Authored-By: Claude Opus 4.6 (1M context) --- include/chaiscript/extras/math.hpp | 114 +++++++++++++++++++++++++++++ tests/catch.hpp | 7 +- tests/math.cpp | 31 ++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) diff --git a/include/chaiscript/extras/math.hpp b/include/chaiscript/extras/math.hpp index 4fb1bc6..82b5976 100644 --- a/include/chaiscript/extras/math.hpp +++ b/include/chaiscript/extras/math.hpp @@ -826,6 +826,120 @@ namespace chaiscript { isunordered(m); isunordered(m); + // Create math namespace object + m->eval("def math_type::math_type() {}"); + + // TRIG FUNCTIONS + m->eval("def math_type::cos(x) { return cos(x) }"); + m->eval("def math_type::sin(x) { return sin(x) }"); + m->eval("def math_type::tan(x) { return tan(x) }"); + m->eval("def math_type::acos(x) { return acos(x) }"); + m->eval("def math_type::asin(x) { return asin(x) }"); + m->eval("def math_type::atan(x) { return atan(x) }"); + m->eval("def math_type::atan2(x, y) { return atan2(x, y) }"); + + // HYPERBOLIC FUNCTIONS + m->eval("def math_type::cosh(x) { return cosh(x) }"); + m->eval("def math_type::sinh(x) { return sinh(x) }"); + m->eval("def math_type::tanh(x) { return tanh(x) }"); + +#ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED + m->eval("def math_type::acosh(x) { return acosh(x) }"); + m->eval("def math_type::asinh(x) { return asinh(x) }"); + m->eval("def math_type::atanh(x) { return atanh(x) }"); +#endif + + // EXPONENTIAL AND LOGARITHMIC FUNCTIONS + m->eval("def math_type::exp(x) { return exp(x) }"); + m->eval("def math_type::frexp(x, y) { return frexp(x, y) }"); + m->eval("def math_type::ldexp(x, y) { return ldexp(x, y) }"); + m->eval("def math_type::log(x) { return log(x) }"); + m->eval("def math_type::log10(x) { return log10(x) }"); + m->eval("def math_type::modf(x, y) { return modf(x, y) }"); + +#ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED + m->eval("def math_type::exp2(x) { return exp2(x) }"); + m->eval("def math_type::expm1(x) { return expm1(x) }"); + m->eval("def math_type::ilogb(x) { return ilogb(x) }"); + m->eval("def math_type::log1p(x) { return log1p(x) }"); + m->eval("def math_type::log2(x) { return log2(x) }"); + m->eval("def math_type::logb(x) { return logb(x) }"); + m->eval("def math_type::scalbn(x, y) { return scalbn(x, y) }"); + m->eval("def math_type::scalbln(x, y) { return scalbln(x, y) }"); +#endif + + // POWER FUNCTIONS + m->eval("def math_type::pow(x, y) { return pow(x, y) }"); + m->eval("def math_type::sqrt(x) { return sqrt(x) }"); + +#ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED + m->eval("def math_type::cbrt(x) { return cbrt(x) }"); + m->eval("def math_type::hypot(x, y) { return hypot(x, y) }"); + + // ERROR AND GAMMA FUNCTIONS + m->eval("def math_type::erf(x) { return erf(x) }"); + m->eval("def math_type::erfc(x) { return erfc(x) }"); + m->eval("def math_type::tgamma(x) { return tgamma(x) }"); + m->eval("def math_type::lgamma(x) { return lgamma(x) }"); +#endif + + // ROUNDING AND REMAINDER FUNCTIONS + m->eval("def math_type::ceil(x) { return ceil(x) }"); + m->eval("def math_type::floor(x) { return floor(x) }"); + m->eval("def math_type::fmod(x, y) { return fmod(x, y) }"); + +#ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED + m->eval("def math_type::trunc(x) { return trunc(x) }"); + m->eval("def math_type::round(x) { return round(x) }"); + m->eval("def math_type::lround(x) { return lround(x) }"); + m->eval("def math_type::llround(x) { return llround(x) }"); + m->eval("def math_type::rint(x) { return rint(x) }"); + m->eval("def math_type::lrint(x) { return lrint(x) }"); + m->eval("def math_type::llrint(x) { return llrint(x) }"); + m->eval("def math_type::nearbyint(x) { return nearbyint(x) }"); + m->eval("def math_type::remainder(x, y) { return remainder(x, y) }"); + m->eval("def math_type::remquo(x, y, z) { return remquo(x, y, z) }"); + + // FLOATING-POINT MANIPULATION FUNCTIONS + m->eval("def math_type::copysign(x, y) { return copysign(x, y) }"); + m->eval("def math_type::nan(x) { return nan(x) }"); + m->eval("def math_type::nextafter(x, y) { return nextafter(x, y) }"); + m->eval("def math_type::nexttoward(x, y) { return nexttoward(x, y) }"); + + // MINIMUM, MAXIMUM, DIFFERENCE FUNCTIONS + m->eval("def math_type::fdim(x, y) { return fdim(x, y) }"); + m->eval("def math_type::fmax(x, y) { return fmax(x, y) }"); + m->eval("def math_type::fmin(x, y) { return fmin(x, y) }"); + + // OTHER FUNCTIONS + m->eval("def math_type::fabs(x) { return fabs(x) }"); +#endif + + m->eval("def math_type::abs(x) { return abs(x) }"); + +#ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED + m->eval("def math_type::fma(x, y, z) { return fma(x, y, z) }"); + + // CLASSIFICATION FUNCTIONS + m->eval("def math_type::fpclassify(x) { return fpclassify(x) }"); +#endif + + m->eval("def math_type::isfinite(x) { return isfinite(x) }"); + m->eval("def math_type::isinf(x) { return isinf(x) }"); + m->eval("def math_type::isnan(x) { return isnan(x) }"); + m->eval("def math_type::isnormal(x) { return isnormal(x) }"); + m->eval("def math_type::signbit(x) { return signbit(x) }"); + + // COMPARISON FUNCTIONS + m->eval("def math_type::isgreater(x, y) { return isgreater(x, y) }"); + m->eval("def math_type::isgreaterequal(x, y) { return isgreaterequal(x, y) }"); + m->eval("def math_type::isless(x, y) { return isless(x, y) }"); + m->eval("def math_type::islessequal(x, y) { return islessequal(x, y) }"); + m->eval("def math_type::islessgreater(x, y) { return islessgreater(x, y) }"); + m->eval("def math_type::isunordered(x, y) { return isunordered(x, y) }"); + + m->eval("global math = math_type()"); + return m; } } diff --git a/tests/catch.hpp b/tests/catch.hpp index 362f869..1851bec 100644 --- a/tests/catch.hpp +++ b/tests/catch.hpp @@ -6462,7 +6462,8 @@ namespace Catch { static bool isSet; static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; static stack_t oldSigStack; - static char altStackMem[]; + static constexpr std::size_t altStackSize = 32768; + static char altStackMem[altStackSize]; static void handleSignal( int sig ); @@ -6597,7 +6598,7 @@ namespace Catch { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; + sigStack.ss_size = altStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = { }; @@ -6628,7 +6629,7 @@ namespace Catch { bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + char FatalConditionHandler::altStackMem[FatalConditionHandler::altStackSize] = {}; } // namespace Catch diff --git a/tests/math.cpp b/tests/math.cpp index 63eafcd..47054dc 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -8,6 +8,37 @@ #include +TEST_CASE( "Math namespace works", "[math]" ) { + auto mathlib = chaiscript::extras::math::bootstrap(); + + auto stdlib = chaiscript::Std_Lib::library(); + chaiscript::ChaiScript chai(stdlib); + chai.add(mathlib); + + // Trig + CHECK(chai.eval("math.cos(0.5)") == std::cos(0.5)); + CHECK(chai.eval("math.sin(0.5)") == std::sin(0.5)); + CHECK(chai.eval("math.tan(0.5)") == std::tan(0.5)); + CHECK(chai.eval("math.acos(0.5)") == std::acos(0.5)); + CHECK(chai.eval("math.asin(0.5)") == std::asin(0.5)); + CHECK(chai.eval("math.atan(0.5)") == std::atan(0.5)); + CHECK(chai.eval("math.atan2(0.5, 0.5)") == std::atan2(0.5, 0.5)); + + // Power + CHECK(chai.eval("math.pow(0.5, 3.0)") == std::pow(0.5, 3.0)); + CHECK(chai.eval("math.sqrt(0.5)") == std::sqrt(0.5)); + + // Rounding + CHECK(chai.eval("math.ceil(0.5)") == std::ceil(0.5)); + CHECK(chai.eval("math.floor(0.5)") == std::floor(0.5)); + CHECK(chai.eval("math.abs(-0.5)") == std::abs(-0.5)); + + // Exponential + CHECK(chai.eval("math.exp(0.5)") == std::exp(0.5)); + CHECK(chai.eval("math.log(0.5)") == std::log(0.5)); + CHECK(chai.eval("math.log10(0.5)") == std::log10(0.5)); +} + TEST_CASE( "Math functions work", "[math]" ) { auto mathlib = chaiscript::extras::math::bootstrap(); From 23008f2b5f9a3b0f1ee19c448b477cb4dd891609 Mon Sep 17 00:00:00 2001 From: leftibot Date: Tue, 14 Apr 2026 15:48:54 -0600 Subject: [PATCH 2/3] Address review: Use ChaiScript namespaces instead of global math object Upgrade ChaiScript from 5.8.6 to 6.1.0 to use its native namespace support. Replace the Dynamic_Object/math_type class hack with proper namespace("math") + function assignment, which is the idiomatic way to create namespaces in ChaiScript 6.x. Update test files for the 6.x ChaiScript constructor API (stdlib is now included by default). Requested by @lefticus in PR #30 review. Co-Authored-By: Claude Opus 4.6 (1M context) --- cmake/chaiscript.cmake | 2 +- include/chaiscript/extras/math.hpp | 146 ++++++++++++++--------------- tests/math.cpp | 6 +- tests/string_methods.cpp | 3 +- 4 files changed, 76 insertions(+), 81 deletions(-) diff --git a/cmake/chaiscript.cmake b/cmake/chaiscript.cmake index 5fbdf1c..1d00c2e 100644 --- a/cmake/chaiscript.cmake +++ b/cmake/chaiscript.cmake @@ -1,4 +1,4 @@ -set(CHAISCRIPT_VERSION 5.8.6) +set(CHAISCRIPT_VERSION 6.1.0) find_package(chaiscript ${CHAISCRIPT_VERSION} QUIET) if (NOT chaiscript_FOUND) diff --git a/include/chaiscript/extras/math.hpp b/include/chaiscript/extras/math.hpp index 82b5976..82495d1 100644 --- a/include/chaiscript/extras/math.hpp +++ b/include/chaiscript/extras/math.hpp @@ -826,119 +826,117 @@ namespace chaiscript { isunordered(m); isunordered(m); - // Create math namespace object - m->eval("def math_type::math_type() {}"); + // Create math namespace + m->eval("namespace(\"math\")"); // TRIG FUNCTIONS - m->eval("def math_type::cos(x) { return cos(x) }"); - m->eval("def math_type::sin(x) { return sin(x) }"); - m->eval("def math_type::tan(x) { return tan(x) }"); - m->eval("def math_type::acos(x) { return acos(x) }"); - m->eval("def math_type::asin(x) { return asin(x) }"); - m->eval("def math_type::atan(x) { return atan(x) }"); - m->eval("def math_type::atan2(x, y) { return atan2(x, y) }"); + m->eval("math.cos = fun(x) { return cos(x) }"); + m->eval("math.sin = fun(x) { return sin(x) }"); + m->eval("math.tan = fun(x) { return tan(x) }"); + m->eval("math.acos = fun(x) { return acos(x) }"); + m->eval("math.asin = fun(x) { return asin(x) }"); + m->eval("math.atan = fun(x) { return atan(x) }"); + m->eval("math.atan2 = fun(x, y) { return atan2(x, y) }"); // HYPERBOLIC FUNCTIONS - m->eval("def math_type::cosh(x) { return cosh(x) }"); - m->eval("def math_type::sinh(x) { return sinh(x) }"); - m->eval("def math_type::tanh(x) { return tanh(x) }"); + m->eval("math.cosh = fun(x) { return cosh(x) }"); + m->eval("math.sinh = fun(x) { return sinh(x) }"); + m->eval("math.tanh = fun(x) { return tanh(x) }"); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("def math_type::acosh(x) { return acosh(x) }"); - m->eval("def math_type::asinh(x) { return asinh(x) }"); - m->eval("def math_type::atanh(x) { return atanh(x) }"); + m->eval("math.acosh = fun(x) { return acosh(x) }"); + m->eval("math.asinh = fun(x) { return asinh(x) }"); + m->eval("math.atanh = fun(x) { return atanh(x) }"); #endif // EXPONENTIAL AND LOGARITHMIC FUNCTIONS - m->eval("def math_type::exp(x) { return exp(x) }"); - m->eval("def math_type::frexp(x, y) { return frexp(x, y) }"); - m->eval("def math_type::ldexp(x, y) { return ldexp(x, y) }"); - m->eval("def math_type::log(x) { return log(x) }"); - m->eval("def math_type::log10(x) { return log10(x) }"); - m->eval("def math_type::modf(x, y) { return modf(x, y) }"); + m->eval("math.exp = fun(x) { return exp(x) }"); + m->eval("math.frexp = fun(x, y) { return frexp(x, y) }"); + m->eval("math.ldexp = fun(x, y) { return ldexp(x, y) }"); + m->eval("math.log = fun(x) { return log(x) }"); + m->eval("math.log10 = fun(x) { return log10(x) }"); + m->eval("math.modf = fun(x, y) { return modf(x, y) }"); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("def math_type::exp2(x) { return exp2(x) }"); - m->eval("def math_type::expm1(x) { return expm1(x) }"); - m->eval("def math_type::ilogb(x) { return ilogb(x) }"); - m->eval("def math_type::log1p(x) { return log1p(x) }"); - m->eval("def math_type::log2(x) { return log2(x) }"); - m->eval("def math_type::logb(x) { return logb(x) }"); - m->eval("def math_type::scalbn(x, y) { return scalbn(x, y) }"); - m->eval("def math_type::scalbln(x, y) { return scalbln(x, y) }"); + m->eval("math.exp2 = fun(x) { return exp2(x) }"); + m->eval("math.expm1 = fun(x) { return expm1(x) }"); + m->eval("math.ilogb = fun(x) { return ilogb(x) }"); + m->eval("math.log1p = fun(x) { return log1p(x) }"); + m->eval("math.log2 = fun(x) { return log2(x) }"); + m->eval("math.logb = fun(x) { return logb(x) }"); + m->eval("math.scalbn = fun(x, y) { return scalbn(x, y) }"); + m->eval("math.scalbln = fun(x, y) { return scalbln(x, y) }"); #endif // POWER FUNCTIONS - m->eval("def math_type::pow(x, y) { return pow(x, y) }"); - m->eval("def math_type::sqrt(x) { return sqrt(x) }"); + m->eval("math.pow = fun(x, y) { return pow(x, y) }"); + m->eval("math.sqrt = fun(x) { return sqrt(x) }"); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("def math_type::cbrt(x) { return cbrt(x) }"); - m->eval("def math_type::hypot(x, y) { return hypot(x, y) }"); + m->eval("math.cbrt = fun(x) { return cbrt(x) }"); + m->eval("math.hypot = fun(x, y) { return hypot(x, y) }"); // ERROR AND GAMMA FUNCTIONS - m->eval("def math_type::erf(x) { return erf(x) }"); - m->eval("def math_type::erfc(x) { return erfc(x) }"); - m->eval("def math_type::tgamma(x) { return tgamma(x) }"); - m->eval("def math_type::lgamma(x) { return lgamma(x) }"); + m->eval("math.erf = fun(x) { return erf(x) }"); + m->eval("math.erfc = fun(x) { return erfc(x) }"); + m->eval("math.tgamma = fun(x) { return tgamma(x) }"); + m->eval("math.lgamma = fun(x) { return lgamma(x) }"); #endif // ROUNDING AND REMAINDER FUNCTIONS - m->eval("def math_type::ceil(x) { return ceil(x) }"); - m->eval("def math_type::floor(x) { return floor(x) }"); - m->eval("def math_type::fmod(x, y) { return fmod(x, y) }"); + m->eval("math.ceil = fun(x) { return ceil(x) }"); + m->eval("math.floor = fun(x) { return floor(x) }"); + m->eval("math.fmod = fun(x, y) { return fmod(x, y) }"); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("def math_type::trunc(x) { return trunc(x) }"); - m->eval("def math_type::round(x) { return round(x) }"); - m->eval("def math_type::lround(x) { return lround(x) }"); - m->eval("def math_type::llround(x) { return llround(x) }"); - m->eval("def math_type::rint(x) { return rint(x) }"); - m->eval("def math_type::lrint(x) { return lrint(x) }"); - m->eval("def math_type::llrint(x) { return llrint(x) }"); - m->eval("def math_type::nearbyint(x) { return nearbyint(x) }"); - m->eval("def math_type::remainder(x, y) { return remainder(x, y) }"); - m->eval("def math_type::remquo(x, y, z) { return remquo(x, y, z) }"); + m->eval("math.trunc = fun(x) { return trunc(x) }"); + m->eval("math.round = fun(x) { return round(x) }"); + m->eval("math.lround = fun(x) { return lround(x) }"); + m->eval("math.llround = fun(x) { return llround(x) }"); + m->eval("math.rint = fun(x) { return rint(x) }"); + m->eval("math.lrint = fun(x) { return lrint(x) }"); + m->eval("math.llrint = fun(x) { return llrint(x) }"); + m->eval("math.nearbyint = fun(x) { return nearbyint(x) }"); + m->eval("math.remainder = fun(x, y) { return remainder(x, y) }"); + m->eval("math.remquo = fun(x, y, z) { return remquo(x, y, z) }"); // FLOATING-POINT MANIPULATION FUNCTIONS - m->eval("def math_type::copysign(x, y) { return copysign(x, y) }"); - m->eval("def math_type::nan(x) { return nan(x) }"); - m->eval("def math_type::nextafter(x, y) { return nextafter(x, y) }"); - m->eval("def math_type::nexttoward(x, y) { return nexttoward(x, y) }"); + m->eval("math.copysign = fun(x, y) { return copysign(x, y) }"); + m->eval("math.nan = fun(x) { return nan(x) }"); + m->eval("math.nextafter = fun(x, y) { return nextafter(x, y) }"); + m->eval("math.nexttoward = fun(x, y) { return nexttoward(x, y) }"); // MINIMUM, MAXIMUM, DIFFERENCE FUNCTIONS - m->eval("def math_type::fdim(x, y) { return fdim(x, y) }"); - m->eval("def math_type::fmax(x, y) { return fmax(x, y) }"); - m->eval("def math_type::fmin(x, y) { return fmin(x, y) }"); + m->eval("math.fdim = fun(x, y) { return fdim(x, y) }"); + m->eval("math.fmax = fun(x, y) { return fmax(x, y) }"); + m->eval("math.fmin = fun(x, y) { return fmin(x, y) }"); // OTHER FUNCTIONS - m->eval("def math_type::fabs(x) { return fabs(x) }"); + m->eval("math.fabs = fun(x) { return fabs(x) }"); #endif - m->eval("def math_type::abs(x) { return abs(x) }"); + m->eval("math.abs = fun(x) { return abs(x) }"); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("def math_type::fma(x, y, z) { return fma(x, y, z) }"); + m->eval("math.fma = fun(x, y, z) { return fma(x, y, z) }"); // CLASSIFICATION FUNCTIONS - m->eval("def math_type::fpclassify(x) { return fpclassify(x) }"); + m->eval("math.fpclassify = fun(x) { return fpclassify(x) }"); #endif - m->eval("def math_type::isfinite(x) { return isfinite(x) }"); - m->eval("def math_type::isinf(x) { return isinf(x) }"); - m->eval("def math_type::isnan(x) { return isnan(x) }"); - m->eval("def math_type::isnormal(x) { return isnormal(x) }"); - m->eval("def math_type::signbit(x) { return signbit(x) }"); + m->eval("math.isfinite = fun(x) { return isfinite(x) }"); + m->eval("math.isinf = fun(x) { return isinf(x) }"); + m->eval("math.isnan = fun(x) { return isnan(x) }"); + m->eval("math.isnormal = fun(x) { return isnormal(x) }"); + m->eval("math.signbit = fun(x) { return signbit(x) }"); // COMPARISON FUNCTIONS - m->eval("def math_type::isgreater(x, y) { return isgreater(x, y) }"); - m->eval("def math_type::isgreaterequal(x, y) { return isgreaterequal(x, y) }"); - m->eval("def math_type::isless(x, y) { return isless(x, y) }"); - m->eval("def math_type::islessequal(x, y) { return islessequal(x, y) }"); - m->eval("def math_type::islessgreater(x, y) { return islessgreater(x, y) }"); - m->eval("def math_type::isunordered(x, y) { return isunordered(x, y) }"); - - m->eval("global math = math_type()"); + m->eval("math.isgreater = fun(x, y) { return isgreater(x, y) }"); + m->eval("math.isgreaterequal = fun(x, y) { return isgreaterequal(x, y) }"); + m->eval("math.isless = fun(x, y) { return isless(x, y) }"); + m->eval("math.islessequal = fun(x, y) { return islessequal(x, y) }"); + m->eval("math.islessgreater = fun(x, y) { return islessgreater(x, y) }"); + m->eval("math.isunordered = fun(x, y) { return isunordered(x, y) }"); return m; } diff --git a/tests/math.cpp b/tests/math.cpp index 47054dc..03097e7 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -11,8 +11,7 @@ TEST_CASE( "Math namespace works", "[math]" ) { auto mathlib = chaiscript::extras::math::bootstrap(); - auto stdlib = chaiscript::Std_Lib::library(); - chaiscript::ChaiScript chai(stdlib); + chaiscript::ChaiScript chai; chai.add(mathlib); // Trig @@ -42,8 +41,7 @@ TEST_CASE( "Math namespace works", "[math]" ) { TEST_CASE( "Math functions work", "[math]" ) { auto mathlib = chaiscript::extras::math::bootstrap(); - auto stdlib = chaiscript::Std_Lib::library(); - chaiscript::ChaiScript chai(stdlib); + chaiscript::ChaiScript chai; chai.add(mathlib); // TRIG FUNCTIONS diff --git a/tests/string_methods.cpp b/tests/string_methods.cpp index 07efc3f..d93b1ee 100644 --- a/tests/string_methods.cpp +++ b/tests/string_methods.cpp @@ -8,8 +8,7 @@ TEST_CASE( "string_methods functions work", "[string_methods]" ) { // Create the ChaiScript environment with stdlib available. - auto stdlib = chaiscript::Std_Lib::library(); - chaiscript::ChaiScript chai(stdlib); + chaiscript::ChaiScript chai; // Add the string_methods module. auto stringmethods = chaiscript::extras::string_methods::bootstrap(); From 022c611e10f06f04f5baf92cb23f6d7a2dff8ff5 Mon Sep 17 00:00:00 2001 From: leftibot Date: Thu, 16 Apr 2026 01:48:12 -0600 Subject: [PATCH 3/3] Address review: Use register_namespace for real math namespace Replace the script-level namespace("math") + lambda-wrapping hack with chai.register_namespace(), ChaiScript's native namespace API. Functions are now attached directly as Namespace attributes instead of being wrapped in ChaiScript functions that forward to globals. The namespace is registered lazily so its contents are only built when import("math") is called. Requested by @lefticus in PR #30 review. Co-Authored-By: Claude Opus 4.6 (1M context) --- include/chaiscript/extras/math.hpp | 185 +++++++++++++++-------------- tests/math.cpp | 5 +- 2 files changed, 96 insertions(+), 94 deletions(-) diff --git a/include/chaiscript/extras/math.hpp b/include/chaiscript/extras/math.hpp index 82495d1..6e259bf 100644 --- a/include/chaiscript/extras/math.hpp +++ b/include/chaiscript/extras/math.hpp @@ -826,119 +826,122 @@ namespace chaiscript { isunordered(m); isunordered(m); - // Create math namespace - m->eval("namespace(\"math\")"); - - // TRIG FUNCTIONS - m->eval("math.cos = fun(x) { return cos(x) }"); - m->eval("math.sin = fun(x) { return sin(x) }"); - m->eval("math.tan = fun(x) { return tan(x) }"); - m->eval("math.acos = fun(x) { return acos(x) }"); - m->eval("math.asin = fun(x) { return asin(x) }"); - m->eval("math.atan = fun(x) { return atan(x) }"); - m->eval("math.atan2 = fun(x, y) { return atan2(x, y) }"); + return m; + } - // HYPERBOLIC FUNCTIONS - m->eval("math.cosh = fun(x) { return cosh(x) }"); - m->eval("math.sinh = fun(x) { return sinh(x) }"); - m->eval("math.tanh = fun(x) { return tanh(x) }"); + /// \brief Registers a "math" namespace on the given ChaiScript engine, + /// exposing the standard C++ math functions as attributes of + /// the namespace (e.g. \c math.cos(x), \c math.sqrt(x)). + /// + /// Uses ChaiScript's native \c register_namespace API so that the + /// functions live inside a real namespace object rather than being + /// attached to a global variable. The namespace is lazily populated + /// when it is first imported. + inline void bootstrap_namespace(chaiscript::ChaiScript &chai) + { + chai.register_namespace([](chaiscript::Namespace &math) { + // TRIG FUNCTIONS + math["cos"] = chaiscript::var(chaiscript::fun([](double p){ return std::cos(p); })); + math["sin"] = chaiscript::var(chaiscript::fun([](double p){ return std::sin(p); })); + math["tan"] = chaiscript::var(chaiscript::fun([](double p){ return std::tan(p); })); + math["acos"] = chaiscript::var(chaiscript::fun([](double p){ return std::acos(p); })); + math["asin"] = chaiscript::var(chaiscript::fun([](double p){ return std::asin(p); })); + math["atan"] = chaiscript::var(chaiscript::fun([](double p){ return std::atan(p); })); + math["atan2"] = chaiscript::var(chaiscript::fun([](double y, double x){ return std::atan2(y, x); })); + + // HYPERBOLIC FUNCTIONS + math["cosh"] = chaiscript::var(chaiscript::fun([](double p){ return std::cosh(p); })); + math["sinh"] = chaiscript::var(chaiscript::fun([](double p){ return std::sinh(p); })); + math["tanh"] = chaiscript::var(chaiscript::fun([](double p){ return std::tanh(p); })); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("math.acosh = fun(x) { return acosh(x) }"); - m->eval("math.asinh = fun(x) { return asinh(x) }"); - m->eval("math.atanh = fun(x) { return atanh(x) }"); + math["acosh"] = chaiscript::var(chaiscript::fun([](double p){ return std::acosh(p); })); + math["asinh"] = chaiscript::var(chaiscript::fun([](double p){ return std::asinh(p); })); + math["atanh"] = chaiscript::var(chaiscript::fun([](double p){ return std::atanh(p); })); #endif - // EXPONENTIAL AND LOGARITHMIC FUNCTIONS - m->eval("math.exp = fun(x) { return exp(x) }"); - m->eval("math.frexp = fun(x, y) { return frexp(x, y) }"); - m->eval("math.ldexp = fun(x, y) { return ldexp(x, y) }"); - m->eval("math.log = fun(x) { return log(x) }"); - m->eval("math.log10 = fun(x) { return log10(x) }"); - m->eval("math.modf = fun(x, y) { return modf(x, y) }"); + // EXPONENTIAL AND LOGARITHMIC FUNCTIONS + math["exp"] = chaiscript::var(chaiscript::fun([](double p){ return std::exp(p); })); + math["log"] = chaiscript::var(chaiscript::fun([](double p){ return std::log(p); })); + math["log10"] = chaiscript::var(chaiscript::fun([](double p){ return std::log10(p); })); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("math.exp2 = fun(x) { return exp2(x) }"); - m->eval("math.expm1 = fun(x) { return expm1(x) }"); - m->eval("math.ilogb = fun(x) { return ilogb(x) }"); - m->eval("math.log1p = fun(x) { return log1p(x) }"); - m->eval("math.log2 = fun(x) { return log2(x) }"); - m->eval("math.logb = fun(x) { return logb(x) }"); - m->eval("math.scalbn = fun(x, y) { return scalbn(x, y) }"); - m->eval("math.scalbln = fun(x, y) { return scalbln(x, y) }"); + math["exp2"] = chaiscript::var(chaiscript::fun([](double p){ return std::exp2(p); })); + math["expm1"] = chaiscript::var(chaiscript::fun([](double p){ return std::expm1(p); })); + math["ilogb"] = chaiscript::var(chaiscript::fun([](double p){ return std::ilogb(p); })); + math["log1p"] = chaiscript::var(chaiscript::fun([](double p){ return std::log1p(p); })); + math["log2"] = chaiscript::var(chaiscript::fun([](double p){ return std::log2(p); })); + math["logb"] = chaiscript::var(chaiscript::fun([](double p){ return std::logb(p); })); #endif - // POWER FUNCTIONS - m->eval("math.pow = fun(x, y) { return pow(x, y) }"); - m->eval("math.sqrt = fun(x) { return sqrt(x) }"); + // POWER FUNCTIONS + math["pow"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::pow(x, y); })); + math["sqrt"] = chaiscript::var(chaiscript::fun([](double p){ return std::sqrt(p); })); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("math.cbrt = fun(x) { return cbrt(x) }"); - m->eval("math.hypot = fun(x, y) { return hypot(x, y) }"); - - // ERROR AND GAMMA FUNCTIONS - m->eval("math.erf = fun(x) { return erf(x) }"); - m->eval("math.erfc = fun(x) { return erfc(x) }"); - m->eval("math.tgamma = fun(x) { return tgamma(x) }"); - m->eval("math.lgamma = fun(x) { return lgamma(x) }"); + math["cbrt"] = chaiscript::var(chaiscript::fun([](double p){ return std::cbrt(p); })); + math["hypot"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::hypot(x, y); })); + + // ERROR AND GAMMA FUNCTIONS + math["erf"] = chaiscript::var(chaiscript::fun([](double p){ return std::erf(p); })); + math["erfc"] = chaiscript::var(chaiscript::fun([](double p){ return std::erfc(p); })); + math["tgamma"] = chaiscript::var(chaiscript::fun([](double p){ return std::tgamma(p); })); + math["lgamma"] = chaiscript::var(chaiscript::fun([](double p){ return std::lgamma(p); })); #endif - // ROUNDING AND REMAINDER FUNCTIONS - m->eval("math.ceil = fun(x) { return ceil(x) }"); - m->eval("math.floor = fun(x) { return floor(x) }"); - m->eval("math.fmod = fun(x, y) { return fmod(x, y) }"); + // ROUNDING AND REMAINDER FUNCTIONS + math["ceil"] = chaiscript::var(chaiscript::fun([](double p){ return std::ceil(p); })); + math["floor"] = chaiscript::var(chaiscript::fun([](double p){ return std::floor(p); })); + math["fmod"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::fmod(x, y); })); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("math.trunc = fun(x) { return trunc(x) }"); - m->eval("math.round = fun(x) { return round(x) }"); - m->eval("math.lround = fun(x) { return lround(x) }"); - m->eval("math.llround = fun(x) { return llround(x) }"); - m->eval("math.rint = fun(x) { return rint(x) }"); - m->eval("math.lrint = fun(x) { return lrint(x) }"); - m->eval("math.llrint = fun(x) { return llrint(x) }"); - m->eval("math.nearbyint = fun(x) { return nearbyint(x) }"); - m->eval("math.remainder = fun(x, y) { return remainder(x, y) }"); - m->eval("math.remquo = fun(x, y, z) { return remquo(x, y, z) }"); - - // FLOATING-POINT MANIPULATION FUNCTIONS - m->eval("math.copysign = fun(x, y) { return copysign(x, y) }"); - m->eval("math.nan = fun(x) { return nan(x) }"); - m->eval("math.nextafter = fun(x, y) { return nextafter(x, y) }"); - m->eval("math.nexttoward = fun(x, y) { return nexttoward(x, y) }"); - - // MINIMUM, MAXIMUM, DIFFERENCE FUNCTIONS - m->eval("math.fdim = fun(x, y) { return fdim(x, y) }"); - m->eval("math.fmax = fun(x, y) { return fmax(x, y) }"); - m->eval("math.fmin = fun(x, y) { return fmin(x, y) }"); - - // OTHER FUNCTIONS - m->eval("math.fabs = fun(x) { return fabs(x) }"); + math["trunc"] = chaiscript::var(chaiscript::fun([](double p){ return std::trunc(p); })); + math["round"] = chaiscript::var(chaiscript::fun([](double p){ return std::round(p); })); + math["lround"] = chaiscript::var(chaiscript::fun([](double p){ return std::lround(p); })); + math["llround"] = chaiscript::var(chaiscript::fun([](double p){ return std::llround(p); })); + math["rint"] = chaiscript::var(chaiscript::fun([](double p){ return std::rint(p); })); + math["lrint"] = chaiscript::var(chaiscript::fun([](double p){ return std::lrint(p); })); + math["llrint"] = chaiscript::var(chaiscript::fun([](double p){ return std::llrint(p); })); + math["nearbyint"] = chaiscript::var(chaiscript::fun([](double p){ return std::nearbyint(p); })); + math["remainder"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::remainder(x, y); })); + + // FLOATING-POINT MANIPULATION FUNCTIONS + math["copysign"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::copysign(x, y); })); + math["nextafter"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::nextafter(x, y); })); + math["nexttoward"] = chaiscript::var(chaiscript::fun([](double x, long double y){ return std::nexttoward(x, y); })); + + // MINIMUM, MAXIMUM, DIFFERENCE FUNCTIONS + math["fdim"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::fdim(x, y); })); + math["fmax"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::fmax(x, y); })); + math["fmin"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::fmin(x, y); })); + + // OTHER FUNCTIONS + math["fabs"] = chaiscript::var(chaiscript::fun([](double p){ return std::fabs(p); })); #endif - m->eval("math.abs = fun(x) { return abs(x) }"); + math["abs"] = chaiscript::var(chaiscript::fun([](double p){ return std::abs(p); })); #ifndef CHAISCRIPT_EXTRAS_MATH_SKIP_ADVANCED - m->eval("math.fma = fun(x, y, z) { return fma(x, y, z) }"); + math["fma"] = chaiscript::var(chaiscript::fun([](double x, double y, double z){ return std::fma(x, y, z); })); - // CLASSIFICATION FUNCTIONS - m->eval("math.fpclassify = fun(x) { return fpclassify(x) }"); + // CLASSIFICATION FUNCTIONS + math["fpclassify"] = chaiscript::var(chaiscript::fun([](double p){ return std::fpclassify(p); })); #endif - m->eval("math.isfinite = fun(x) { return isfinite(x) }"); - m->eval("math.isinf = fun(x) { return isinf(x) }"); - m->eval("math.isnan = fun(x) { return isnan(x) }"); - m->eval("math.isnormal = fun(x) { return isnormal(x) }"); - m->eval("math.signbit = fun(x) { return signbit(x) }"); - - // COMPARISON FUNCTIONS - m->eval("math.isgreater = fun(x, y) { return isgreater(x, y) }"); - m->eval("math.isgreaterequal = fun(x, y) { return isgreaterequal(x, y) }"); - m->eval("math.isless = fun(x, y) { return isless(x, y) }"); - m->eval("math.islessequal = fun(x, y) { return islessequal(x, y) }"); - m->eval("math.islessgreater = fun(x, y) { return islessgreater(x, y) }"); - m->eval("math.isunordered = fun(x, y) { return isunordered(x, y) }"); - - return m; + math["isfinite"] = chaiscript::var(chaiscript::fun([](double p){ return std::isfinite(p); })); + math["isinf"] = chaiscript::var(chaiscript::fun([](double p){ return std::isinf(p); })); + math["isnan"] = chaiscript::var(chaiscript::fun([](double p){ return std::isnan(p); })); + math["isnormal"] = chaiscript::var(chaiscript::fun([](double p){ return std::isnormal(p); })); + math["signbit"] = chaiscript::var(chaiscript::fun([](double p){ return std::signbit(p); })); + + // COMPARISON FUNCTIONS + math["isgreater"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::isgreater(x, y); })); + math["isgreaterequal"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::isgreaterequal(x, y); })); + math["isless"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::isless(x, y); })); + math["islessequal"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::islessequal(x, y); })); + math["islessgreater"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::islessgreater(x, y); })); + math["isunordered"] = chaiscript::var(chaiscript::fun([](double x, double y){ return std::isunordered(x, y); })); + }, "math"); } } } diff --git a/tests/math.cpp b/tests/math.cpp index 03097e7..69cc7d9 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -9,10 +9,9 @@ #include TEST_CASE( "Math namespace works", "[math]" ) { - auto mathlib = chaiscript::extras::math::bootstrap(); - chaiscript::ChaiScript chai; - chai.add(mathlib); + chaiscript::extras::math::bootstrap_namespace(chai); + chai.eval(R"(import("math"))"); // Trig CHECK(chai.eval("math.cos(0.5)") == std::cos(0.5));