Skip to content

v3.5: close OpenSCAD-completeness gaps missed by the v3 audit#72

Merged
particlesector merged 2 commits into
mainfrom
claude/openscad-feature-complete-lcq9eq
Jul 15, 2026
Merged

v3.5: close OpenSCAD-completeness gaps missed by the v3 audit#72
particlesector merged 2 commits into
mainfrom
claude/openscad-feature-complete-lcq9eq

Conversation

@particlesector

@particlesector particlesector commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

The v3 roadmap claimed "OpenSCAD Language Completeness," but that checklist tracked a specific 33-issue correctness audit — not a full diff against OpenSCAD's actual builtin/special-variable surface. An independent read-through against the OpenSCAD language reference turned up real gaps that had never been tracked as roadmap items or GitHub issues. This PR closes the tractable ones and documents the rest as explicit follow-up work in docs/roadmap.md (new "v3.5" section).

Fixed:

  • PI constant (was entirely absent — scripts had to hard-code 3.14159)
  • is_undef()/is_bool()/is_num()/is_string()/is_list()/is_function() type predicates
  • search() — string/list/table search, matching OpenSCAD's flat-vs-nested result shape depending on match_value's arity
  • version()/version_num() — reports a fixed OpenSCAD-compatibility level (2019.05) so version-gated library code picks its modern branch
  • $preview/$t special variables (fixed defaults — no animation/dual-render-pass distinction exists yet)
  • parent_module(idx)/$parent_modules — new module-call-name stack (Interpreter::pushModuleName/popModuleName, driven by CsgEvaluator::evalModuleCall)
  • linear_extrude(slices=...) — was parsed into the generic params map but silently never read; twist division count now honors an explicit slices over the $fn-derived default
  • children([vector]) / children([range]) — previously only a bare number index worked; vector and range forms silently returned nothing
  • echo(name=value) now formats as name = value, matching real OpenSCAD (previously printed the bare value)

Explicitly deferred (documented in roadmap with rationale, not silently dropped):

  • First-class function literals/closures (f = function(x) x*2;, OpenSCAD 2019.05+) — the language has named function foo(x) = expr; defs but no closure value type; adding one touches the Value variant, parser grammar, and call dispatch broadly enough to deserve its own pass.
  • $vpr/$vpt/$vpd viewport special variables — needs the render layer's camera state plumbed into the interpreter, a cross-subsystem wire that doesn't exist today.
  • cube/sphere/translate/etc. being reserved lexer keywords rather than ordinary identifiers (a script can't shadow one as a variable, unlike real OpenSCAD) — a grammar-level deviation, not a point fix.

Test plan

  • Added unit tests in tests/test_interpreter.cpp (PI, $preview/$t, all six type predicates, search() across single-char/multi-char/table forms, version()/version_num())
  • Added unit tests in tests/test_csg_evaluator.cpp (children([vector]), children([range]), out-of-range index skipping, echo(name=value) formatting, parent_module/$parent_modules at various call depths including "no parent", linear_extrude(slices=...) threading into the IR)
  • CI build/test run (this sandbox has no Vulkan SDK/vcpkg, so I could not build or run the suite locally — relying on CI to validate)

Note: MeshEvaluator.cpp (where slices is actually consumed to build geometry) isn't linked into the chiselcad_tests binary today (it pulls in manifold, which the test target doesn't), so the slices fix itself is covered only by production-code reading, not a runtime assertion — the test that exists verifies the param survives parsing into the CSG IR, which is the testable half.


Generated by Claude Code

The v3 roadmap checkboxes tracked a specific 33-issue correctness audit,
not a full diff against OpenSCAD's builtin/special-variable surface. An
independent read-through against the OpenSCAD language reference found
several real gaps that slipped through:

- PI constant was entirely absent
- is_undef/is_bool/is_num/is_string/is_list/is_function type predicates
- search() (string/list/table search)
- version()/version_num() (fixed compatibility level for version-gated
  library code)
- $preview/$t special variables
- parent_module(idx)/$parent_modules (new module-call-name stack, driven
  by CsgEvaluator, backing Interpreter's lookups)
- linear_extrude(slices=...) was parsed but silently dropped
- children([vector])/children([range]) — only a bare number index worked
- echo(name=value) now formats as "name = value" like real OpenSCAD

Deferred and documented in docs/roadmap.md rather than rushed: first-class
function literals/closures, $vpr/$vpt/$vpd viewport vars, and the
lexer-keyword-vs-identifier deviation for primitive names — each needs its
own pass rather than riding along with these point fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnU6afM9zR44X518jofBSY

@particlesector particlesector left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff logic in detail (module-name stack scoping for parent_module/$parent_modules, children([...]) vector/range handling, search()'s flat-vs-nested shape, and the slices extrusion fix) against the new tests and existing APIs — all correct. CI is green on both platforms. One sparse nit left inline; otherwise looks good to merge.


Generated by Claude Code

Comment thread src/lang/Interpreter.cpp
Real OpenSCAD's index_col_num accepts -1 to mean "compare match_value
against each entire row" instead of a specific column — the mode that
lets a vector match_value be matched as one unit against table rows,
e.g. search([3,"y"], table, 0, -1). Previously any negative indexCol
made every row resolve to undef, so -1 could never match.

Also had to guard the per-element vector-needle decomposition (search()
normally treats a vector match_value as one needle per element) so it
doesn't fire when indexCol == -1 — in that mode the whole match_value
vector is the needle being compared row-by-row, not something to split
apart.

Addresses PR review comment on #72.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnU6afM9zR44X518jofBSY
@particlesector
particlesector merged commit fc53202 into main Jul 15, 2026
4 checks passed
@particlesector
particlesector deleted the claude/openscad-feature-complete-lcq9eq branch July 15, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants