openscad-cpp-evaluator 0.13.4__tar.gz → 0.13.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bytecode.hpp +125 -12
  3. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bytecode_vm.hpp +27 -0
  4. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/pyproject.toml +1 -1
  5. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/bytecode_compiler.cpp +46 -0
  6. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/bytecode_vm.cpp +69 -0
  7. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_bytecode_compiler.cpp +106 -11
  8. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/.github/workflows/ci.yml +0 -0
  9. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/.github/workflows/wheels.yml +0 -0
  10. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/.gitignore +0 -0
  11. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/.gitmodules +0 -0
  12. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/CLAUDE.md +0 -0
  13. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/CMakeLists.txt +0 -0
  14. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/LICENSE +0 -0
  15. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/README.md +0 -0
  16. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/bindings/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/bindings/module.cpp +0 -0
  18. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/cmake/embed_font.cmake +0 -0
  19. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/examples/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/examples/eval_perf_benchmark.cpp +0 -0
  21. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/examples/manifold_cache_reuse.cpp +0 -0
  22. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/examples/minimal_debugger.cpp +0 -0
  23. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  24. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/.gitignore +0 -0
  25. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  26. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/README.md +0 -0
  28. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  29. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  30. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  31. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  32. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  33. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  34. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  35. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  36. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  37. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  39. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  40. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  41. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  42. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  43. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  44. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/api.cpp +0 -0
  45. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  46. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  47. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  48. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  49. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  50. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  51. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  52. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  53. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  54. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  55. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  56. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  57. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  58. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  59. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  60. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  61. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/position.cpp +0 -0
  62. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  63. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  64. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  65. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  66. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  67. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  68. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  69. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  70. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  71. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  72. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  73. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  74. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  75. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  76. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  77. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  78. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  79. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  80. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  81. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  82. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  83. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  84. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  85. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  86. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  87. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  88. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  89. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  90. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  91. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  92. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  93. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  94. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  95. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  96. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  97. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  98. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  99. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  100. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  101. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  102. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  103. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/export.hpp +0 -0
  104. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  105. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  106. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  107. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  108. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  109. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  110. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  111. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  112. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  113. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  114. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  115. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  116. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/value.hpp +0 -0
  117. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  118. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/python/openscad_cpp_evaluator/__init__.py +0 -0
  119. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/resources/fonts/LICENSE +0 -0
  120. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  121. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/CMakeLists.txt +0 -0
  122. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/booleans.cpp +0 -0
  123. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/builtins.hpp +0 -0
  124. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/call_args.cpp +0 -0
  125. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/color.cpp +0 -0
  126. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/control.cpp +0 -0
  127. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/extrude.cpp +0 -0
  128. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/function_builtins.cpp +0 -0
  129. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/import.cpp +0 -0
  130. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/modifiers.cpp +0 -0
  131. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/primitives_2d.cpp +0 -0
  132. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/primitives_3d.cpp +0 -0
  133. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/registry.cpp +0 -0
  134. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/roof.cpp +0 -0
  135. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/segments.cpp +0 -0
  136. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/surface.cpp +0 -0
  137. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/text.cpp +0 -0
  138. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/topology.cpp +0 -0
  139. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/builtins/transforms.cpp +0 -0
  140. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/colored_body.cpp +0 -0
  141. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/csg_generate.cpp +0 -0
  142. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/csg_resolve.cpp +0 -0
  143. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/css_colors.cpp +0 -0
  144. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/debug/debug_repl.cpp +0 -0
  145. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/debug_profile.cpp +0 -0
  146. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/eval_context.cpp +0 -0
  147. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/eval_error.cpp +0 -0
  148. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/eval_use.cpp +0 -0
  149. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/export.cpp +0 -0
  150. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/expr_eval.cpp +0 -0
  151. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/import/dxf_import.cpp +0 -0
  152. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/import/mesh_import.cpp +0 -0
  153. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/import/surface_load.cpp +0 -0
  154. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/import/svg_import.cpp +0 -0
  155. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/manifold_cache.cpp +0 -0
  156. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/stmt_eval.cpp +0 -0
  157. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/text/stb_font_provider.cpp +0 -0
  158. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/text/text_metrics.cpp +0 -0
  159. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/user_calls.cpp +0 -0
  160. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/value.cpp +0 -0
  161. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/src/zip_stored.cpp +0 -0
  162. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/CMakeLists.txt +0 -0
  163. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_booleans.cpp +0 -0
  164. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_cli.cpp +0 -0
  165. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_control_flow.cpp +0 -0
  166. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_csg_tree.cpp +0 -0
  167. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_debug_hooks.cpp +0 -0
  168. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_dxf_svg_import.cpp +0 -0
  169. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_eval_context.cpp +0 -0
  170. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_export.cpp +0 -0
  171. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_expr_eval.cpp +0 -0
  172. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_extrude_roof.cpp +0 -0
  173. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_function_builtins.cpp +0 -0
  174. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_helpers.hpp +0 -0
  175. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_import_export.cpp +0 -0
  176. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_manifold_cache.cpp +0 -0
  177. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_multi_color_merge.cpp +0 -0
  178. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_primitives.cpp +0 -0
  179. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_profiling.cpp +0 -0
  180. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_python_bindings.py +0 -0
  181. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_scope_trail.cpp +0 -0
  182. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_scoping.cpp +0 -0
  183. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_surface.cpp +0 -0
  184. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_tail_calls.cpp +0 -0
  185. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_text.cpp +0 -0
  186. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_transforms.cpp +0 -0
  187. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_use.cpp +0 -0
  188. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_value.cpp +0 -0
  189. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_viewport_params.cpp +0 -0
  190. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tests/test_zip_stored.cpp +0 -0
  191. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tools/cli/CMakeLists.txt +0 -0
  192. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tools/cli/cli_lib.cpp +0 -0
  193. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tools/cli/cli_lib.hpp +0 -0
  194. {openscad_cpp_evaluator-0.13.4 → openscad_cpp_evaluator-0.13.5}/tools/cli/main.cpp +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.13.4
