openscad-cpp-evaluator 0.13.3__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.3 → openscad_cpp_evaluator-0.13.5}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bytecode.hpp +168 -11
  3. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bytecode_vm.hpp +27 -0
  4. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/evaluator.hpp +69 -5
  5. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/scope_trail.hpp +19 -1
  6. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/pyproject.toml +1 -1
  7. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/bytecode_compiler.cpp +65 -6
  8. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/bytecode_vm.cpp +177 -0
  9. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/user_calls.cpp +36 -12
  10. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_bytecode_compiler.cpp +188 -11
  11. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_scope_trail.cpp +30 -0
  12. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/.github/workflows/ci.yml +0 -0
  13. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/.github/workflows/wheels.yml +0 -0
  14. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/.gitignore +0 -0
  15. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/.gitmodules +0 -0
  16. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/CLAUDE.md +0 -0
  17. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/CMakeLists.txt +0 -0
  18. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/LICENSE +0 -0
  19. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/README.md +0 -0
  20. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/bindings/CMakeLists.txt +0 -0
  21. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/bindings/module.cpp +0 -0
  22. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/cmake/embed_font.cmake +0 -0
  23. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/examples/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/examples/eval_perf_benchmark.cpp +0 -0
  25. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/examples/manifold_cache_reuse.cpp +0 -0
  26. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/examples/minimal_debugger.cpp +0 -0
  27. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  28. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/.gitignore +0 -0
  29. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  30. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  31. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/README.md +0 -0
  32. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  33. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  34. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  35. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  36. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  37. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  38. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  39. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  40. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  41. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  42. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  43. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  44. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  45. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  46. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  47. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  48. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/api.cpp +0 -0
  49. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  50. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  51. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  52. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  53. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  54. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  55. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  56. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  57. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  58. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  59. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  60. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  61. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  62. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  63. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  64. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  65. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/position.cpp +0 -0
  66. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  67. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  68. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  69. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  70. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  71. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  72. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  73. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  74. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  75. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  76. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  77. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  78. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  79. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  80. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  81. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  82. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  83. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  84. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  85. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  86. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  87. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  88. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  89. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  90. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  91. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  92. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  93. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  94. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  95. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  96. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  97. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  98. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  99. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  100. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  101. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  102. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  103. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  104. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  105. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  106. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/export.hpp +0 -0
  107. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  108. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  109. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  110. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  111. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  112. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  113. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  114. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  115. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  117. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  118. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/value.hpp +0 -0
  119. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  120. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/python/openscad_cpp_evaluator/__init__.py +0 -0
  121. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/resources/fonts/LICENSE +0 -0
  122. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  123. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/CMakeLists.txt +0 -0
  124. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/booleans.cpp +0 -0
  125. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/builtins.hpp +0 -0
  126. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/call_args.cpp +0 -0
  127. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/color.cpp +0 -0
  128. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/control.cpp +0 -0
  129. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/extrude.cpp +0 -0
  130. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/function_builtins.cpp +0 -0
  131. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/import.cpp +0 -0
  132. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/modifiers.cpp +0 -0
  133. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/primitives_2d.cpp +0 -0
  134. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/primitives_3d.cpp +0 -0
  135. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/registry.cpp +0 -0
  136. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/roof.cpp +0 -0
  137. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/segments.cpp +0 -0
  138. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/surface.cpp +0 -0
  139. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/text.cpp +0 -0
  140. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/topology.cpp +0 -0
  141. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/builtins/transforms.cpp +0 -0
  142. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/colored_body.cpp +0 -0
  143. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/csg_generate.cpp +0 -0
  144. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/csg_resolve.cpp +0 -0
  145. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/css_colors.cpp +0 -0
  146. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/debug/debug_repl.cpp +0 -0
  147. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/debug_profile.cpp +0 -0
  148. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/eval_context.cpp +0 -0
  149. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/eval_error.cpp +0 -0
  150. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/eval_use.cpp +0 -0
  151. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/export.cpp +0 -0
  152. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/expr_eval.cpp +0 -0
  153. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/import/dxf_import.cpp +0 -0
  154. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/import/mesh_import.cpp +0 -0
  155. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/import/surface_load.cpp +0 -0
  156. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/import/svg_import.cpp +0 -0
  157. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/manifold_cache.cpp +0 -0
  158. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/stmt_eval.cpp +0 -0
  159. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/text/stb_font_provider.cpp +0 -0
  160. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/text/text_metrics.cpp +0 -0
  161. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/value.cpp +0 -0
  162. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/src/zip_stored.cpp +0 -0
  163. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/CMakeLists.txt +0 -0
  164. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_booleans.cpp +0 -0
  165. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_cli.cpp +0 -0
  166. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_control_flow.cpp +0 -0
  167. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_csg_tree.cpp +0 -0
  168. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_debug_hooks.cpp +0 -0
  169. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_dxf_svg_import.cpp +0 -0
  170. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_eval_context.cpp +0 -0
  171. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_export.cpp +0 -0
  172. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_expr_eval.cpp +0 -0
  173. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_extrude_roof.cpp +0 -0
  174. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_function_builtins.cpp +0 -0
  175. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_helpers.hpp +0 -0
  176. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_import_export.cpp +0 -0
  177. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_manifold_cache.cpp +0 -0
  178. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_multi_color_merge.cpp +0 -0
  179. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_primitives.cpp +0 -0
  180. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_profiling.cpp +0 -0
  181. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_python_bindings.py +0 -0
  182. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_scoping.cpp +0 -0
  183. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_surface.cpp +0 -0
  184. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_tail_calls.cpp +0 -0
  185. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_text.cpp +0 -0
  186. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_transforms.cpp +0 -0
  187. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_use.cpp +0 -0
  188. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_value.cpp +0 -0
  189. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_viewport_params.cpp +0 -0
  190. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tests/test_zip_stored.cpp +0 -0
  191. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tools/cli/CMakeLists.txt +0 -0
  192. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tools/cli/cli_lib.cpp +0 -0
  193. {openscad_cpp_evaluator-0.13.3 → openscad_cpp_evaluator-0.13.5}/tools/cli/cli_lib.hpp +0 -0
  194. {openscad_cpp_evaluator-0.13.3 → 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.3
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,10 +325,148 @@ enum class Op {
325
325
  // same seam, now a counter).
326
326
  PopBuiltinWrap,
327
327
 
328
- // A single "native passthrough" statement -- intersection_for,
329
- // children(), union/difference/intersection and every other builtin
330
- // module call NOT covered by Op::PushBuiltinWrap (see that op's own
331
- // doc comment for exactly which builtins ARE covered), the `*`
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
+
423
+ // A `children()` / `children(N)` statement -- the runtime-varying
424
+ // sibling of Op::CallModule/Op::PushBuiltinWrap, closing the LAST
425
+ // dominant native-reentry source (BOSL2's attachable() calls
426
+ // children() at nearly every wrapper level; measured 85 of 93 native
427
+ // reentries in a real script). Unlike PushBuiltinWrap's constructs,
428
+ // the forwarded children aren't known at compile time (they're the
429
+ // CALLER's own call-site statements, carried on ctx.childrenNodes/
430
+ // childrenCallerCtx -- see Evaluator::buildModuleChildCtx), so they
431
+ // can't compile inline; instead the handler resolves the list at
432
+ // RUNTIME, looks up/compiles its chunk (the same
433
+ // childrenListChunkCache_ tryRunCompiledChildren uses, via
434
+ // lookupOrCompileChildrenListChunk -- gated on useBytecodeVm() &&
435
+ // inResolvePass_, the cache is pass-scoped), and pushes it onto
436
+ // vmCallStack_ directly, mirroring Op::CallModule's own zero-native-
437
+ // call push -- but with a THIRD frame shape: splice-owning like a
438
+ // module frame (ownsModuleSplice=true, mirroring evalModularCall's
439
+ // own unconditional splice branch for "children"), yet bracketless
440
+ // like a bare frame (children() never gets a callStack_/profiling
441
+ // entry natively either -- only enterUserCall pushes those, and
442
+ // resolveChildren/builtinChildren never call it). driveVm's existing
443
+ // completion branch and teardownVmCallStackDownTo both already
444
+ // handle that combination (their bracket and splice concerns are
445
+ // independent).
446
+ //
447
+ // Handler ordering is load-bearing: checkDebug fires in-handler
448
+ // against the scope-wrapped ctx (byte-for-byte what Op::
449
+ // NativeStatement does -- NOT via an emitted Op::CheckDebugStatement,
450
+ // whose handler passes the un-wrapped ctx); randsBefore is captured
451
+ // BEFORE argument resolution (rands-in-args taint, same lesson
452
+ // PushBuiltinWrap already encodes); and treeStack_ is pushed LAST,
453
+ // immediately before the frame push / native fallback (an early-out
454
+ // or a throw during arg resolution then has nothing to clean up --
455
+ // the native path's own catch{pop;throw} has no equivalent here, so
456
+ // the reorder IS the exception-safety mechanism). The not-eligible
457
+ // fallback reuses the ALREADY-resolved args (never evalStatement,
458
+ // which would re-resolve them: double rands(), double side effects)
459
+ // by inlining evalModularCall's own children branch around a native
460
+ // evalChildren call. a = index into CompiledChunk::nativeStatements
461
+ // (the ModularCall node -- arguments, error position, splice node;
462
+ // no separate site table needed).
463
+ CallChildren,
464
+
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 `*`
332
470
  // modifier's own no-op case, or a user-module call that didn't
333
471
  // resolve at compile time (shadowed, forward-declared, or otherwise
334
472
  // not statically known) -- anything compileStatementList doesn't give
@@ -344,17 +482,22 @@ enum class Op {
344
482
  // pattern that made this op's own native reentry a genuine Windows
345
483
  // crash risk in practice, not just a missed optimization. See
346
484
  // Op::PushBuiltinWrap's own doc comment for the real story and the
347
- // fix.)
485
+ // fix. children() fell here too, and was the LAST and largest such
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.)
348
492
  // a = index into CompiledChunk::nativeStatements. Runtime just does
349
493
  // what Evaluator::evalChildren's own per-statement loop already does
350
494
  // for one node: derive childCtx via ctx.withScope(...), checkDebug,
351
495
  // evalStatement. These are still "leaf-shaped" for what's left here
352
- // after Op::PushBuiltinWrap peeled off the proven-risky subset: real
353
- // recursion safety for a recursive module chain is covered by
354
- // CallModule/PushBuiltinWrap/ForIterNext/the Jump-based if/for control
355
- // flow, not by how many of THESE sit alongside them in the same body
356
- // -- true again now that the one construct that violated it
357
- // (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.
358
501
  NativeStatement,
359
502
 
360
503
  // If/if-else's own condition, evaluated NATIVELY (Evaluator::
@@ -690,6 +833,19 @@ struct CompiledChunk {
690
833
  const oscad::ASTNode* node = nullptr;
691
834
  };
692
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
+
693
849
  // One Op::AssertStatement site -- see that op's own doc comment for
694
850
  // the full contract. `conditionArgIndex`/`messageArgIndex` are indices
695
851
  // into the site's own argCount-sized popped-argument array (source
@@ -752,6 +908,7 @@ struct CompiledChunk {
752
908
  // own doc comments, above) -- always empty for a function chunk.
753
909
  std::vector<ModuleCallSite> moduleCallSites;
754
910
  std::vector<BuiltinWrapSite> builtinWrapSites;
911
+ std::vector<CsgWrapSite> csgWrapSites;
755
912
  std::vector<AssertSite> assertSites;
756
913
  std::vector<const oscad::Expression*> nativeExprs;
757
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
@@ -271,6 +271,25 @@ public:
271
271
  // _builtin_children/_eval_children_lazy.
272
272
  void builtinChildren(const CallArgs& args, EvalContext& ctx);
273
273
 
274
+ // The "which nodes, evaluated against what context" half of
275
+ // builtinChildren, shared with Op::CallChildren's runtime handler
276
+ // (bytecode_vm.cpp) so the native and compiled paths cannot drift on
277
+ // any of the subtle parts: the caller-ctx re-derivation, the
278
+ // $-forwarding loop (which must read the post-resolveCallArgs effCtx
279
+ // -- a children($fn=12) named-$ override lives only there), and the
280
+ // children(N) statement-index filtering/bounds-check. nullopt means
281
+ // "nothing to evaluate" (no forwarded children in scope, empty list,
282
+ // or index out of range) -- a silent no-op in every existing caller,
283
+ // exactly matching builtinChildren's own early returns. `ctx` must be
284
+ // the effCtx resolveCallArgs returned, same as builtinChildren's own
285
+ // parameter today. Public for the same free-function reasoning as
286
+ // builtinChildren itself.
287
+ struct ChildrenForward {
288
+ EvalContext evalCtx;
289
+ std::vector<const oscad::ASTNode*> nodes;
290
+ };
291
+ std::optional<ChildrenForward> prepareChildrenForward(const CallArgs& args, EvalContext& ctx);
292
+
274
293
  // "WARNING: {message}{locSuffix(position)}" via echoFn_, no-op if unset.
275
294
  // Public: builtins/import.cpp's not-manifold warning is emitted from a
276
295
  // free function, same reasoning as tagGenerated()/builtinChildren().
@@ -532,6 +551,17 @@ private:
532
551
  // into the caller's scope exactly the way the native per-statement
533
552
  // loop's own writes already do.
534
553
  bool tryRunCompiledChildren(const std::vector<const oscad::ASTNode*>& children, EvalContext& ctx);
554
+ public:
555
+ // The cache-lookup half of tryRunCompiledChildren, shared with
556
+ // Op::CallChildren's runtime handler (bytecode_vm.cpp, a free
557
+ // function -- public for the same no-friend-declaration reasoning as
558
+ // vmCallStack_/treeStack_). Returns the eligible compiled chunk for
559
+ // `children` or nullptr. Caller owns the useBytecodeVm()/
560
+ // inResolvePass_ gate -- the pass gate is load-bearing
561
+ // (childrenListChunkCache_ is pass-scoped, see its own doc comment),
562
+ // not defensive.
563
+ const CompiledChunk* lookupOrCompileChildrenListChunk(const std::vector<const oscad::ASTNode*>& children);
564
+ private:
535
565
  void evalModularCall(const oscad::ModularCall& node, EvalContext& ctx);
536
566
  void evalFor(const oscad::ModularFor& node, EvalContext& ctx);
537
567
  void evalLetBlock(const oscad::ModularLet& node, EvalContext& ctx);
@@ -951,15 +981,38 @@ private:
951
981
  // body -- so a resolvable module call anywhere in that list gets
952
982
  // Op::CallModule bytecode, and any if/for control flow around it gets
953
983
  // real Jump-based bytecode too, instead of falling through to the
954
- // native per-statement loop. Keyed by the list's own FIRST element,
955
- // same convention as assignBlockChunkCache_ (stable and unique per
956
- // list: a given evalChildren() call site is always handed the same
957
- // leading element across repeated evaluations). Same nullopt-means-
984
+ // native per-statement loop.
985
+ //
986
+ // Keyed by (FIRST element, list SIZE) -- NOT the first element alone,
987
+ // the original convention borrowed from assignBlockChunkCache_
988
+ // (where it IS sufficient: a leading-assignment run's first member
989
+ // uniquely determines the run). Here it wasn't: children()'s
990
+ // forwarding produces two DIFFERENT lists sharing a first element --
991
+ // bare `children()` forwards the caller's whole list, `children(0)`
992
+ // a single-element slice of it -- and keying by front alone made
993
+ // whichever form ran first poison the cache for the other (caught
994
+ // for real: `module m() { children(); children(0); }\n
995
+ // m() { cube(); sphere(); }` made children(0) emit BOTH shapes,
996
+ // or bare children() emit only one, depending on statement order).
997
+ // (front, size) fully disambiguates every real producer: fixed AST
998
+ // lists have unique fronts per call site, and the only same-front
999
+ // pair (bare vs indexed forwarding) always differs in size except
1000
+ // when the lists are literally identical anyway. Same nullopt-means-
958
1001
  // tried-and-failed convention, same dangling-pointer hazard, same
959
1002
  // two-part fix (cleared alongside stmtExprChunkCache_ at the top of
960
1003
  // every resolveTreeImpl call, only read/written while inResolvePass_)
961
1004
  // as stmtExprChunkCache_ itself.
962
- std::unordered_map<const oscad::ASTNode*, std::optional<CompiledChunk>> childrenListChunkCache_;
1005
+ struct ChildrenListKeyHash {
1006
+ size_t operator()(const std::pair<const oscad::ASTNode*, size_t>& k) const {
1007
+ // Standard hash-combine (boost-style golden-ratio mix) --
1008
+ // either half alone collides by construction here.
1009
+ const size_t h1 = std::hash<const oscad::ASTNode*>{}(k.first);
1010
+ const size_t h2 = std::hash<size_t>{}(k.second);
1011
+ return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
1012
+ }
1013
+ };
1014
+ std::unordered_map<std::pair<const oscad::ASTNode*, size_t>, std::optional<CompiledChunk>, ChildrenListKeyHash>
1015
+ childrenListChunkCache_;
963
1016
 
964
1017
  // See stmtExprChunkCache_'s own doc comment, immediately above, for why
965
1018
  // this exists. Not a reentrancy guard (resolveTreeImpl is never called
@@ -1115,6 +1168,17 @@ public:
1115
1168
  return bytecodeVmEnabled() && (!debugHooks_.debugHook || fastContinueBreakpoints_.has_value());
1116
1169
  }
1117
1170
 
1171
+ // Read accessor for inResolvePass_ (private, below) -- Op::CallChildren's
1172
+ // runtime handler (bytecode_vm.cpp, a free function) must gate its
1173
+ // childrenListChunkCache_ access on it, exactly like
1174
+ // tryRunCompiledChildren's own self-gate: the cache is pass-scoped
1175
+ // (cleared per resolveTreeImpl, AST-address-reuse hazard -- see
1176
+ // stmtExprChunkCache_'s own doc comment), and driveVm CAN run outside
1177
+ // the resolve pass (a host calling evalChildren directly reaches
1178
+ // module opcodes via lookupOrCompileModuleChunk, which is NOT
1179
+ // pass-scoped).
1180
+ bool inResolvePass() const { return inResolvePass_; }
1181
+
1118
1182
  private:
1119
1183
  // The fine-grained half of the check above: even when useBytecodeVm()
1120
1184
  // says compiling/using bytecode is on the table at all, a SPECIFIC
@@ -368,12 +368,30 @@ public:
368
368
  dirty_[level].push_back(id);
369
369
  }
370
370
 
371
+ // Level-aware, NOT a blind pop_back() -- the exact fix
372
+ // ScopeTrailStorage::popLevel (above) already carries, for the exact
373
+ // out-of-order-pop bug class its own doc comment describes; this
374
+ // indexed twin never got the same fix because nothing violated LIFO
375
+ // view destruction on the dyn trail until Op::CallChildren's
376
+ // forwarding frame (bytecode_vm.cpp): its evalCtx (a dyn level opened
377
+ // AFTER the call's own effCtx level) is moved into a VmFrame and
378
+ // OUTLIVES effCtx, so effCtx's pop runs while evalCtx's later entry
379
+ // is still physically on top of the same name's stack -- the blind
380
+ // pop_back() silently removed the still-live forwarded value instead
381
+ // of the one actually being popped (caught for real: a
382
+ // children($fn=9) named-$ override read back as the root default 0
383
+ // inside the forwarded child).
371
384
  void popLevel(int level) {
372
385
  auto it = dirty_.find(level);
373
386
  if (it != dirty_.end()) {
374
387
  for (int id : it->second) {
375
388
  auto& stack = stacks_[static_cast<size_t>(id)];
376
- if (!stack.empty()) stack.pop_back();
389
+ for (auto rit = stack.rbegin(); rit != stack.rend(); ++rit) {
390
+ if (rit->level == level) {
391
+ stack.erase(std::next(rit).base());
392
+ break;
393
+ }
394
+ }
377
395
  }
378
396
  dirty_.erase(it);
379
397
  }
@@ -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.3"
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);
@@ -1420,12 +1459,32 @@ public:
1420
1459
  emitBuiltinWrap(*wrapKind, call.name->name, call, children, out);
1421
1460
  return;
1422
1461
  }
