openscad-cpp-evaluator 0.2.0__tar.gz → 0.3.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 (193) hide show
  1. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/bindings/module.cpp +47 -9
  3. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/evaluator.hpp +26 -0
  4. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/pyproject.toml +1 -1
  5. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/python/openscad_cpp_evaluator/__init__.py +12 -3
  6. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/csg_generate.cpp +37 -6
  7. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_python_bindings.py +48 -1
  8. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/.github/workflows/ci.yml +0 -0
  9. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/.github/workflows/wheels.yml +0 -0
  10. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/.gitignore +0 -0
  11. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/.gitmodules +0 -0
  12. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/CLAUDE.md +0 -0
  13. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/CMakeLists.txt +0 -0
  14. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/LICENSE +0 -0
  15. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/README.md +0 -0
  16. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/bindings/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/examples/CMakeLists.txt +0 -0
  18. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/examples/eval_perf_benchmark.cpp +0 -0
  19. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/examples/manifold_cache_reuse.cpp +0 -0
  20. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/examples/minimal_debugger.cpp +0 -0
  21. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  22. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/.gitignore +0 -0
  23. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  24. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  25. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/README.md +0 -0
  26. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  27. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  28. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  29. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  30. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  31. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  32. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  33. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  34. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  35. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  36. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  37. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  38. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  39. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  40. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  41. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  42. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  43. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  44. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  45. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  46. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  47. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  48. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  49. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  50. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  51. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  52. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  53. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  54. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  55. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  56. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  57. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  58. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  59. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  60. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  61. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  62. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  63. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  64. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  65. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  66. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  67. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  68. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  69. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  70. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  71. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  72. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  73. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  74. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  75. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  76. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  77. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  78. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  79. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  80. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  81. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  82. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  83. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  84. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  85. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  86. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  87. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  88. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bundled_font_path.hpp.in +0 -0
  89. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  90. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  91. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  92. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  93. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  94. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  95. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  96. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  97. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  98. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  99. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  100. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  101. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  102. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  103. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  104. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  105. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  106. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  107. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  108. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  109. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  110. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  111. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  112. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  113. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  114. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  115. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  116. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  117. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  118. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/resources/fonts/LICENSE +0 -0
  119. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  120. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/CMakeLists.txt +0 -0
  121. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/booleans.cpp +0 -0
  122. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/builtins.hpp +0 -0
  123. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/call_args.cpp +0 -0
  124. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/color.cpp +0 -0
  125. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/control.cpp +0 -0
  126. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/extrude.cpp +0 -0
  127. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/function_builtins.cpp +0 -0
  128. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/import.cpp +0 -0
  129. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/modifiers.cpp +0 -0
  130. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/primitives_2d.cpp +0 -0
  131. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/primitives_3d.cpp +0 -0
  132. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/registry.cpp +0 -0
  133. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/roof.cpp +0 -0
  134. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/segments.cpp +0 -0
  135. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/surface.cpp +0 -0
  136. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/text.cpp +0 -0
  137. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/topology.cpp +0 -0
  138. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/transforms.cpp +0 -0
  139. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/bytecode_compiler.cpp +0 -0
  140. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/bytecode_vm.cpp +0 -0
  141. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/colored_body.cpp +0 -0
  142. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/csg_resolve.cpp +0 -0
  143. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/css_colors.cpp +0 -0
  144. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/debug/debug_repl.cpp +0 -0
  145. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/debug_profile.cpp +0 -0
  146. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/eval_context.cpp +0 -0
  147. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/eval_error.cpp +0 -0
  148. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/eval_use.cpp +0 -0
  149. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/export.cpp +0 -0
  150. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/expr_eval.cpp +0 -0
  151. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/import/dxf_import.cpp +0 -0
  152. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/import/mesh_import.cpp +0 -0
  153. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/import/surface_load.cpp +0 -0
  154. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/import/svg_import.cpp +0 -0
  155. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/manifold_cache.cpp +0 -0
  156. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/stmt_eval.cpp +0 -0
  157. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/text/stb_font_provider.cpp +0 -0
  158. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/text/text_metrics.cpp +0 -0
  159. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/user_calls.cpp +0 -0
  160. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/value.cpp +0 -0
  161. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/src/zip_stored.cpp +0 -0
  162. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/CMakeLists.txt +0 -0
  163. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_booleans.cpp +0 -0
  164. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_bytecode_compiler.cpp +0 -0
  165. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_cli.cpp +0 -0
  166. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_control_flow.cpp +0 -0
  167. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_csg_tree.cpp +0 -0
  168. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_debug_hooks.cpp +0 -0
  169. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_dxf_svg_import.cpp +0 -0
  170. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_eval_context.cpp +0 -0
  171. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_export.cpp +0 -0
  172. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_expr_eval.cpp +0 -0
  173. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_extrude_roof.cpp +0 -0
  174. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_function_builtins.cpp +0 -0
  175. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_helpers.hpp +0 -0
  176. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_import_export.cpp +0 -0
  177. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_manifold_cache.cpp +0 -0
  178. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_multi_color_merge.cpp +0 -0
  179. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_primitives.cpp +0 -0
  180. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_profiling.cpp +0 -0
  181. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_scoping.cpp +0 -0
  182. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_surface.cpp +0 -0
  183. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_tail_calls.cpp +0 -0
  184. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_text.cpp +0 -0
  185. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_transforms.cpp +0 -0
  186. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_use.cpp +0 -0
  187. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_value.cpp +0 -0
  188. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_viewport_params.cpp +0 -0
  189. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tests/test_zip_stored.cpp +0 -0
  190. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/CMakeLists.txt +0 -0
  191. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/cli_lib.cpp +0 -0
  192. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/cli_lib.hpp +0 -0
  193. {openscad_cpp_evaluator-0.2.0 → openscad_cpp_evaluator-0.3.0}/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.2.0
