openscad-cpp-evaluator 0.42.1__tar.gz → 0.43.0__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 (207) hide show
  1. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/CLAUDE.md +13 -7
  2. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/bytecode.hpp +15 -0
  4. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -4
  5. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -16
  6. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/evaluator.hpp +46 -33
  7. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/pyproject.toml +1 -1
  8. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/booleans.cpp +8 -7
  9. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/control.cpp +5 -4
  10. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/bytecode_compiler.cpp +7 -0
  11. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/bytecode_vm.cpp +26 -16
  12. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/csg_resolve.cpp +5 -21
  13. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/user_calls.cpp +94 -9
  14. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_children_separate.cpp +119 -0
  15. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/.github/workflows/ci.yml +0 -0
  16. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/.github/workflows/wheels.yml +0 -0
  17. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/.gitignore +0 -0
  18. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/.gitmodules +0 -0
  19. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/LICENSE +0 -0
  21. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/README.md +0 -0
  22. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/bindings/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/bindings/ast_to_py.cpp +0 -0
  24. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/bindings/module.cpp +0 -0
  25. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/cmake/embed_font.cmake +0 -0
  26. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/examples/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/examples/eval_perf_benchmark.cpp +0 -0
  28. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/examples/manifold_cache_reuse.cpp +0 -0
  29. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/examples/minimal_debugger.cpp +0 -0
  30. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  31. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/.gitignore +0 -0
  32. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  33. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  34. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/README.md +0 -0
  35. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  36. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  37. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  39. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  40. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  41. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  42. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  43. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  44. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  45. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  46. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  47. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  48. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  49. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  50. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  51. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  52. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  53. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  54. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  55. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  56. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  57. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  58. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  59. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  60. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  61. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  62. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  63. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  64. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  65. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  66. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  67. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  68. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  69. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  70. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  71. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  72. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  73. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  74. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  75. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  76. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  77. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  78. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  79. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  80. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  81. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  82. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  83. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  84. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  85. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  86. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  87. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  88. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  89. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  90. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  91. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  92. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  93. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  94. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  95. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  96. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  97. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  98. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  99. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  100. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  101. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  102. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  103. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  104. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  105. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  106. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  107. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  108. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  109. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  110. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  111. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  112. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  113. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  114. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  115. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  116. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  117. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  118. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  119. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  120. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  121. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  122. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  123. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  124. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  125. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  126. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  127. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/resources/fonts/LICENSE +0 -0
  128. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  129. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/CMakeLists.txt +0 -0
  130. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/builtins.hpp +0 -0
  131. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/call_args.cpp +0 -0
  132. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/color.cpp +0 -0
  133. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/dxf_dim.cpp +0 -0
  134. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/extrude.cpp +0 -0
  135. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/function_builtins.cpp +0 -0
  136. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/import.cpp +0 -0
  137. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/modifiers.cpp +0 -0
  138. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/primitives_2d.cpp +0 -0
  139. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/primitives_3d.cpp +0 -0
  140. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/registry.cpp +0 -0
  141. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/roof.cpp +0 -0
  142. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/segments.cpp +0 -0
  143. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/surface.cpp +0 -0
  144. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/text.cpp +0 -0
  145. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/topology.cpp +0 -0
  146. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/builtins/transforms.cpp +0 -0
  147. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/colored_body.cpp +0 -0
  148. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/csg_generate.cpp +0 -0
  149. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/css_colors.cpp +0 -0
  150. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/debug/debug_repl.cpp +0 -0
  151. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/debug_profile.cpp +0 -0
  152. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/eval_context.cpp +0 -0
  153. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/eval_error.cpp +0 -0
  154. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/eval_use.cpp +0 -0
  155. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/export.cpp +0 -0
  156. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/expr_eval.cpp +0 -0
  157. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/import/dxf_import.cpp +0 -0
  158. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/import/mesh_import.cpp +0 -0
  159. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/import/surface_load.cpp +0 -0
  160. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/import/svg_import.cpp +0 -0
  161. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/manifold_cache.cpp +0 -0
  162. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/measure_geometry.cpp +0 -0
  163. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/mesh_check.cpp +0 -0
  164. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/stmt_eval.cpp +0 -0
  165. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/text/font_match.cpp +0 -0
  166. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/text/freetype_font_provider.cpp +0 -0
  167. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/text/text_metrics.cpp +0 -0
  168. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/value.cpp +0 -0
  169. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/src/zip_stored.cpp +0 -0
  170. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/CMakeLists.txt +0 -0
  171. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_booleans.cpp +0 -0
  172. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_bytecode_compiler.cpp +0 -0
  173. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_cli.cpp +0 -0
  174. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_control_flow.cpp +0 -0
  175. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_csg_tree.cpp +0 -0
  176. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_debug_hooks.cpp +0 -0
  177. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_dxf_svg_import.cpp +0 -0
  178. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_eval_context.cpp +0 -0
  179. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_export.cpp +0 -0
  180. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_expr_eval.cpp +0 -0
  181. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_extrude_roof.cpp +0 -0
  182. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_function_builtins.cpp +0 -0
  183. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_helpers.hpp +0 -0
  184. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_import_export.cpp +0 -0
  185. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_manifold_cache.cpp +0 -0
  186. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_mesh_check.cpp +0 -0
  187. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_multi_color_merge.cpp +0 -0
  188. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_primitives.cpp +0 -0
  189. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_profiling.cpp +0 -0
  190. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_python_bindings.py +0 -0
  191. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_render_expr.cpp +0 -0
  192. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_scope_trail.cpp +0 -0
  193. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_scoping.cpp +0 -0
  194. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_surface.cpp +0 -0
  195. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_tail_calls.cpp +0 -0
  196. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_text.cpp +0 -0
  197. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_transforms.cpp +0 -0
  198. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_unexpected_args.cpp +0 -0
  199. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_use.cpp +0 -0
  200. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_value.cpp +0 -0
  201. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_viewport_params.cpp +0 -0
  202. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tests/test_zip_stored.cpp +0 -0
  203. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tools/cli/CMakeLists.txt +0 -0
  204. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tools/cli/cli_lib.cpp +0 -0
  205. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tools/cli/cli_lib.hpp +0 -0
  206. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/tools/cli/main.cpp +0 -0
  207. {openscad_cpp_evaluator-0.42.1 → openscad_cpp_evaluator-0.43.0}/uv.lock +0 -0