1423
- // Every other builtin, children(), or a name that didn't
1424
- // resolve to a user module statically -- native
1425
- // passthrough, exactly like echo/assert/etc. below. Never
1426
- // the recursion-depth risk this compiler targets for
1427
- // THESE (see NativeStatement's own doc comment,
1428
- // bytecode.hpp).
1462
+ // children() -- the runtime-varying forwarding builtin,
1463
+ // detected by the same function-pointer-identity pattern
1464
+ // as Transform/Color above. Deliberately NO preceding
1465
+ // Op::CheckDebugStatement (unlike emitBuiltinWrap's own
1466
+ // emission): CallChildren's handler fires checkDebug
1467
+ // itself against the SCOPE-WRAPPED ctx, byte-for-byte
1468
+ // matching Op::NativeStatement's own handler -- the
1469
+ // CheckDebugStatement handler passes the un-wrapped ctx,
1470
+ // which would be a subtle behavior change for this node.
1471
+ // See Op::CallChildren's own doc comment (bytecode.hpp).
1472
+ if (dispatchIt != dispatch.end() && dispatchIt->second == &resolveChildren) {
1473
+ out.push_back({Op::CallChildren, internNativeStatement(&stmt), 0, &stmt.position()});
1474
+ return;
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
+ }
1483
+ // Every other builtin, or a name that didn't resolve to a
1484
+ // user module statically -- native passthrough, exactly
1485
+ // like echo/assert/etc. below. Never the recursion-depth
1486
+ // risk this compiler targets for THESE (see
1487
+ // NativeStatement's own doc comment, bytecode.hpp).
1429
1488
  out.push_back({Op::NativeStatement, internNativeStatement(&stmt), 0, &stmt.position()});
1430
1489
  return;
1431
1490
  }