3
+ Version: 0.3.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -292,13 +292,34 @@ nb::list callStackToPy(const std::vector<oscadeval::CallStackFrame>& cs) {
292
292
  return out;
293
293
  }
294
294
 
295
+ // Called from within a debug_hook/error_break callback (never after it
296
+ // returns) to best-effort generate a live render of whatever's been
297
+ // resolved so far -- see Evaluator::generatePartialTree()'s own doc
298
+ // comment for why this is safe to call mid-resolve.
299
+ using GeneratePartialFn = std::function<std::vector<oscadeval::ColoredBody>()>;
300
+
301
+ // Wraps a GeneratePartialFn as a Python callable returning a plain list of
302
+ // body-dicts (bodiesToList's own shape) -- the facade's _generate_partial_render
303
+ // converts these to ColoredBody and catches whatever exception a GenerateFn
304
+ // raises, the same try/except shape it already had calling generate_tree()
305
+ // directly. No error handling needed here: a C++ exception crossing into
306
+ // Python via this cpp_function is nanobind's normal std::exception ->
307
+ // RuntimeError mapping, which that Python try/except already catches.
308
+ nb::object generatePartialTrampoline(const GeneratePartialFn& generatePartial) {
309
+ return nb::cpp_function([&generatePartial]() -> nb::list {
310
+ std::vector<oscadeval::ColoredBody> bodies = generatePartial();
311
+ return bodiesToList(bodies);
312
+ });
313
+ }
314
+
295
315
  // Trampoline: C++ debug hook -> the Python DebugSession hook. Runs with the
296
- // GIL held (reacquired by the caller). `getFrame`/`callStack` are valid only
297
- // for this synchronous call, so the get_frames closure is only ever invoked
298
- // from within the Python hook, before it returns.
316
+ // GIL held (reacquired by the caller). `getFrame`/`callStack`/`generatePartial`
317
+ // are valid only for this synchronous call, so the get_frames/generate_partial
318
+ // closures are only ever invoked from within the Python hook, before it returns.
299
319
  oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, bool forced, const std::string& origin,
300
320
  const std::vector<oscadeval::CallStackFrame>& callStack,
301
- const oscadeval::DebugFramesFn& getFrame) {
321
+ const oscadeval::DebugFramesFn& getFrame,
322
+ const GeneratePartialFn& generatePartial) {
302
323
  auto getFramesPy = nb::cpp_function([&callStack, &getFrame]() -> nb::object {
303
324
  std::vector<oscadeval::DebugFrame> frames = getFrame();
304
325
  nb::list allFrameLocals;
@@ -313,9 +334,11 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
313
334
  }
314
335
  return nb::make_tuple(nb::make_tuple(lastLocals, allFrameLocals), callStackToPy(callStack));
315
336
  });
