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" },