3
+ Version: 0.13.5
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -325,6 +325,101 @@ enum class Op {
325
325
  // same seam, now a counter).
326
326
  PopBuiltinWrap,
327
327
 
328
+ // -- CSG-wrap compilation (closes the LAST native-reentry source in ----
329
+ // -- the original NativeStatement gap: union()/difference()/ -
330
+ // -- intersection()) ----------------------------------------------------
331
+ // union()/difference()/intersection() weren't covered by the original
332
+ // Op::PushBuiltinWrap (see that op's own doc comment) because they need
333
+ // bespoke bookkeeping PushBuiltinWrap's single all-children bracket
334
+ // doesn't do: Evaluator's own resolveCsg (booleans.cpp) evaluates each
335
+ // TOP-LEVEL child statement of the block SEPARATELY and records how
336
+ // many CSGNodes it individually contributed ("group_sizes") -- e.g.
337
+ // `difference(){ A; B; C; }` = A - (B u C), preserving A's own group
338
+ // even when A itself expands to more than one body (an attachable()-
339
+ // style call returning parent+children as one operand). A flat
340
+ // all-children bracket the way Transform/Color/Modifier use would lose
341
+ // that grouping entirely. This op pair (PushCsgWrap/PopCsgWrap) plus
342
+ // Op::CsgGroupStart/CsgGroupEnd (below) replicate resolveCsg's exact
343
+ // two-pass shape (all assignments first, THEN one evalChildren-per-
344
+ // geometry-statement) as inline compiled bytecode instead, exactly as
345
+ // PushBuiltinWrap already did for translate/rotate/scale/mirror/
346
+ // multmatrix/resize/color/#/%/! -- same rationale (a recursive
347
+ // union()/difference()/intersection()-wrapped module chain was still a
348
+ // real Windows native-reentry depth risk, just never independently
349
+ // fixed when PushBuiltinWrap's own set was).
350
+ //
351
+ // a = index into CompiledChunk::csgWrapSites. Runtime handler: captures
352
+ // ev.randsCallCount() BEFORE argument resolution (same rands-in-args
353
+ // taint reasoning as PushBuiltinWrap), resolves the (rare, $-only)
354
+ // arguments via resolveCallArgs exactly like resolveCsg itself does
355
+ // (discarding the positional/named result -- union/difference/
356
+ // intersection take no real parameters -- keeping only the possibly-
357
+ // $-scoped child ctx), pushes that ctx onto f.ctxChain unconditionally
358
+ // (mirrors Transform/Color's own unconditional push -- a bare
359
+ // `union() {...}` with no `$fn=...` override still pushes a ctx that's
360
+ // merely a copy, cheap and uniform rather than a special-cased branch),
361
+ // pushes a fresh ev.treeStack_ frame (every child statement's own
362
+ // CSGNode(s), across every group, land flat in this ONE frame --
363
+ // mirrors Evaluator::buildTreeNode/evalModularCall's own single
364
+ // treeStack_.emplace_back() around the whole call, not one per group),
365
+ // and stashes {op, randsBefore, siteIdx, empty groupSizes} onto
366
+ // VmFrame::csgWrapStack (a real per-frame LIFO, same reasoning as
367
+ // builtinWrapStack: nested/sequenced CSG wraps within one frame's own
368
+ // instruction stream, e.g. `union() { difference() {...} }`). The
369
+ // compiler always emits a plain Op::CheckDebugStatement immediately
370
+ // before this (see emitCsgWrap, bytecode_compiler.cpp), mirroring
371
+ // emitBuiltinWrap's own pattern -- this is a genuine statement doing
372
+ // real work here, not a call transferring control to a declaration.
373
+ PushCsgWrap,
374
+
375
+ // Opens one "group" within an already-open Op::PushCsgWrap bracket --
376
+ // emitted immediately before each top-level GEOMETRY child statement's
377
+ // own inline-compiled bytecode (compileStatementList of exactly that
378
+ // one statement). Records ev.treeStack_.back().size() into
379
+ // csgWrapStack.back().groupStartSize -- always operates on the
380
+ // TOPMOST (innermost still-open) csgWrapStack entry, matching
381
+ // PopBuiltinWrap's own back()-is-always-mine LIFO discipline, so no
382
+ // operand is needed. Assignments among the block's children are
383
+ // compiled separately, BEFORE any CsgGroupStart/End pair at all (see
384
+ // emitCsgWrap) -- mirrors resolveCsg's own two-pass split exactly
385
+ // (Evaluator::evalChildren(assignNodes, effCtx) always runs to
386
+ // completion before the per-geoNode loop starts).
387
+ CsgGroupStart,
388
+
389
+ // Closes the matching Op::CsgGroupStart: computes
390
+ // ev.treeStack_.back().size() - csgWrapStack.back().groupStartSize
391
+ // (how many CSGNodes THIS one top-level statement just contributed,
392
+ // however many that turned out to be -- 0 for a statement whose own
393
+ // evaluation spliced nothing, e.g. a no-op unknown-module warning; >1
394
+ // for an attachable()-style multi-body operand) and appends it, as a
395
+ // Value, onto csgWrapStack.back().groupSizes -- exactly one entry per
396
+ // top-level geometry statement, in source order, mirroring resolveCsg's
397
+ // own `groupSizes.push_back(Value{...})` loop.
398
+ CsgGroupEnd,
399
+
400
+ // Closes the matching Op::PushCsgWrap bracket: pops
401
+ // VmFrame::csgWrapStack's own top entry FIRST (before anything that can
402
+ // itself throw, e.g. setTreeDepthOrThrow below -- same "exception-
403
+ // teardown's own pending count must already be right" reasoning as
404
+ // PopBuiltinWrap), pops the ctx PushCsgWrap unconditionally pushed,
405
+ // pops ev.treeStack_ to retrieve every group's own CSGNode(s) (flat,
406
+ // exactly like resolveCsg's own `children` result -- group boundaries
407
+ // live only in group_sizes, never in the CSGNode list's own shape), and
408
+ // builds the tagged CSGNode exactly like Evaluator::buildTreeNode's own
409
+ // post-resolveBody() half does, with params = {"op": site.op,
410
+ // "group_sizes": ValueList(pending.groupSizes)} -- byte-for-byte what
411
+ // native resolveCsg returns -- pushing the result onto the new top of
412
+ // treeStack_. a = index into CompiledChunk::csgWrapSites (same site
413
+ // Push used).
414
+ //
415
+ // teardownVmCallStackDownTo's own exception path (bytecode_vm.cpp) pops
416
+ // frame->csgWrapStack.size() additional treeStack_ entries per
417
+ // torn-down frame, alongside its existing builtinWrapStack/
418
+ // ownsModuleSplice accounting -- same reasoning as PopBuiltinWrap's own
419
+ // doc comment: an exception can leave N of these brackets open in one
420
+ // frame.
421
+ PopCsgWrap,
422
+
328
423
  // A `children()` / `children(N)` statement -- the runtime-varying
329
424
  // sibling of Op::CallModule/Op::PushBuiltinWrap, closing the LAST
330
425
  // dominant native-reentry source (BOSL2's attachable() calls
@@ -367,10 +462,11 @@ enum class Op {
367
462
  // no separate site table needed).
368
463
  CallChildren,
369
464
 
370
- // A single "native passthrough" statement -- intersection_for,
371
- // union/difference/intersection and every other builtin
372
- // module call NOT covered by Op::PushBuiltinWrap (see that op's own
373
- // doc comment for exactly which builtins ARE covered), the `*`
465
+ // A single "native passthrough" statement -- intersection_for, every
466
+ // OTHER builtin module call not covered by Op::PushBuiltinWrap/
467
+ // Op::PushCsgWrap (see those ops' own doc comments for exactly which
468
+ // builtins ARE covered -- cube/sphere/hull/linear_extrude/etc., the
469
+ // ones that never wrap a recursive call in idiomatic OpenSCAD), the `*`
374
470
  // modifier's own no-op case, or a user-module call that didn't
375
471
  // resolve at compile time (shadowed, forward-declared, or otherwise
376
472
  // not statically known) -- anything compileStatementList doesn't give
@@ -387,18 +483,21 @@ enum class Op {
387
483
  // crash risk in practice, not just a missed optimization. See
388
484
  // Op::PushBuiltinWrap's own doc comment for the real story and the
389
485
  // fix. children() fell here too, and was the LAST and largest such
390
- // reentry source once PushBuiltinWrap's own set was covered -- it
391
- // now has Op::CallChildren, above.)
486
+ // reentry source once PushBuiltinWrap's own set was covered -- it now
487
+ // has Op::CallChildren, above. union()/difference()/intersection()
488
+ // fell here too, and were the last remaining REAL native-reentry risk
489
+ // (bespoke group_sizes bookkeeping meant they couldn't just reuse
490
+ // PushBuiltinWrap's own bracket) -- they now have Op::PushCsgWrap,
491
+ // above.)
392
492
  // a = index into CompiledChunk::nativeStatements. Runtime just does
393
493
  // what Evaluator::evalChildren's own per-statement loop already does
394
494
  // for one node: derive childCtx via ctx.withScope(...), checkDebug,
395
495
  // evalStatement. These are still "leaf-shaped" for what's left here
396
- // after Op::PushBuiltinWrap peeled off the proven-risky subset: real
397
- // recursion safety for a recursive module chain is covered by
398
- // CallModule/PushBuiltinWrap/ForIterNext/the Jump-based if/for control
399
- // flow, not by how many of THESE sit alongside them in the same body
400
- // -- true again now that the one construct that violated it
401
- // (translate()-wrapped recursion) has its own real bytecode instead.
496
+ // now that Op::PushBuiltinWrap/Op::PushCsgWrap have peeled off every
497
+ // proven-risky construct: real recursion safety for a recursive module
498
+ // chain is covered by CallModule/PushBuiltinWrap/PushCsgWrap/
499
+ // ForIterNext/the Jump-based if/for control flow, not by how many of
500
+ // THESE sit alongside them in the same body.
402
501
  NativeStatement,
403
502
 
404
503
  // If/if-else's own condition, evaluated NATIVELY (Evaluator::
@@ -734,6 +833,19 @@ struct CompiledChunk {
734
833
  const oscad::ASTNode* node = nullptr;
735
834
  };
736
835
 
836
+ // One Op::PushCsgWrap/PopCsgWrap site pair -- see those ops' own doc
837
+ // comments for the full "why union/difference/intersection need
838
+ // bespoke group_sizes bookkeeping instead of just reusing
839
+ // BuiltinWrapSite" rationale. `op` is always "union"/"difference"/
840
+ // "intersection" (the only 3 names resolveDispatch() maps to
841
+ // resolveCsg) -- always a genuine ModularCall (unlike BuiltinWrapSite's
842
+ // Modifier kind, this construct has no non-ModularCall variant), so
843
+ // `node` is typed precisely rather than a generic ASTNode*.
844
+ struct CsgWrapSite {
845
+ std::string op;
846
+ const oscad::ModularCall* node = nullptr;
847
+ };
848
+
737
849
  // One Op::AssertStatement site -- see that op's own doc comment for
738
850
  // the full contract. `conditionArgIndex`/`messageArgIndex` are indices
739
851
  // into the site's own argCount-sized popped-argument array (source
@@ -796,6 +908,7 @@ struct CompiledChunk {
796
908
  // own doc comments, above) -- always empty for a function chunk.
797
909
  std::vector<ModuleCallSite> moduleCallSites;
798
910
  std::vector<BuiltinWrapSite> builtinWrapSites;
911
+ std::vector<CsgWrapSite> csgWrapSites;
799
912
  std::vector<AssertSite> assertSites;
800
913
  std::vector<const oscad::Expression*> nativeExprs;
801
914
  std::vector<const oscad::ASTNode*> nativeStatements;
@@ -30,6 +30,24 @@ struct PendingBuiltinWrap {
30
30
  int siteIdx = -1;
31
31
  };
32
32
 
33
+ // One still-open Op::PushCsgWrap bracket's own state -- see that op's own
34
+ // doc comment (bytecode.hpp) for the full contract. `groupSizes` is built
35
+ // up incrementally, one entry per Op::CsgGroupStart/CsgGroupEnd pair (one
36
+ // per top-level GEOMETRY child statement); `groupStartSize` is scratch
37
+ // space for the CURRENTLY OPEN group only (set by CsgGroupStart, consumed
38
+ // by the matching CsgGroupEnd) -- safe as a single scalar, not a stack of
39
+ // its own, because groups within one CSG wrap are siblings in sequence,
40
+ // never nested (unlike PushCsgWrap brackets themselves, which CAN nest,
41
+ // e.g. `union() { difference() {...} }` -- that's what makes
42
+ // VmFrame::csgWrapStack itself a real LIFO, below).
43
+ struct PendingCsgWrap {
44
+ std::string op;
45
+ std::uint64_t randsBefore = 0;
46
+ int siteIdx = -1;
47
+ std::vector<Value> groupSizes;
48
+ size_t groupStartSize = 0;
49
+ };
50
+
33
51
  // One ListCompFor/statement-for assignment's own materialized iteration
34
52
  // state -- see Op::IterMaterialize/IterReset/IterNext's own doc comments
35
53
  // (bytecode.hpp). Lives in the header (not bytecode_vm.cpp's own anonymous
@@ -127,6 +145,15 @@ struct VmFrame {
127
145
  // existing invariant ("whoever pops this frame drains its own open
128
146
  // brackets first") already covers it.
129
147
  std::vector<PendingBuiltinWrap> builtinWrapStack;
148
+ // Op::PushCsgWrap's own per-frame LIFO -- same role/lifetime/teardown
149
+ // discipline as builtinWrapStack, just for union()/difference()/
150
+ // intersection() (see PendingCsgWrap's own doc comment, above, and
151
+ // Op::PushCsgWrap's, bytecode.hpp). Not explicitly cleared in
152
+ // releaseVmFrame, same as builtinWrapStack/accumStack/ctxChain -- the
153
+ // existing invariant ("whoever pops this frame drains its own open
154
+ // brackets first", normally via matched Push/Pop, or via
155
+ // teardownVmCallStackDownTo on the exception path) already covers it.
156
+ std::vector<PendingCsgWrap> csgWrapStack;
130
157
  // The ORIGINAL callee name at push time, used by
131
158
  // Evaluator::exitUserCallSuccess's own returnHook call when this frame
132
159
  // carries a bracket -- deliberately NOT updated by a later tail hop
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.13.4"
7
+ version = "0.13.5"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -1202,6 +1202,45 @@ public:
1202
1202
  out.push_back({Op::PopBuiltinWrap, idx, 0, &wrapperNode.position()});
1203
1203
  }
1204
1204
 
1205
+ // union()/difference()/intersection() -- see Op::PushCsgWrap's own doc
1206
+ // comment (bytecode.hpp) for why these can't just reuse emitBuiltinWrap:
1207
+ // resolveCsg (booleans.cpp) needs per-top-level-child-statement
1208
+ // "group_sizes" bookkeeping, replicated here as one Op::CsgGroupStart/
1209
+ // CsgGroupEnd pair per GEOMETRY child, with every ASSIGNMENT child
1210
+ // compiled first, unconditionally, regardless of interleaving in
1211
+ // source -- mirrors resolveCsg's own two-pass split (`assignNodes`
1212
+ // fully evaluated, THEN one evalChildren call per `geoNodes` entry)
1213
+ // exactly, including its ModuleDeclaration/FunctionDeclaration
1214
+ // exclusion (a nested declaration inside a CSG block contributes to
1215
+ // neither pass -- already hoisted into scope, nothing to run here).
1216
+ void emitCsgWrap(const oscad::ModularCall& call, std::vector<Instruction>& out) {
1217
+ out.push_back({Op::CheckDebugStatement, internNativeStatement(&call), 0, nullptr});
1218
+ CompiledChunk::CsgWrapSite site;
1219
+ site.op = call.name->name;
1220
+ site.node = &call;
1221
+ chunk_.csgWrapSites.push_back(std::move(site));
1222
+ const int idx = static_cast<int>(chunk_.csgWrapSites.size()) - 1;
1223
+ out.push_back({Op::PushCsgWrap, idx, 0, &call.position()});
1224
+
1225
+ std::vector<const oscad::ASTNode*> assignNodes;
1226
+ std::vector<const oscad::ASTNode*> geoNodes;
1227
+ for (const auto& c : call.children) {
1228
+ if (c->kind() == oscad::NodeKind::Assignment) {
1229
+ assignNodes.push_back(c.get());
1230
+ } else if (c->kind() != oscad::NodeKind::ModuleDeclaration &&
1231
+ c->kind() != oscad::NodeKind::FunctionDeclaration) {
1232
+ geoNodes.push_back(c.get());
1233
+ }
1234
+ }
1235
+ compileStatementList(assignNodes, out);
1236
+ for (const oscad::ASTNode* geoNode : geoNodes) {
1237
+ out.push_back({Op::CsgGroupStart, 0, 0, nullptr});
1238
+ compileStatementList(std::vector<const oscad::ASTNode*>{geoNode}, out);
1239
+ out.push_back({Op::CsgGroupEnd, 0, 0, nullptr});
1240
+ }
1241
+ out.push_back({Op::PopCsgWrap, idx, 0, &call.position()});
1242
+ }
1243
+
1205
1244
  void compileOneStatement(const oscad::ASTNode& stmt, std::vector<Instruction>& out) {
1206
1245
  using oscad::NodeKind;
1207
1246
  trackSpan(stmt);
@@ -1434,6 +1473,13 @@ public:
1434
1473
  out.push_back({Op::CallChildren, internNativeStatement(&stmt), 0, &stmt.position()});
1435
1474
  return;
1436
1475
  }
1476
+ // union()/difference()/intersection() -- see emitCsgWrap's
1477
+ // own doc comment for why these need their own bespoke
1478
+ // bracket rather than emitBuiltinWrap's.
1479
+ if (dispatchIt != dispatch.end() && dispatchIt->second == &resolveCsg) {
1480
+ emitCsgWrap(call, out);
1481
+ return;
1482
+ }
1437
1483
  // Every other builtin, or a name that didn't resolve to a
1438
1484
  // user module statically -- native passthrough, exactly
1439
1485
  // like echo/assert/etc. below. Never the recursion-depth
@@ -401,6 +401,11 @@ void teardownVmCallStackDownTo(Evaluator& ev, size_t floor) {
401
401
  // at once; at most one module-call splice ever can).
402
402
  for (size_t i = 0; i < frame->builtinWrapStack.size(); ++i) ev.treeStack_.pop_back();
403
403
  frame->builtinWrapStack.clear();
404
+ // Same reasoning, same LIFO-order requirement, for any still-open
405
+ // Op::PushCsgWrap bracket(s) -- see PendingCsgWrap's/Op::PushCsgWrap's
406
+ // own doc comments.
407
+ for (size_t i = 0; i < frame->csgWrapStack.size(); ++i) ev.treeStack_.pop_back();
408
+ frame->csgWrapStack.clear();
404
409
  // A module frame's own CALLER (Op::CallModule, mirroring
405
410
  // evalModularCall/buildTreeNode) always pushes a treeStack_
406
411
  // accumulator for it before pushing this frame -- normally popped
@@ -1108,6 +1113,70 @@ Value driveVm(Evaluator& ev, size_t floor) {
1108
1113
  ++f.pc;
1109
1114
  break;
1110
1115
  }
1116
+ case Op::PushCsgWrap: {
1117
+ const CompiledChunk::CsgWrapSite& site = f.chunk->csgWrapSites[static_cast<size_t>(ins.a)];
1118
+ // Captured BEFORE argument resolution -- same
1119
+ // rands-in-args taint reasoning as Op::PushBuiltinWrap.
1120
+ const std::uint64_t randsBefore = ev.randsCallCount();
1121
+ // union/difference/intersection take no positional
1122
+ // arguments in real OpenSCAD -- `args` is discarded,
1123
+ // exactly mirroring resolveCsg's own `(void)args;`
1124
+ // (booleans.cpp). Only `effCtx` (a possibly-$-scoped
1125
+ // child ctx, e.g. `difference($fn=8) {...}`) matters.
1126
+ auto [args, effCtx] = resolveCallArgs(ev, site.node->arguments, ctx);
1127
+ (void)args;
1128
+ f.ctxChain.push_back(std::move(effCtx));
1129
+ ev.treeStack_.emplace_back();
1130
+ f.csgWrapStack.push_back({site.op, randsBefore, ins.a, {}, 0});
1131
+ ++f.pc;
1132
+ break;
1133
+ }
1134
+ case Op::CsgGroupStart: {
1135
+ f.csgWrapStack.back().groupStartSize = ev.treeStack_.back().size();
1136
+ ++f.pc;
1137
+ break;
1138
+ }
1139
+ case Op::CsgGroupEnd: {
1140
+ PendingCsgWrap& pending = f.csgWrapStack.back();
1141
+ const size_t after = ev.treeStack_.back().size();
1142
+ pending.groupSizes.push_back(Value{static_cast<double>(after - pending.groupStartSize)});
1143
+ ++f.pc;
1144
+ break;
1145
+ }
1146
+ case Op::PopCsgWrap: {
1147
+ // Pop this bracket's own bookkeeping FIRST -- before
1148
+ // anything below that can itself throw
1149
+ // (setTreeDepthOrThrow) -- same ordering reasoning as
1150
+ // Op::PopBuiltinWrap's own doc comment.
1151
+ PendingCsgWrap pending = std::move(f.csgWrapStack.back());
1152
+ f.csgWrapStack.pop_back();
1153
+ const CompiledChunk::CsgWrapSite& site =
1154
+ f.chunk->csgWrapSites[static_cast<size_t>(pending.siteIdx)];
1155
+ f.ctxChain.pop_back();
1156
+ std::vector<std::unique_ptr<CSGNode>> children = std::move(ev.treeStack_.back());
1157
+ ev.treeStack_.pop_back();
1158
+ // Mirrors resolveCsg's own params exactly (booleans.cpp).
1159
+ CSGParams params;
1160
+ params["op"] = Value{pending.op};
1161
+ params["group_sizes"] =
1162
+ Value{std::make_shared<const ValueList>(ValueList{std::move(pending.groupSizes)})};
1163
+ // Mirrors Evaluator::buildTreeNode's own post-
1164
+ // resolveBody() half exactly (csg_resolve.cpp).
1165
+ const bool uncacheable =
1166
+ (ev.randsCallCount() != pending.randsBefore) ||
1167
+ std::any_of(children.begin(), children.end(), [](const auto& c) { return c->uncacheable; });
1168
+ auto treeNode = std::make_unique<CSGNode>();
1169
+ treeNode->kind = site.op;
1170
+ treeNode->node = site.node;
1171
+ treeNode->isBuiltin = true;
1172
+ treeNode->children = std::move(children);
1173
+ treeNode->params = std::move(params);
1174
+ treeNode->uncacheable = uncacheable;
1175
+ ev.setTreeDepthOrThrow(*treeNode, *site.node);
1176
+ ev.treeStack_.back().push_back(std::move(treeNode));
1177
+ ++f.pc;
1178
+ break;
1179
+ }
1111
1180
  case Op::NativeStatement: {
1112
1181
  const oscad::ASTNode* stmt = f.chunk->nativeStatements[static_cast<size_t>(ins.a)];
1113
1182
  EvalContext childCtx = ctx.withScope(stmt->scope() ? stmt->scope() : ctx.scope);
@@ -1397,16 +1397,35 @@ TEST(ModuleBodyCompiles, RecursiveModifierWrappedCallSucceedsWellPastTheOldNativ
1397
1397
  ASSERT_EQ(e.bodies.size(), 1u);
1398
1398
  }
1399
1399
 
1400
- // union()/difference()/intersection() are deliberately NOT covered by
1401
- // Op::PushBuiltinWrap (bespoke per-statement grouping/group_sizes logic,
1402
- // genuinely different from the transform/color/modifier shape -- see that
1403
- // op's own doc comment, bytecode.hpp) -- a recursive call wrapped in one
1404
- // still falls to Op::NativeStatement and still costs one real native
1405
- // frame per level, exactly like every covered construct used to. Proves
1406
- // the guard itself (driveVmNativeDepth_/kMaxDriveVmNativeDepth) is still
1407
- // live and still catches this, not accidentally disabled by
1408
- // Op::PushBuiltinWrap's own changes to the shared NativeStatement path.
1409
- TEST(ModuleBodyCompiles, UnionWrappedRecursionStillHitsTheNativeReentryGuardControlledError) {
1400
+ // union()/difference()/intersection() now have their own real bytecode
1401
+ // (Op::PushCsgWrap/CsgGroupStart/CsgGroupEnd/PopCsgWrap) instead of falling
1402
+ // to Op::NativeStatement -- see that op's own doc comment (bytecode.hpp)
1403
+ // for why they needed a bespoke bracket rather than just reusing
1404
+ // Op::PushBuiltinWrap's. A union()/difference()/intersection()-wrapped
1405
+ // recursive call no longer costs any native reentry at all: `recur2(n);`
1406
+ // (a resolved user-module call) compiles inline to Op::CallModule, a pure
1407
+ // in-VM push, so the whole chain never touches driveVmNativeDepth_.
1408
+ // Depth 1500 (same figure the PushBuiltinWrap/CallChildren tests above use)
1409
+ // comfortably clears the old 40-level Windows-unsafe native-reentry ceiling
1410
+ // while staying under kMaxCsgTreeDepth=2000 -- each `recur` level's own
1411
+ // union() still contributes one real CSGNode to the tree (this fix
1412
+ // eliminates native-stack cost, not CSG-tree depth, which is an orthogonal,
1413
+ // unrelated cap on the RESULT shape, not the call chain).
1414
+ TEST(ModuleBodyCompiles, UnionWrappedRecursionSucceedsWellPastTheOldNativeReentryLimit) {
1415
+ ScopedVm vm(true);
1416
+ Evaluated e = evalSrc("module recur(n) { union() { recur2(n); } }\n"
1417
+ "module recur2(n) { if (n > 0) { recur(n - 1); } else { cube(1); } }\n"
1418
+ "recur(1500);");
1419
+ ASSERT_EQ(e.bodies.size(), 1u);
1420
+ }
1421
+
1422
+ // The SAME chain pushed deep enough (3000 levels, one union() CSGNode per
1423
+ // `recur` level) now hits the orthogonal, pre-existing CSG-TREE-depth guard
1424
+ // (kMaxCsgTreeDepth=2000, csg_resolve.cpp) instead of a native-reentry
1425
+ // guard -- proves the native-reentry elimination didn't silently trade one
1426
+ // crash risk for an unguarded one; something still stops an unreasonably
1427
+ // deep result, just a different (and correctly-named) error now.
1428
+ TEST(ModuleBodyCompiles, UnionWrappedRecursionPastCsgTreeDepthLimitStillErrorsCleanly) {
1410
1429
  ScopedVm vm(true);
1411
1430
  try {
1412
1431
  evalSrc("module recur(n) { union() { recur2(n); } }\n"
@@ -1415,10 +1434,86 @@ TEST(ModuleBodyCompiles, UnionWrappedRecursionStillHitsTheNativeReentryGuardCont
1415
1434
  FAIL() << "expected EvalError";
1416
1435
  } catch (const EvalError& e) {
1417
1436
  const std::string what = e.what();
1418
- EXPECT_NE(what.find("Recursion too deep"), std::string::npos) << what;
1437
+ EXPECT_NE(what.find("Recursion too deep while building geometry"), std::string::npos) << what;
1419
1438
  }
1420
1439
  }
1421
1440
 
1441
+ // difference()/intersection() get the same treatment -- one representative
1442
+ // test each rather than the full union() coverage above, since all three
1443
+ // share emitCsgWrap/Op::PushCsgWrap verbatim (only the runtime `op` string
1444
+ // differs, consumed solely by generateCsg at generate time, never by the
1445
+ // compiled path itself).
1446
+ TEST(ModuleBodyCompiles, DifferenceWrappedRecursionSucceedsWellPastTheOldNativeReentryLimit) {
1447
+ ScopedVm vm(true);
1448
+ Evaluated e = evalSrc("module recur(n) { difference() { recur2(n); } }\n"
1449
+ "module recur2(n) { if (n > 0) { recur(n - 1); } else { cube(1); } }\n"
1450
+ "recur(1500);");
1451
+ ASSERT_EQ(e.bodies.size(), 1u);
1452
+ }
1453
+
1454
+ TEST(ModuleBodyCompiles, IntersectionWrappedRecursionSucceedsWellPastTheOldNativeReentryLimit) {
1455
+ ScopedVm vm(true);
1456
+ Evaluated e = evalSrc("module recur(n) { intersection() { recur2(n); } }\n"
1457
+ "module recur2(n) { if (n > 0) { recur(n - 1); } else { cube(1); } }\n"
1458
+ "recur(1500);");
1459
+ ASSERT_EQ(e.bodies.size(), 1u);
1460
+ }
1461
+
1462
+ // group_sizes/multi-body-operand grouping correctness under the COMPILED
1463
+ // path (test_booleans.cpp's own Difference.MultipleBodiesInFirstStatement
1464
+ // FormOnePositiveOperand/Union.MultipleBodiesInFirstStatementAllSurvive
1465
+ // already cover this for the default ambient VM state; this pins it
1466
+ // explicitly under ScopedVm(true) plus a MODULE body, not just a top-level
1467
+ // script, since that's the shape emitCsgWrap actually compiles).
1468
+ TEST(ModuleBodyCompiles, CsgWrapGroupingSurvivesInsideACompiledModuleBody) {
1469
+ ScopedVm vm(true);
1470
+ // First statement's own union() contributes TWO disjoint bodies (one
1471
+ // group, size 2); the second statement subtracts a cube overlapping
1472
+ // only the first of those two by 1. A flat (non-grouped) evaluation
1473
+ // would instead treat the second cube as its own separate operand.
1474
+ Evaluated e = evalSrc("module m() {"
1475
+ " difference() {"
1476
+ " union() { cube(2); translate([10,0,0]) cube(2); }"
1477
+ " translate([1,0,0]) cube(2);"
1478
+ " }"
1479
+ "}"
1480
+ "m();");
1481
+ ASSERT_EQ(e.bodies.size(), 1u);
1482
+ // 16 - overlap(1*2*2=4) = 12, same expected volume as the top-level test.
1483
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 12.0, 1e-6);
1484
+ }
1485
+
1486
+ // $-named-argument propagation into a compiled CSG wrap's children --
1487
+ // union/difference/intersection take no positional args in real OpenSCAD,
1488
+ // but `difference($fn=8) {...}` is legal and must still reach its children,
1489
+ // exactly like resolveCsg's own resolveCallArgs call preserves natively
1490
+ // (booleans.cpp). Verifies Op::PushCsgWrap's ctx push (unconditional,
1491
+ // unlike PushBuiltinWrap's Transform/Color-only push) actually carries it.
1492
+ TEST(ModuleBodyCompiles, DollarArgPropagatesIntoCompiledCsgWrapChildren) {
1493
+ ScopedVm vm(true);
1494
+ EXPECT_EQ(runCapturingEcho("module m() { union($fn = 9) { echo($fn); cube(1); } }\n"
1495
+ "m();"),
1496
+ "ECHO: 9");
1497
+ }
1498
+
1499
+ // Assignment-before-geometry ordering: resolveCsg evaluates EVERY
1500
+ // assignment child first, regardless of source interleaving with geometry
1501
+ // statements, THEN each geometry statement in its own group -- emitCsgWrap
1502
+ // mirrors this with a separate compile pass, not source order. A geometry
1503
+ // statement referencing a LATER-in-source assignment must still see it.
1504
+ TEST(ModuleBodyCompiles, CompiledCsgWrapEvaluatesAllAssignmentsBeforeAnyGeometryStatement) {
1505
+ ScopedVm vm(true);
1506
+ Evaluated e = evalSrc("module m() {"
1507
+ " union() {"
1508
+ " translate([sz, 0, 0]) cube(1);" // geometry statement first in SOURCE order
1509
+ " sz = 5;" // assignment second in source order
1510
+ " }"
1511
+ "}"
1512
+ "m();");
1513
+ ASSERT_EQ(e.bodies.size(), 1u);
1514
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 1.0, 1e-6);
1515
+ }
1516
+
1422
1517
  // The BOSL2 attachable() shape this whole effort targets: a wrapper module
1423
1518
  // whose body is just `children();`, applied at every level of a recursive
1424
1519
  // chain. children() used to fall to Op::NativeStatement -- one genuine