337
+ nb::object generatePartialPy = generatePartialTrampoline(generatePartial);
316
338
 
317
339
  nb::object ret = hook(line, depth, nb::arg("forced") = forced, nb::arg("expr_level") = false,
318
- nb::arg("expr_depth") = 0, nb::arg("origin") = origin, nb::arg("get_frames") = getFramesPy);
340
+ nb::arg("expr_depth") = 0, nb::arg("origin") = origin, nb::arg("get_frames") = getFramesPy,
341
+ nb::arg("generate_partial") = generatePartialPy);
319
342
  nb::tuple t = nb::cast<nb::tuple>(ret);
320
343
  oscadeval::DebugAction action;
321
344
  action.stop = (nb::cast<std::string>(t[0]) == "stop");
@@ -327,11 +350,13 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
327
350
  // Trampoline: C++ errorBreak -> the Python DebugSession error hook.
328
351
  void callPyErrorBreak(nb::handle errorBreak, int line, const std::string& header, const std::string& origin,
329
352
  const std::vector<oscadeval::CallStackFrame>& callStack,
330
- const oscadeval::DebugFramesFn& getFrame) {
353
+ const oscadeval::DebugFramesFn& getFrame, const GeneratePartialFn& generatePartial) {
331
354
  std::vector<oscadeval::DebugFrame> frames = getFrame();
332
355
  nb::list allFrameLocals;
333
356
  for (const oscadeval::DebugFrame& f : frames) allFrameLocals.append(frameToDict(f));
334
- errorBreak(line, header, allFrameLocals, callStackToPy(callStack), nb::arg("origin") = origin);
357
+ nb::object generatePartialPy = generatePartialTrampoline(generatePartial);
358
+ errorBreak(line, header, allFrameLocals, callStackToPy(callStack), nb::arg("origin") = origin,
359
+ nb::arg("generate_partial") = generatePartialPy);
335
360
  }
336
361
 
337
362
  // Trampoline: C++ returnHook -> the Python DebugSession return hook. Only
@@ -362,17 +387,29 @@ nb::object debugEvaluate(const std::string& path, nb::dict viewportParams, nb::c
362
387
  nb::gil_scoped_acquire g;
363
388
  echoFn(m);
364
389
  };
390
+ // Set right after `ev` is constructed below, before evaluate() runs
391
+ // -- every hook fires only from within that call, by which point
392
+ // this is always valid. Needed because `hooks` (captured by the
393
+ // lambdas below) must be fully built and passed into Evaluator's
394
+ // constructor before `ev` exists, so the lambdas can't capture `ev`
395
+ // itself; they capture this pointer's address instead, which DOES
396
+ // already exist (see generatePartial's own doc comment on why
397
+ // GeneratePartialFn needs a live Evaluator& at all).
398
+ oscadeval::Evaluator* evPtr = nullptr;
399
+ GeneratePartialFn generatePartial = [&evPtr]() -> std::vector<oscadeval::ColoredBody> {
400
+ return evPtr->generatePartialTree();
401
+ };
365
402
  oscadeval::DebugHooks hooks;
366
403
  hooks.debugHook = [&](int line, int depth, bool forced, const std::string& origin,
367
404
  const std::vector<oscadeval::CallStackFrame>& cs,
368
405
  const oscadeval::DebugFramesFn& gf) -> oscadeval::DebugAction {
369
406
  nb::gil_scoped_acquire g;
370
- return callPyDebugHook(debugHook, line, depth, forced, origin, cs, gf);
407
+ return callPyDebugHook(debugHook, line, depth, forced, origin, cs, gf, generatePartial);
371
408
  };
372
409
  hooks.errorBreak = [&](int line, const std::string& header, const std::string& origin,
373
410
  const std::vector<oscadeval::CallStackFrame>& cs, const oscadeval::DebugFramesFn& gf) {
374
411
  nb::gil_scoped_acquire g;
375
- callPyErrorBreak(errorBreak, line, header, origin, cs, gf);
412
+ callPyErrorBreak(errorBreak, line, header, origin, cs, gf, generatePartial);
376
413
  };
