From d6b29991eb48f4cb6c9d54e10fbd1a5697fade8e Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Fri, 14 Aug 2026 23:09:25 -0700 Subject: [PATCH] Report 2026.01.01, and take version_num()'s optional vector Two fixes to the version builtins: version()/version_num() reported 2025.01.01, a release that never existed. The OpenSCAD release to track is 2026.01.01 (tag openscad-2026.01.01-TEST2, the first tagged build carrying hex literals and the object() function), so version() is [2026, 1, 1] and version_num() is 20260101. version_num() also ignored the optional vector argument the reference accepts, always answering with our own release: version_num([2019, 5, 0]) returned 20250101 instead of 20190500. It now folds whatever it is handed, via the reference's own y * 10000 + m * 100 + d, with getVec3's 2-element form defaulting the day to 0. A non-list, a wrong length, or a non-numeric element is undef. One deliberate divergence, commented at the call site: the reference's size-2 path ignores getVec2's own failure and folds uninitialized doubles, so real OpenSCAD answers version_num(["a", "b"]) with garbage (measured: 3.81052e+286). We return undef there instead. Every other case was checked against real OpenSCAD 2022.08.22 directly -- [2019,5,0], [2019,5], 5, undef, [2019], [2019,5,0,7] and ["a","b","c"] all match exactly. VersionReturnsThreeElementList only asserted the list's length, so it could not have caught a wrong release; it now pins the values alongside version_num()'s, which is the point of the pair. Co-Authored-By: Claude Opus 5 (1M context) --- pyproject.toml | 2 +- src/builtins/function_builtins.cpp | 25 +++++++++++++++++++++++-- tests/test_function_builtins.cpp | 29 ++++++++++++++++++++++++++++- uv.lock | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2297683..2060871 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" [project] name = "openscad_cpp_evaluator" -version = "0.32.1" +version = "0.32.2" description = "C++ OpenSCAD evaluator with Python bindings" readme = "README.md" requires-python = ">=3.12" diff --git a/src/builtins/function_builtins.cpp b/src/builtins/function_builtins.cpp index 67825df..4c36351 100644 --- a/src/builtins/function_builtins.cpp +++ b/src/builtins/function_builtins.cpp @@ -691,8 +691,29 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs } return Value{false}; } - case BuiltinFnId::Version: return numList({2025.0, 1.0, 1.0}); - case BuiltinFnId::VersionNum: return Value{20250101.0}; + // The OpenSCAD release we track. version_num() is that same + // year/month/day folded as y * 10000 + m * 100 + d, exactly like the + // reference's own builtin_version_num (builtin_functions.cc). + case BuiltinFnId::Version: return numList({2026.0, 1.0, 1.0}); + case BuiltinFnId::VersionNum: { + // The optional vector argument the reference also accepts: with + // no argument this is our own version() folded; with one, it is + // whatever [y, m] / [y, m, d] the caller passed, so + // version_num([2019, 5, 0]) == 20190500 regardless of what + // release we report. A 2-element vector defaults the day to 0 + // (getVec3's own defaultval), and anything else -- a non-list, a + // wrong length, a non-numeric element -- is undef. + // + // One deliberate divergence: the reference's size-2 path ignores + // getVec2's own failure and folds uninitialized doubles for e.g. + // version_num(["a", "b"]). That is undef here rather than + // whatever happened to be on the stack. + const Value* arg = args.findPositional(0); + if (!arg) return Value{20260101.0}; + const auto v = allNumericList(*arg); + if (!v || (v->size() != 2 && v->size() != 3)) return Value{}; + return Value{(*v)[0] * 10000.0 + (*v)[1] * 100.0 + (v->size() == 3 ? (*v)[2] : 0.0)}; + } case BuiltinFnId::ParentModule: return ev.parentModuleName(static_cast(toDoubleLenient(getArg(args, 0, "index", Value{0.0})))); } diff --git a/tests/test_function_builtins.cpp b/tests/test_function_builtins.cpp index d6d647d..b77efac 100644 --- a/tests/test_function_builtins.cpp +++ b/tests/test_function_builtins.cpp @@ -369,7 +369,28 @@ TEST(StringListBuiltins, FunctionValuedObjectMemberIsCallable) { TEST(StringListBuiltins, VersionBuiltins) { Evaluator ev; - EXPECT_DOUBLE_EQ(asNum(evalSrc("version_num()", ev)), 20250101.0); + EXPECT_DOUBLE_EQ(asNum(evalSrc("version_num()", ev)), 20260101.0); +} + +// version_num() also takes the optional vector the reference's own +// builtin_version_num does -- it folds whatever y/m/d it is handed, not just +// this build's release. +TEST(StringListBuiltins, VersionNumFoldsAGivenVersionVector) { + Evaluator ev; + EXPECT_DOUBLE_EQ(asNum(evalSrc("version_num([2019, 5, 0])", ev)), 20190500.0); + EXPECT_DOUBLE_EQ(asNum(evalSrc("version_num([2021, 1, 0])", ev)), 20210100.0); + // A 2-element vector defaults the day to 0 (getVec3's own defaultval). + EXPECT_DOUBLE_EQ(asNum(evalSrc("version_num([2019, 5])", ev)), 20190500.0); +} + +TEST(StringListBuiltins, VersionNumRejectsAnythingButA2Or3NumberVector) { + Evaluator ev; + EXPECT_TRUE(isUndef(evalSrc("version_num(5)", ev))); + EXPECT_TRUE(isUndef(evalSrc("version_num(undef)", ev))); + EXPECT_TRUE(isUndef(evalSrc("version_num([2019])", ev))); + EXPECT_TRUE(isUndef(evalSrc("version_num([2019, 5, 0, 7])", ev))); + EXPECT_TRUE(isUndef(evalSrc("version_num([\"a\", \"b\"])", ev))); + EXPECT_TRUE(isUndef(evalSrc("version_num([\"a\", \"b\", \"c\"])", ev))); } TEST(StringListBuiltins, VersionReturnsThreeElementList) { @@ -377,6 +398,12 @@ TEST(StringListBuiltins, VersionReturnsThreeElementList) { Value v = evalSrc("version()", ev); const auto& items = asList(v); ASSERT_EQ(items.size(), 3u); + // Pinned to the same release version_num() reports above -- the reference + // derives one from the other (y * 10000 + m * 100 + d, see + // builtin_version_num), so the two must not drift apart here either. + EXPECT_DOUBLE_EQ(asNum(items[0]), 2026.0); + EXPECT_DOUBLE_EQ(asNum(items[1]), 1.0); + EXPECT_DOUBLE_EQ(asNum(items[2]), 1.0); } TEST(StringListBuiltins, IsFunctionAndIsObject) { diff --git a/uv.lock b/uv.lock index 2a610b7..baf59b7 100644 --- a/uv.lock +++ b/uv.lock @@ -55,7 +55,7 @@ wheels = [ [[package]] name = "openscad-cpp-evaluator" -version = "0.32.1" +version = "0.32.2" source = { editable = "." } dependencies = [ { name = "numpy" },