@@ -481,13 +481,19 @@ grep for `ponytail:`.
481
481
  lexically nested inside an already-active call," picking `childCtx()` vs `callCtx()`
482
482
  accordingly; read its doc comment before touching it, it's the second-trickiest mechanism in this
483
483
  codebase after the CSG tree stack), `evalUserModule`/`evalUserFunction`/`evalFunctionLiteral`,
484
- and `builtinChildren` (children()/children(N), deferred evaluation; `children(separate=true)`
485
- additionally marks each forwarded node `separateOperand` so the enclosing
486
- union/difference/intersection treats them as separate operands rather than one grouped one --
487
- a BelfrySCAD extension, see `Evaluator::appendGroupSizes`. The marks stop at a user-module
488
- boundary: only the children() call's own splice honours them
489
- (`spliceModuleChildren`'s `honorSeparateMarks`), so a module wrapping such a call wraps its
490
- children as usual and its own caller sees one operand). `evalFunctionCall`'s
484
+ and `builtinChildren` (children()/children(N), deferred evaluation).
485
+ **`children(..., separate=true)`** is a BelfrySCAD extension handled *before* any of that, by
486
+ `expandChildStatements`: a block is read with each separating forward replaced by one synthetic
487
+ `children(k)` statement per child it selects (`makeChildrenIndexCall`, owned by
488
+ `syntheticNodes_` and cleared with `idToNode` at `resolveTreeImpl` entry). Everything the
489
+ feature does follows from their being real statements -- `$children` counts them,
490
+ `children(i)` indexes them, and `union`/`difference`/`intersection` give each its own operand
491
+ group -- with no flag on `CSGNode` and nothing to leak. Called from `buildModuleChildCtx`,
492
+ `resolveCsg`, `resolveIntersectionFor`, and the VM's `Op::CsgGroupChildren`, which exists
493
+ because the compiler fixes group boundaries long before `$children` (and so the expansion size)
494
+ is knowable; it shares `expandSeparatingChildren` with the interpreter so the engines cannot
495
+ drift. Synthetic calls carry the author's own `Position` and no scope, so they resolve in the
496
+ evaluating context (`evalChildren`'s `runAll`). `evalFunctionCall`'s
491
497
  precedence order (checked in this exact sequence): `import` (special-cased, its own
492
498
  module/expression-context split) → `isBuiltinFunctionName` (function_builtins.hpp — always wins
493
499
  if true) → user function lookup → function-literal *value* probe → "unknown function" warning.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.42.1
3
+ Version: 0.43.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -426,6 +426,21 @@ enum class Op {
426
426
  // own `groupSizes.push_back(Value{...})` loop.
427
427
  CsgGroupEnd,
428
428
 
429
+ // One `children(..., separate=true)` statement inside a CSG bracket.
430
+ // `a` interns the ModularCall in nativeStatements.
431
+ //
432
+ // A statement of its own, rather than a CsgGroupStart/End pair around
433
+ // compiled code, because how many groups it contributes is only known
434
+ // at RUNTIME -- `children([2:$children-1], separate=true)` reads
435
+ // $children, and the compiler emits its group boundaries long before
436
+ // any of that exists. So this op expands the call (the same
437
+ // Evaluator::expandSeparatingChildren the interpreter uses, so the two
438
+ // engines cannot drift) and records one group per expanded statement.
439
+ //
440
+ // Not an interpreter bail: the forwarded statements go through
441
+ // evalChildren, which self-gates and retries its own compiled chunks.
442
+ CsgGroupChildren,
443
+
429
444
  // Closes the matching Op::PushCsgWrap bracket: pops
430
445
  // VmFrame::csgWrapStack's own top entry FIRST (before anything that can
431
446
  // itself throw, e.g. setTreeDepthOrThrow below -- same "exception-
@@ -219,10 +219,6 @@ struct VmFrame {
219
219
  // mirrors evalModularCall's own `&node` (csg_resolve.cpp). Only
220
220
  // meaningful when ownsModuleSplice is true.
221
221
  const oscad::ASTNode* moduleSpliceCallNode = nullptr;
222
- // children(separate=true) on the call this frame is forwarding for.
223
- // Only meaningful while ownsModuleSplice is true, same as the two
224
- // fields above; reset in releaseVmFrame for the same pooling reason.
225
- bool separateChildren = false;
226
222
 
227
223
  // Whether callStack_.back() (at push time) genuinely IS this frame's
228
224
  // own logical call, and therefore safe for a tail hop inside this
@@ -34,15 +34,6 @@ struct CSGNode {
34
34
  CSGParams params; // resolve step's plain-data output
35
35
  bool uncacheable = false; // set by a later phase (ManifoldCache, Phase 8)
36
36
 
37
- // Set only by children(separate=true): this node begins its OWN operand
38
- // group in an enclosing union/difference/intersection/intersection_for,
39
- // instead of merging into the single group its enclosing statement
40
- // would otherwise form. See Evaluator::appendGroupSizes.
41
- //
42
- // Deliberately absent from cacheKey()'s allowlist (manifold_cache.cpp):
43
- // it changes the PARENT's hashed "group_sizes" param, never this node's
44
- // own geometry, so the parent already re-keys and this node must not.
45
- bool separateOperand = false;
46
37
 
47
38
  // The call site that entered this node's call chain from the top
48
39
  // level, captured at RESOLVE time -- non-owning, AST-lifetime-bound
@@ -83,12 +74,5 @@ struct CSGNode {
83
74
  std::optional<std::string> cachedKey;
84
75
  };
85
76
 
86
- // Marks every node from `from` onward as starting its own operand group --
87
- // what children(separate=true) does to the geometry it just forwarded.
88
- // A free function, not a member, so all three splice sites (user_calls.cpp
89
- // and bytecode_vm.cpp's two) can reach it from this header alone.
90
- inline void markSeparateOperands(std::vector<std::unique_ptr<CSGNode>>& nodes, size_t from) {
91
- for (size_t i = from; i < nodes.size(); ++i) nodes[i]->separateOperand = true;
92
- }
93
77
 
94
78
  } // namespace oscadeval
@@ -190,29 +190,6 @@ public:
190
190
  // statement.
191
191
  size_t currentTreeFrameSize() const { return treeStack_.back().size(); }
192
192
 
193
- // Records the group(s) one child statement contributed, given the frame
194
- // size captured before it ran. Normally that is exactly one group, of
195
- // however many nodes the statement pushed -- including a group of size
196
- // ZERO for a statement that produced no geometry (a disabled `*cube()`),
197
- // which generateCsg relies on to reset intersection() and to bail
198
- // difference(). Only children(separate=true) produces more than one:
199
- // each node it marked starts a fresh group, so its forwarded children
200
- // reach the enclosing operator as separate operands.
201
- //
202
- // Shared by all four group builders -- resolveCsg, resolveIntersectionFor
203
- // and the VM's Op::CsgGroupEnd (which serves both) -- because
204
- // "group_sizes" is otherwise built twice, once per engine, and would
205
- // drift.
206
- void appendGroupSizes(std::vector<Value>& groupSizes, size_t before) const {
207
- const std::vector<std::unique_ptr<CSGNode>>& frame = treeStack_.back();
208
- size_t start = before;
209
- for (size_t i = before + 1; i < frame.size(); ++i) {
210
- if (!frame[i]->separateOperand) continue;
211
- groupSizes.push_back(Value{static_cast<double>(i - start)});
212
- start = i;
213
- }
214
- groupSizes.push_back(Value{static_cast<double>(frame.size() - start)});
215
- }
216
193
 
217
194
  // Generate pass: walks `tree` bottom-up (children before their own
218
195
  // node), calling each node's registered GenerateFn (falling back to
@@ -366,14 +343,49 @@ public:
366
343
  struct ChildrenForward {
367
344
  EvalContext evalCtx;
368
345
  std::vector<const oscad::ASTNode*> nodes;
369
- // children(separate=true): hand these to the enclosing union/
370
- // difference/intersection as SEPARATE operands rather than as one
371
- // grouped operand. Acted on after the nodes are evaluated, by
372
- // marking the CSGNodes they produced -- see markSeparateOperands.
346
+ // The `separate` flag as written at the call. Read by
347
+ // expandSeparatingChildren, which turns the forward into one
348
+ // statement per child; builtinChildren itself ignores it, since by
349
+ // the time a forward runs the expansion has already happened.
373
350
  bool separate = false;
351
+ // Which of the caller's geometry statements `nodes` came from, in
352
+ // the same order. Needed by expandChildStatements, which turns each
353
+ // one back into its own `children(k)` statement.
354
+ std::vector<size_t> pickedIndices;
374
355
  };
375
356
  std::optional<ChildrenForward> prepareChildrenForward(const CallArgs& args, EvalContext& ctx);
376
357
 
358
+ // A child-statement block with every `children(..., separate=true)`
359
+ // replaced by one synthetic `children(k)` statement per child it
360
+ // selects. That is the whole of what separate=true does: the forwarded
361
+ // children become real, countable statements at this call site, so
362
+ // $children sees them, children(i) indexes them, and a boolean operator
363
+ // gives each its own operand group -- all for free, because they ARE
364
+ // statements rather than nodes wearing a flag.
365
+ //
366
+ // Called wherever a block is consumed: a module call's children
367
+ // (buildModuleChildCtx), and union/difference/intersection/
368
+ // intersection_for's own child list.
369
+ std::vector<const oscad::ASTNode*> expandChildStatements(
370
+ const std::vector<std::unique_ptr<oscad::ASTNode>>& block, EvalContext& ctx);
371
+
372
+ // One synthetic `children(index)` statement, owned by syntheticNodes_.
373
+ const oscad::ASTNode* makeChildrenIndexCall(const oscad::ModularCall& origin, size_t index);
374
+
375
+ // Syntactic gate: is this statement a `children(..., separate=true)`?
376
+ // Cheap and side-effect-free, so the bytecode compiler can ask it at
377
+ // COMPILE time to decide which opcode to emit, long before the flag's
378
+ // value (or the child count it selects) is knowable.
379
+ static bool isSeparatingChildrenCall(const oscad::ASTNode& stmt);
380
+
381
+ // The runtime half: the statements `call` expands to. nullopt means the
382
+ // `separate` flag resolved falsey after all, and the caller should run
383
+ // the original statement unchanged. Shared by expandChildStatements
384
+ // (interpreter) and Op::CsgGroupChildren (VM) so the engines cannot
385
+ // drift on what expansion means.
386
+ std::optional<std::vector<const oscad::ASTNode*>> expandSeparatingChildren(const oscad::ModularCall& call,
387
+ EvalContext& ctx);
388
+
377
389
  // "WARNING: {message}{locSuffix(position)}" via echoFn_, no-op if unset.
378
390
  // Public: builtins/import.cpp's not-manifold warning is emitted from a
379
391
  // free function, same reasoning as tagGenerated()/builtinChildren().
@@ -999,13 +1011,8 @@ public:
999
1011
  // (evalModularCall's OWN "is this splice or wrap-as-a-tagged-node"
1000
1012
  // branch already decided `splice` before ever reaching a user module,
1001
1013
  // so this helper never needs that decision itself).
1002
- // `honorSeparateMarks` is true only for a children() call's OWN splice.
1003
- // A user module's splice passes false, so it wraps its marked children
1004
- // as usual and the marks stop there -- children(separate=true) applies
1005
- // to the operator enclosing that call, not to whatever encloses the
1006
- // module the call happens to sit in.
1007
1014
  void spliceModuleChildren(std::vector<std::unique_ptr<CSGNode>> children, std::uint64_t randsBefore,
1008
- const oscad::ASTNode& callNode, bool honorSeparateMarks = false);
1015
+ const oscad::ASTNode& callNode);
1009
1016
  private:
1010
1017
  Value evalUserFunction(const std::string& name, const oscad::FunctionDeclaration& decl,
1011
1018
  const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& ctx,
@@ -1661,6 +1668,12 @@ public:
1661
1668
  // is on top *at that moment* -- the frame this node's own build call
1662
1669
  // pushed just before resolving. See csg_resolve.cpp for the full
1663
1670
  // push/call/pop sequence.
1671
+ // Synthetic `children(k)` calls built by expandChildStatements. Owned
1672
+ // here because CSGNode::node and idToNode hold non-owning pointers into
1673
+ // them, so they must outlive the pass; cleared with those maps at
1674
+ // resolveTreeImpl entry.
1675
+ std::vector<std::unique_ptr<oscad::ASTNode>> syntheticNodes_;
1676
+
1664
1677
  std::vector<std::vector<std::unique_ptr<CSGNode>>> treeStack_;
1665
1678
 
1666
1679
  private:
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.42.1"
7
+ version = "0.43.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -70,10 +70,11 @@ std::optional<manifold::CrossSection> toCrossSection(const std::vector<ColoredBo
70
70
  // misbehave when e.g. BOSL2's attachable() returns multiple bodies (parent
71
71
  // + attached children) as one operand.
72
72
  //
73
- // One statement can contribute MORE than one group: children(separate=true)
74
- // marks the nodes it forwards so each starts its own operand group, which is
73
+ // One statement can still turn into several: children(separate=true) is
74
+ // expanded into one `children(k)` statement per child it forwards, before
75
+ // this loop ever sees the block (Evaluator::expandChildStatements), which is
75
76
  // how `difference() children(separate=true)` subtracts children 1..n from
76
- // child 0. See Evaluator::appendGroupSizes, which both engines share. Mirrors _resolve_csg/_generate_csg,
77
+ // child 0. Nothing special happens here -- they are simply statements. Mirrors _resolve_csg/_generate_csg,
77
78
  // minus _attach_tri_colors' multi-color-merge provenance (ponytail: a
78
79
  // merged body always takes its first contributing child's color, matching
79
80
  // this evaluator's own pre-tri_colors baseline behavior -- revisit
@@ -92,11 +93,11 @@ CSGParams resolveCsg(Evaluator& ev, const oscad::ModularCall& node, EvalContext&
92
93
 
93
94
  std::vector<const oscad::ASTNode*> assignNodes;
94
95
  std::vector<const oscad::ASTNode*> geoNodes;
95
- for (const auto& c : node.children) {
96
+ for (const oscad::ASTNode* c : ev.expandChildStatements(node.children, effCtx)) {
96
97
  if (c->kind() == oscad::NodeKind::Assignment) {
97
- assignNodes.push_back(c.get());
98
+ assignNodes.push_back(c);
98
99
  } else if (c->kind() != oscad::NodeKind::ModuleDeclaration && c->kind() != oscad::NodeKind::FunctionDeclaration) {
99
- geoNodes.push_back(c.get());
100
+ geoNodes.push_back(c);
100
101
  }
101
102
  }
102
103
  if (!assignNodes.empty()) ev.evalChildren(assignNodes, effCtx);
@@ -106,7 +107,7 @@ CSGParams resolveCsg(Evaluator& ev, const oscad::ModularCall& node, EvalContext&
106
107
  for (const oscad::ASTNode* geoNode : geoNodes) {
107
108
  const size_t before = ev.currentTreeFrameSize();
108
109
  ev.evalChildren(std::vector<const oscad::ASTNode*>{geoNode}, effCtx);
109
- ev.appendGroupSizes(groupSizes, before);
110
+ groupSizes.push_back(Value{static_cast<double>(ev.currentTreeFrameSize() - before)});
110
111
  }
111
112
 
112
113
  CSGParams params;
@@ -97,9 +97,10 @@ ColoredBody combineBodies(const std::vector<ColoredBody>& bodies) {
97
97
  // children) -- same rationale as booleans.cpp's own group_sizes. Mirrors
98
98
  // _resolve_intersection_for/_generate_intersection_for.
99
99
  CSGParams resolveIntersectionFor(Evaluator& ev, const oscad::ModularIntersectionFor& node, EvalContext& ctx) {
100
- std::vector<const oscad::ASTNode*> bodyNodes;
101
- bodyNodes.reserve(node.body.size());
102
- for (const auto& b : node.body) bodyNodes.push_back(b.get());
100
+ // Expanded for the same reason resolveCsg expands its own block: a
101
+ // children(..., separate=true) here contributes one statement -- and so
102
+ // one operand group per iteration -- per child it selects.
103
+ const std::vector<const oscad::ASTNode*> bodyNodes = ev.expandChildStatements(node.body, ctx);
103
104
 
104
105
  std::vector<Value> groupSizes;
105
106
 
@@ -119,7 +120,7 @@ CSGParams resolveIntersectionFor(Evaluator& ev, const oscad::ModularIntersection
119
120
  if (!bodyNodes.empty()) ev.checkDebug(*bodyNodes.front(), parentCtx, /*forced=*/false, /*exprLevel=*/true);
120
121
  const size_t before = ev.currentTreeFrameSize();
121
122
  ev.evalChildren(bodyNodes, parentCtx);
122
- ev.appendGroupSizes(groupSizes, before);
123
+ groupSizes.push_back(Value{static_cast<double>(ev.currentTreeFrameSize() - before)});
123
124
  return;
124
125
  }
125
126
  const auto& assign = node.assignments[depth];
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include "openscad_cpp_evaluator/call_args.hpp"
4
4
  #include "openscad_cpp_evaluator/dispatch.hpp"
5
+ #include "openscad_cpp_evaluator/evaluator.hpp"
5
6
  #include "openscad_cpp_evaluator/function_builtins.hpp"
6
7
 
7
8
  #include "builtins/builtins.hpp"
@@ -1292,6 +1293,12 @@ public:
1292
1293
  }
1293
1294
  compileStatementList(assignNodes, out);
1294
1295
  for (const oscad::ASTNode* geoNode : geoNodes) {
1296
+ // A separating children() forward contributes a runtime-decided
1297
+ // NUMBER of groups, so it cannot be bracketed at compile time.
1298
+ if (Evaluator::isSeparatingChildrenCall(*geoNode)) {
1299
+ out.push_back({Op::CsgGroupChildren, internNativeStatement(geoNode), 0, &geoNode->position()});
1300
+ continue;
1301
+ }
1295
1302
  out.push_back({Op::CsgGroupStart, 0, 0, nullptr});
1296
1303
  compileStatementList(std::vector<const oscad::ASTNode*>{geoNode}, out);
1297
1304
  out.push_back({Op::CsgGroupEnd, 0, 0, nullptr});
@@ -360,7 +360,7 @@ void pushBracketedModuleFrame(Evaluator& ev, const CompiledChunk& chunk, const o
360
360
  // because module chunks never contain tail-call opcodes); don't inherit
361
361
  // a pooled function frame's stale true here either.
362
362
  void pushChildrenForwardFrame(Evaluator& ev, const CompiledChunk& chunk, EvalContext evalCtx,
363
- std::uint64_t randsBefore, const oscad::ASTNode& callNode, bool separate) {
363
+ std::uint64_t randsBefore, const oscad::ASTNode& callNode) {
364
364
  if (ev.vmCallStack_.size() >= Evaluator::kMaxVmCallStackDepth) {
365
365
  ev.error("Recursion too deep while forwarding children()", callNode);
366
366
  }
@@ -381,7 +381,6 @@ void pushChildrenForwardFrame(Evaluator& ev, const CompiledChunk& chunk, EvalCon
381
381
  frame->ownsModuleSplice = true;
382
382
  frame->moduleRandsBefore = randsBefore;
383
383
  frame->moduleSpliceCallNode = &callNode;
384
- frame->separateChildren = separate;
385
384
  ev.vmCallStack_.push_back(std::move(frame));
386
385
  ev.vmCallBrackets_.emplace_back(std::nullopt);
387
386
  }
@@ -506,7 +505,6 @@ Value driveVm(Evaluator& ev, size_t floor) {
506
505
  const bool ownsModuleSplice = finished->ownsModuleSplice;
507
506
  const std::uint64_t moduleRandsBefore = finished->moduleRandsBefore;
508
507
  const oscad::ASTNode* moduleSpliceCallNode = finished->moduleSpliceCallNode;
509
- const bool separateChildren = finished->separateChildren;
510
508
  while (!finished->ctxChain.empty()) finished->ctxChain.pop_back();
511
509
  // Module frames never fire returnHook (native evalUserModule
512
510
  // never did either -- a module call has no "return value"
@@ -516,12 +514,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
516
514
  if (isModule && ownsModuleSplice) {
517
515
  std::vector<std::unique_ptr<CSGNode>> children = std::move(ev.treeStack_.back());
518
516
  ev.treeStack_.pop_back();
519
- // separateChildren is set only by pushChildrenForwardFrame,
520
- // so it also answers "is this a children() splice?" -- a
521
- // user module's frame always passes false and wraps.
522
- if (separateChildren) markSeparateOperands(children, 0);
523
- ev.spliceModuleChildren(std::move(children), moduleRandsBefore, *moduleSpliceCallNode,
524
- /*honorSeparateMarks=*/separateChildren);
517
+ ev.spliceModuleChildren(std::move(children), moduleRandsBefore, *moduleSpliceCallNode);
525
518
  }
526
519
  if (isFloorFrame) {
527
520
  finalResult = std::move(result); // unused by a module caller (runCompiledModuleBody ignores it)
@@ -1051,11 +1044,9 @@ Value driveVm(Evaluator& ev, size_t floor) {
1051
1044
  const CompiledChunk* chunk = (ev.useBytecodeVm() && ev.inResolvePass())
1052
1045
  ? ev.lookupOrCompileChildrenListChunk(fwd->nodes)
1053
1046
  : nullptr;
1054
- // Read before fwd->evalCtx is moved from.
1055
- const bool separate = fwd->separate;
1056
1047
  if (chunk) {
1057
1048
  ev.treeStack_.emplace_back();
1058
- pushChildrenForwardFrame(ev, *chunk, std::move(fwd->evalCtx), randsBefore, *callNode, separate);
1049
+ pushChildrenForwardFrame(ev, *chunk, std::move(fwd->evalCtx), randsBefore, *callNode);
1059
1050
  // f.pc deliberately NOT advanced -- resumes when
1060
1051
  // the pushed frame completes; driveVm's completion
1061
1052
  // branch runs the splice (isModule &&
@@ -1079,9 +1070,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
1079
1070
  }
1080
1071
  std::vector<std::unique_ptr<CSGNode>> children = std::move(ev.treeStack_.back());
1081
1072
  ev.treeStack_.pop_back();
1082
- if (separate) markSeparateOperands(children, 0);
1083
- ev.spliceModuleChildren(std::move(children), randsBefore, *callNode,
1084
- /*honorSeparateMarks=*/true);
1073
+ ev.spliceModuleChildren(std::move(children), randsBefore, *callNode);
1085
1074
  ++f.pc;
1086
1075
  }
1087
1076
  break;
@@ -1318,9 +1307,30 @@ Value driveVm(Evaluator& ev, size_t floor) {
1318
1307
  ++f.pc;
1319
1308
  break;
1320
1309
  }
1310
+ case Op::CsgGroupChildren: {
1311
+ const auto& call =
1312
+ static_cast<const oscad::ModularCall&>(*f.chunk->nativeStatements[static_cast<size_t>(ins.a)]);
1313
+ PendingCsgWrap& pending = f.csgWrapStack.back();
1314
+ std::optional<std::vector<const oscad::ASTNode*>> expanded =
1315
+ ev.expandSeparatingChildren(call, ctx);
1316
+ // nullopt = `separate` resolved falsey: one group, the
1317
+ // original statement, exactly as CsgGroupStart/End would
1318
+ // have done for it.
1319
+ const std::vector<const oscad::ASTNode*> stmts =
1320
+ expanded ? *expanded : std::vector<const oscad::ASTNode*>{&call};
1321
+ for (const oscad::ASTNode* stmt : stmts) {
1322
+ const size_t before = ev.treeStack_.back().size();
1323
+ ev.evalChildren(std::vector<const oscad::ASTNode*>{stmt}, ctx);
1324
+ pending.groupSizes.push_back(
1325
+ Value{static_cast<double>(ev.treeStack_.back().size() - before)});
1326
+ }
1327
+ ++f.pc;
1328
+ break;
1329
+ }
1321
1330
  case Op::CsgGroupEnd: {
1322
1331
  PendingCsgWrap& pending = f.csgWrapStack.back();
1323
- ev.appendGroupSizes(pending.groupSizes, pending.groupStartSize);
1332
+ const size_t after = ev.treeStack_.back().size();
1333
+ pending.groupSizes.push_back(Value{static_cast<double>(after - pending.groupStartSize)});
1324
1334
  ++f.pc;
1325
1335
  break;
1326
1336
  }
@@ -126,10 +126,9 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
126
126
  // under a synthetic (display-only, is_builtin=false) "union" node so
127
127
  // the tree still reads as one shape at this call site. Mirrors
128
128
  // _eval_statement's splice branch exactly.
129
- const bool isChildrenCall = (name == "children" && isBuiltin);
130
- const bool splice = isChildrenCall || !isBuiltin;
129
+ const bool splice = (name == "children" && isBuiltin) || !isBuiltin;
131
130
  if (splice) {
132
- spliceModuleChildren(std::move(children), randsBefore, node, isChildrenCall);
131
+ spliceModuleChildren(std::move(children), randsBefore, node);
133
132
  return;
134
133
  }
135
134
 
@@ -148,7 +147,7 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
148
147
  }
149
148
 
150
149
  void Evaluator::spliceModuleChildren(std::vector<std::unique_ptr<CSGNode>> children, std::uint64_t randsBefore,
151
- const oscad::ASTNode& callNode, bool honorSeparateMarks) {
150
+ const oscad::ASTNode& callNode) {
152
151
  if (randsCallCount_ != randsBefore) {
153
152
  // rands() fired directly during *this* call's own resolve (e.g. an
154
153
  // assignment before any geometry statement in a user module's
@@ -159,23 +158,7 @@ void Evaluator::spliceModuleChildren(std::vector<std::unique_ptr<CSGNode>> child
159
158
  // landed on.
160
159
  for (auto& c : children) c->uncacheable = true;
161
160
  }
162
- // children(separate=true) marked these to start their own operand
163
- // groups, and the group walk only inspects the enclosing frame's top
164
- // level -- so the wrapper would hide the marks. Splice instead.
165
- //
166
- // Only for the children() call's own splice. A USER MODULE wrapping
167
- // such a call wraps as usual, which hides the marks and is exactly what
168
- // makes separateness stop at the module boundary:
169
- //
170
- // module pass() { children(separate=true); }
171
- // difference() pass() { a; b; } // a | b, not a - b
172
- //
173
- // pass() is its own shape; that it happens to forward its children
174
- // separately is its business, not its caller's.
175
- const bool anySeparate =
176
- honorSeparateMarks &&
177
- std::any_of(children.begin(), children.end(), [](const auto& c) { return c->separateOperand; });
178
- if (children.size() > 1 && !anySeparate) {
161
+ if (children.size() > 1) {
179
162
  auto unionNode = std::make_unique<CSGNode>();
180
163
  unionNode->kind = "union";
181
164
  unionNode->node = &callNode;
@@ -224,6 +207,7 @@ template <typename NodeList>
224
207
  std::vector<std::unique_ptr<CSGNode>> Evaluator::resolveTreeImpl(const NodeList& nodes, EvalContext& ctx) {
225
208
  idToNode.clear();
226
209
  idToColor.clear();
210
+ syntheticNodes_.clear();
227
211
  cacheProducer_.clear(); // holds AST pointers, same lifetime as idToNode's
228
212
  treeStack_.clear();
229
213
  treeStack_.emplace_back();
@@ -179,7 +179,6 @@ void Evaluator::releaseVmFrame(std::unique_ptr<VmFrame> frame) {
179
179
  frame->ownsModuleSplice = false;
180
180
  frame->moduleRandsBefore = 0;
181
181
  frame->moduleSpliceCallNode = nullptr;
182
- frame->separateChildren = false;
183
182
  vmFramePool_.push_back(std::move(frame));
184
183
  }
185
184
 
@@ -340,9 +339,10 @@ EvalContext Evaluator::buildModuleChildCtx(const oscad::ModuleDeclaration& decl,
340
339
  EvalContext& ctx, BoundArgs bound) {
341
340
  const oscad::Scope* childScope = decl.scope() ? decl.scope() : ctx.scope;
342
341
 
343
- auto childrenNodes = std::make_shared<ChildrenNodeList>();
344
- childrenNodes->reserve(call.children.size());
345
- for (const auto& c : call.children) childrenNodes->push_back(c.get());
342
+ // Expanded, so a children(..., separate=true) in this block becomes one
343
+ // real statement per child it selects -- which is what makes $children
344
+ // below count them and children(i) index them.
345
+ auto childrenNodes = std::make_shared<ChildrenNodeList>(expandChildStatements(call.children, ctx));
346
346
 
347
347
  EvalContext childCtx = callCtxFor(decl, ctx, childScope, childrenNodes, &ctx);
348
348
  // callCtx reset this to false; carry the CALLER's value across so
@@ -353,7 +353,7 @@ EvalContext Evaluator::buildModuleChildCtx(const oscad::ModuleDeclaration& decl,
353
353
  // `{}`, not the number of geometries they produce -- e.g. children()
354
354
  // counts as one child even if it forwards zero bodies.
355
355
  int childrenCount = 0;
356
- for (const auto& c : call.children) {
356
+ for (const oscad::ASTNode* c : *childrenNodes) {
357
357
  if (c->kind() != oscad::NodeKind::Assignment && c->kind() != oscad::NodeKind::ModuleDeclaration &&
358
358
  c->kind() != oscad::NodeKind::FunctionDeclaration) {
359
359
  ++childrenCount;
@@ -983,7 +983,15 @@ std::optional<Evaluator::ChildrenForward> Evaluator::prepareChildrenForward(cons
983
983
  }
984
984
 
985
985
  if (std::holds_alternative<std::monostate>(idxArg)) {
986
- return ChildrenForward{std::move(evalCtx), *ctx.childrenNodes, separate};
986
+ std::vector<size_t> allIndices;
987
+ size_t geoIdx = 0;
988
+ for (const oscad::ASTNode* c : *ctx.childrenNodes) {
989
+ if (c->kind() != oscad::NodeKind::Assignment && c->kind() != oscad::NodeKind::ModuleDeclaration &&
990
+ c->kind() != oscad::NodeKind::FunctionDeclaration) {
991
+ allIndices.push_back(geoIdx++);
992
+ }
993
+ }
994
+ return ChildrenForward{std::move(evalCtx), *ctx.childrenNodes, separate, std::move(allIndices)};
987
995
  }
988
996
 
989
997
  // children(N) indexes child *statements*, not output bodies -- a
@@ -1025,6 +1033,7 @@ std::optional<Evaluator::ChildrenForward> Evaluator::prepareChildrenForward(cons
1025
1033
  // does evaluate child 2 three times, and the order shows through in the
1026
1034
  // CSG tree even though a union usually hides it.
1027
1035
  std::vector<const oscad::ASTNode*> picked;
1036
+ std::vector<size_t> pickedIndices;
1028
1037
  picked.reserve(indexValues.size());
1029
1038
  for (const Value& v : indexValues) {
1030
1039
  if (!std::holds_alternative<double>(v)) {
@@ -1045,17 +1054,93 @@ std::optional<Evaluator::ChildrenForward> Evaluator::prepareChildrenForward(cons
1045
1054
  continue;
1046
1055
  }
1047
1056
  picked.push_back(geoNodes[static_cast<size_t>(idx)]);
1057
+ pickedIndices.push_back(static_cast<size_t>(idx));
1048
1058
  }
1049
1059
  if (picked.empty()) return std::nullopt;
1050
- return ChildrenForward{std::move(evalCtx), std::move(picked), separate};
1060
+ return ChildrenForward{std::move(evalCtx), std::move(picked), separate, std::move(pickedIndices)};
1061
+ }
1062
+
1063
+ // Syntactic only -- whether the flag is actually TRUE still needs the
1064
+ // argument evaluated, but gating on its presence first means an ordinary
1065
+ // children() call never has its arguments resolved twice.
1066
+ bool Evaluator::isSeparatingChildrenCall(const oscad::ASTNode& stmt) {
1067
+ if (stmt.kind() != oscad::NodeKind::ModularCall) return false;
1068
+ const auto& call = static_cast<const oscad::ModularCall&>(stmt);
1069
+ if (!call.name || call.name->name != "children") return false;
1070
+ size_t positional = 0;
1071
+ for (const auto& arg : call.arguments) {
1072
+ if (arg->kind() == oscad::NodeKind::NamedArgument) {
1073
+ if (static_cast<const oscad::NamedArgument&>(*arg).name->name == "separate") return true;
1074
+ } else if (++positional == 2) {
1075
+ return true; // children(index, separate) positionally
1076
+ }
1077
+ }
1078
+ return false;
1079
+ }
1080
+
1081
+ std::optional<std::vector<const oscad::ASTNode*>> Evaluator::expandSeparatingChildren(const oscad::ModularCall& call,
1082
+ EvalContext& ctx) {
1083
+ auto [args, effCtx] = resolveCallArgs(*this, call.arguments, ctx);
1084
+ std::optional<ChildrenForward> fwd = prepareChildrenForward(args, effCtx);
1085
+ if (!fwd) return std::vector<const oscad::ASTNode*>{}; // selects nothing -> no statements at all
1086
+ if (!fwd->separate) {
1087
+ // ponytail: `separate` written but falsey -- tell the caller to run
1088
+ // the original statement, which resolves its arguments a second
1089
+ // time. Only observable if an argument expression has side effects
1090
+ // (echo/rands inside `children(0, someFalseFlag)`); building a
1091
+ // faithful single-statement equivalent instead would cost a
1092
+ // synthetic vector-literal node for a shape nobody writes.
1093
+ return std::nullopt;
1094
+ }
1095
+ std::vector<const oscad::ASTNode*> out;
1096
+ out.reserve(fwd->pickedIndices.size());
1097
+ for (size_t idx : fwd->pickedIndices) out.push_back(makeChildrenIndexCall(call, idx));
1098
+ return out;
1099
+ }
1100
+
1101
+ const oscad::ASTNode* Evaluator::makeChildrenIndexCall(const oscad::ModularCall& origin, size_t index) {
1102
+ std::vector<std::unique_ptr<oscad::Argument>> args;
1103
+ args.push_back(std::make_unique<oscad::PositionalArgument>(
1104
+ origin.position(), std::make_unique<oscad::NumberLiteral>(origin.position(), static_cast<double>(index))));
1105
+ auto call = std::make_unique<oscad::ModularCall>(
1106
+ origin.position(), std::make_unique<oscad::Identifier>(origin.position(), "children"), std::move(args),
1107
+ std::vector<std::unique_ptr<oscad::ASTNode>>{});
1108
+ // Position copied from the call the author actually wrote, so a warning
1109
+ // or a click-to-source lands on that line rather than nowhere.
1110
+ //
1111
+ // No scope is set: every consumer falls back to the evaluating
1112
+ // context's own scope for a node without one (see evalChildren's runAll
1113
+ // in stmt_eval.cpp), which is exactly where this call should resolve.
1114
+ const oscad::ASTNode* raw = call.get();
1115
+ syntheticNodes_.push_back(std::move(call));
1116
+ return raw;
1117
+ }
1118
+
1119
+ std::vector<const oscad::ASTNode*> Evaluator::expandChildStatements(
1120
+ const std::vector<std::unique_ptr<oscad::ASTNode>>& block, EvalContext& ctx) {
1121
+ std::vector<const oscad::ASTNode*> out;
1122
+ out.reserve(block.size());
1123
+ for (const auto& stmtPtr : block) {
1124
+ const oscad::ASTNode* stmt = stmtPtr.get();
1125
+ if (!isSeparatingChildrenCall(*stmt)) {
1126
+ out.push_back(stmt);
1127
+ continue;
1128
+ }
1129
+ std::optional<std::vector<const oscad::ASTNode*>> expanded =
1130
+ expandSeparatingChildren(static_cast<const oscad::ModularCall&>(*stmt), ctx);
1131
+ if (!expanded) {
1132
+ out.push_back(stmt);
1133
+ continue;
1134
+ }
1135
+ for (const oscad::ASTNode* n : *expanded) out.push_back(n);
1136
+ }
1137
+ return out;
1051
1138
  }
1052
1139
 
1053
1140
  void Evaluator::builtinChildren(const CallArgs& args, EvalContext& ctx) {
1054
1141
  std::optional<ChildrenForward> fwd = prepareChildrenForward(args, ctx);
1055
1142
  if (!fwd) return;
1056
- const size_t before = currentTreeFrameSize();
1057
1143
  evalChildren(fwd->nodes, fwd->evalCtx);
1058
- if (fwd->separate) markSeparateOperands(treeStack_.back(), before);
1059
1144
  }
1060
1145
 
1061
1146
  } // namespace oscadeval