377
414
  if (!returnHook.is_none()) {
378
415
  hooks.returnHook = [&](const std::string& name, const oscadeval::Value& result, int depth) {
@@ -384,6 +421,7 @@ nb::object debugEvaluate(const std::string& path, nb::dict viewportParams, nb::c
384
421
  std::vector<std::unique_ptr<oscad::ASTNode>> ast = oscad::getASTFromFile(path);
385
422
  oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(ast, path, echoCpp);
386
423
  oscadeval::Evaluator ev(echoCpp, nullptr, manifoldCache, hooks, false);
424
+ evPtr = &ev;
387
425
  oscadeval::EvalContext ctx = oscadeval::EvalContext::makeRoot(used.rootScope.get());
388
426
  bodies = oscadeval::toRenderableBodies(ev.evaluate(used.processedNodes, ctx, vp));
389
427
  collectIdSpans(ev, idSpans);
@@ -174,6 +174,26 @@ public:
174
174
  // top-level show_only filter -- see evaluate().
175
175
  std::vector<ColoredBody> generateTree(const std::vector<std::unique_ptr<CSGNode>>& tree);
176
176
 
177
+ // Best-effort generate over whatever's been resolved SO FAR, for a live
178
+ // partial render while genuinely paused inside a debug hook mid-
179
+ // evaluation (resolveTree()/evaluate() haven't returned yet) -- flattens
180
+ // treeStack_ across every nesting level and runs the same bottom-up
181
+ // generate walk generateTree() uses, via generateTreeImpl(), over
182
+ // non-owning pointers into nodes treeStack_ (not this call) still owns.
183
+ // Safe to call mid-resolve: the resolve pass never reads CSGNode::bodies,
184
+ // only ever writes kind/params/children, so populating .bodies early
185
+ // here cannot corrupt or race the resolve that's paused around this
186
+ // call -- and if a ManifoldCache is set, the real generateTree() call at
187
+ // the end of evaluate() gets a cache hit for any subtree this already
188
+ // generated, so a debug session pays no repeated Manifold work session-
189
+ // wide (see manifold_cache.hpp's own doc comment on this exact use
190
+ // case). Only meaningful to call synchronously from within a
191
+ // DebugHookFn/ErrorBreakFn callback; the caller must not retain a
192
+ // reference into anything treeStack_ owns past that callback's return.
193
+ // Mirrors the reference's `ev.generate_tree(partial_nodes)` called from
194
+ // `_generate_partial_render`.
195
+ std::vector<ColoredBody> generatePartialTree();
196
+
177
197
  // Provenance tables populated by tagGenerated() during generate --
178
198
  // originalID -> the AST node that produced it / that node's own
179
199
  // resolved color. Public, read by a caller (CLI, WYSIWYG picking) after
@@ -357,6 +377,12 @@ private:
357
377
  std::vector<ColoredBody> evaluateImpl(const NodeList& nodes, EvalContext& ctx,
358
378
  const std::unordered_map<std::string, Value>& viewportParams);
359
379
 
380
+ // Shared bottom-up generate walk behind generateTree()/generatePartialTree()
381
+ // -- see generatePartialTree()'s own doc comment and this function's
382
+ // definition (csg_generate.cpp) for why non-owning pointers, not
383
+ // unique_ptr, let both callers share one implementation.
384
+ std::vector<ColoredBody> generateTreeImpl(const std::vector<CSGNode*>& tree);
385
+
360
386
  // Evaluates one top-level element of a `[...]` list literal (a plain
361
387
  // expression, or one of the 6 list-comprehension clause kinds --
362
388
  // for/c-for/let/if/if-else/each), appending whatever it contributes to
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -17,7 +17,7 @@ from ._openscad_cpp_evaluator import ManifoldCache
17
17
 
18
18
  __all__ = [
19
19
  "Evaluator", "ColoredBody", "EvalError", "ParseError", "OscObject", "parse", "to_renderable_bodies",
20
- "ManifoldCache", "CallSiteProfile", "ProfileResult", "format_csg_tree",
20
+ "ManifoldCache", "CallSiteProfile", "ProfileResult", "format_csg_tree", "bodies_from_dicts",
21
21
  ]
22
22
 
23
23
 
@@ -132,7 +132,7 @@ class _CSGNode:
132
132
  def __init__(self, kind, params, bodies, is_builtin, children):
133
133
  self.kind = kind
134
134
  self.params = params
135
- self.bodies = [ColoredBody(_BodyShim(d), d["color"], None, d["flat_preview"]) for d in bodies]
135
+ self.bodies = bodies_from_dicts(bodies)
136
136
  self.is_builtin = is_builtin
137
137
  self.children = children
138
138
 
@@ -289,6 +289,15 @@ class ColoredBody:
289
289
  flat_preview: bool = False
290
290
 
291
291
 
292
+ def bodies_from_dicts(body_dicts) -> list:
293
+ """Convert raw body-dicts (the shape both evaluate()/debug_evaluate() and
294
+ a debug hook's `generate_partial()` callable return) to ColoredBody
295
+ instances. Shared by Evaluator.evaluate() and a debug session's own live
296
+ partial-render (see debug_evaluate()'s `generate_partial` hook kwarg,
297
+ module.cpp) so both paths convert identically."""
298
+ return [ColoredBody(_BodyShim(d), d["color"], None, d["flat_preview"]) for d in body_dicts]
299
+
300
+
292
301
  class Evaluator:
293
302
  """Drop-in for belfryscad.engine.evaluator.Evaluator, backed by C++.
294
303
 
@@ -355,7 +364,7 @@ class Evaluator:
355
364
  raise
356
365
  except Exception as e: # ParseError/EvalError from C++ arrive as RuntimeError
357
366
  raise EvalError(str(e)) from e
358
- bodies = [ColoredBody(_BodyShim(d), d["color"], None, d["flat_preview"]) for d in body_dicts]
367
+ bodies = bodies_from_dicts(body_dicts)
359
368
  # originalID -> a node with `.position` (start/end offsets) for WYSIWYG
360
369
  # picking and gizmo write-back.
361
370
  id_to_node = {
@@ -22,10 +22,18 @@ std::vector<ColoredBody> flattenCsgTree(const std::vector<std::unique_ptr<CSGNod
22
22
  return result;
23
23
  }
24
24
 
25
- std::vector<ColoredBody> Evaluator::generateTree(const std::vector<std::unique_ptr<CSGNode>>& tree) {
25
+ // Shared bottom-up walk behind generateTree() (owning unique_ptr tree) and
26
+ // generatePartialTree() (a live, still-being-resolved treeStack_ level,
27
+ // which the resolve pass -- not this function -- continues to own): takes
28
+ // non-owning CSGNode* so both callers share one implementation without
29
+ // either transferring ownership. unique_ptr's constness doesn't propagate
30
+ // to the pointee, so mutating node.bodies here is legal even from a const
31
+ // vector<unique_ptr<CSGNode>>& (see csg_node.hpp) -- this overload just
32
+ // makes that non-owning access explicit for the treeStack_ caller too.
33
+ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>& tree) {
26
34
  std::vector<ColoredBody> topLevelBodies;
27
35
  const auto& dispatch = generateDispatch();
28
- for (const auto& nodePtr : tree) {
36
+ for (CSGNode* nodePtr : tree) {
29
37
  CSGNode& node = *nodePtr;
30
38
 
31
39
  // ManifoldCache lookup: node.uncacheable (rands() taint, set
@@ -43,10 +51,11 @@ std::vector<ColoredBody> Evaluator::generateTree(const std::vector<std::unique_p
43
51
  } else {
44
52
  // Recurse into children first (bottom-up) -- populates each
45
53
  // child's own .bodies in place so flattenCsgTree/a GenerateFn
46
- // can read them back. unique_ptr's constness doesn't propagate
47
- // to the pointee, so this mutation is legal even though `tree`
48
- // is a const& (see csg_node.hpp).
49
- generateTree(node.children);
54
+ // can read them back.
55
+ std::vector<CSGNode*> childPtrs;
56
+ childPtrs.reserve(node.children.size());
57
+ for (const std::unique_ptr<CSGNode>& c : node.children) childPtrs.push_back(c.get());
58
+ generateTreeImpl(childPtrs);
50
59
 
51
60
  auto it = dispatch.find(node.kind);
52
61
  if (node.isBuiltin && it != dispatch.end()) {
@@ -66,6 +75,28 @@ std::vector<ColoredBody> Evaluator::generateTree(const std::vector<std::unique_p
66
75
  return topLevelBodies;
67
76
  }
68
77
 
78
+ std::vector<ColoredBody> Evaluator::generateTree(const std::vector<std::unique_ptr<CSGNode>>& tree) {
79
+ std::vector<CSGNode*> ptrs;
80
+ ptrs.reserve(tree.size());
81
+ for (const std::unique_ptr<CSGNode>& n : tree) ptrs.push_back(n.get());
82
+ return generateTreeImpl(ptrs);
83
+ }
84
+
85
+ std::vector<ColoredBody> Evaluator::generatePartialTree() {
86
+ // Flatten treeStack_ across every nesting level (mirrors the reference's
87
+ // `[node for level in ev._tree_stack for node in level]`): a CSGNode
88
+ // only lands in its parent's own accumulator once the parent's resolve
89
+ // finishes, so for a script whose whole geometry is one deeply-nested
90
+ // top-level statement, the *top* of the tree stays empty for the entire
91
+ // time spent stepping through its innermost leaves -- flattening every
92
+ // level picks up already-resolved leaves (e.g. a finished cube() sitting
93
+ // in a still-in-progress union()'s accumulator) regardless of how deep.
94
+ std::vector<CSGNode*> flat;
95
+ for (const std::vector<std::unique_ptr<CSGNode>>& level : treeStack_)
96
+ for (const std::unique_ptr<CSGNode>& node : level) flat.push_back(node.get());
97
+ return generateTreeImpl(flat);
98
+ }
99
+
69
100
  ColoredBody Evaluator::tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue) {
70
101
  manifold::MeshGL mesh = body.GetMeshGL();
71
102
  std::optional<std::array<float, 4>> color = valueToColor(colorValue);
@@ -96,7 +96,8 @@ def test_return_hook_fires_for_user_function():
96
96
  path = _write("function double(x) = x * 2; y = double(21); cube(y);")
97
97
  returns = []
98
98
 
99
- def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None):
99
+ def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
100
+ generate_partial=None):
100
101
  return ("continue", {})
101
102
 
102
103
  def return_hook(name, value, depth):
@@ -107,6 +108,52 @@ def test_return_hook_fires_for_user_function():
107
108
  assert returns == [("double", 42.0, 1)]
108
109
 
109
110
 
111
+ def test_generate_partial_during_a_live_pause():
112
+ # Two statements: by the time the hook fires on line 2 (forced=True,
113
+ # the breakpoint() call), cube(1) has already fully resolved and
114
+ # generated once via the normal resolve pass -- but sphere(5) on the
115
+ # next line hasn't been reached yet. generate_partial() should return
116
+ # geometry from whatever's ALREADY been resolved at that exact moment
117
+ # (just the cube), proving it reads live in-progress state, not a
118
+ # snapshot from a completed evaluate().
119
+ path = _write("cube(1);\nbreakpoint();\nsphere(5);")
120
+ seen_partial_counts = []
121
+
122
+ def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
123
+ generate_partial=None):
124
+ if forced:
125
+ bodies = generate_partial()
126
+ seen_partial_counts.append(len(bodies))
127
+ return ("continue", {})
128
+
129
+ ev = Evaluator(debug_hook=debug_hook)
130
+ bodies, _ = ev.evaluate(path)
131
+ assert seen_partial_counts == [1] # only the cube, at the moment breakpoint() paused
132
+ assert len(bodies) == 2 # both cube and sphere in the final, complete result
133
+
134
+
135
+ def test_generate_partial_on_an_empty_tree_returns_empty_not_an_error():
136
+ # At the very first statement's forced pause, nothing has resolved yet --
137
+ # generate_partial() must handle an empty tree gracefully (empty list),
138
+ # not raise. (A genuine GenerateFn exception mid-partial-render is real
139
+ # error-propagation behavior too -- see generatePartialTrampoline's own
140
+ # doc comment in module.cpp -- but isn't exercised here: this backend's
141
+ # builtins are permissive about invalid dimensions, same as real OpenSCAD,
142
+ # so there's no reliable way to force one from a script.)
143
+ path = _write("breakpoint();\ncube(1);")
144
+ seen = []
145
+
146
+ def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
147
+ generate_partial=None):
148
+ if forced:
149
+ seen.append(generate_partial())
150
+ return ("continue", {})
151
+
152
+ ev = Evaluator(debug_hook=debug_hook)
153
+ ev.evaluate(path)
154
+ assert seen == [[]]
155
+
156
+
110
157
  def test_dyn_explicit_distinguishes_seeded_from_script_assigned():
111
158
  path = _write("$fn = 72; cube(1);")
112
159
  ev = Evaluator()