openscad-cpp-evaluator 0.1.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 (194) hide show
  1. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/.github/workflows/wheels.yml +1 -1
  2. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/bindings/module.cpp +201 -61
  4. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/evaluator.hpp +35 -0
  5. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/pyproject.toml +1 -1
  6. openscad_cpp_evaluator-0.3.0/python/openscad_cpp_evaluator/__init__.py +379 -0
  7. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/csg_generate.cpp +37 -6
  8. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/csg_resolve.cpp +1 -0
  9. openscad_cpp_evaluator-0.3.0/tests/test_python_bindings.py +187 -0
  10. openscad_cpp_evaluator-0.1.0/python/openscad_cpp_evaluator/__init__.py +0 -207
  11. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/.github/workflows/ci.yml +0 -0
  12. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/.gitignore +0 -0
  13. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/.gitmodules +0 -0
  14. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/CLAUDE.md +0 -0
  15. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/LICENSE +0 -0
  17. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/README.md +0 -0
  18. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/bindings/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/examples/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/examples/eval_perf_benchmark.cpp +0 -0
  21. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/examples/manifold_cache_reuse.cpp +0 -0
  22. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/examples/minimal_debugger.cpp +0 -0
  23. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  24. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/.gitignore +0 -0
  25. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  26. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/README.md +0 -0
  28. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  29. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  30. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  31. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  32. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  33. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  34. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  35. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  36. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  37. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  39. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  40. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  41. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  42. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  43. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  44. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  45. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  46. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  47. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  48. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  49. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  50. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  51. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  52. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  53. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  54. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  55. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  56. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  57. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  58. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  59. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  60. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  61. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  62. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  63. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  64. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  65. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  66. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  67. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  68. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  69. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  70. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  71. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  72. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  73. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  74. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  75. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  76. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  77. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  78. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  79. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  80. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  81. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  82. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  83. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  84. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  85. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  86. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  87. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  88. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  89. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  90. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bundled_font_path.hpp.in +0 -0
  91. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  92. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  93. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  94. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  95. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  96. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  97. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  98. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  99. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  100. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  101. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  102. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  103. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  104. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  105. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  106. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  107. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  108. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  109. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  110. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  111. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  112. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  113. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  114. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  115. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  117. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  118. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  119. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  120. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/resources/fonts/LICENSE +0 -0
  121. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  122. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/CMakeLists.txt +0 -0
  123. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/booleans.cpp +0 -0
  124. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/builtins.hpp +0 -0
  125. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/call_args.cpp +0 -0
  126. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/color.cpp +0 -0
  127. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/control.cpp +0 -0
  128. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/extrude.cpp +0 -0
  129. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/function_builtins.cpp +0 -0
  130. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/import.cpp +0 -0
  131. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/modifiers.cpp +0 -0
  132. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/primitives_2d.cpp +0 -0
  133. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/primitives_3d.cpp +0 -0
  134. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/registry.cpp +0 -0
  135. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/roof.cpp +0 -0
  136. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/segments.cpp +0 -0
  137. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/surface.cpp +0 -0
  138. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/text.cpp +0 -0
  139. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/topology.cpp +0 -0
  140. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/builtins/transforms.cpp +0 -0
  141. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/bytecode_compiler.cpp +0 -0
  142. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/bytecode_vm.cpp +0 -0
  143. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/colored_body.cpp +0 -0
  144. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/css_colors.cpp +0 -0
  145. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/debug/debug_repl.cpp +0 -0
  146. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/debug_profile.cpp +0 -0
  147. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/eval_context.cpp +0 -0
  148. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/eval_error.cpp +0 -0
  149. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/eval_use.cpp +0 -0
  150. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/export.cpp +0 -0
  151. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/expr_eval.cpp +0 -0
  152. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/import/dxf_import.cpp +0 -0
  153. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/import/mesh_import.cpp +0 -0
  154. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/import/surface_load.cpp +0 -0
  155. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/import/svg_import.cpp +0 -0
  156. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/manifold_cache.cpp +0 -0
  157. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/stmt_eval.cpp +0 -0
  158. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/text/stb_font_provider.cpp +0 -0
  159. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/text/text_metrics.cpp +0 -0
  160. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/user_calls.cpp +0 -0
  161. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/value.cpp +0 -0
  162. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/src/zip_stored.cpp +0 -0
  163. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/CMakeLists.txt +0 -0
  164. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_booleans.cpp +0 -0
  165. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_bytecode_compiler.cpp +0 -0
  166. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_cli.cpp +0 -0
  167. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_control_flow.cpp +0 -0
  168. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_csg_tree.cpp +0 -0
  169. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_debug_hooks.cpp +0 -0
  170. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_dxf_svg_import.cpp +0 -0
  171. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_eval_context.cpp +0 -0
  172. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_export.cpp +0 -0
  173. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_expr_eval.cpp +0 -0
  174. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_extrude_roof.cpp +0 -0
  175. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_function_builtins.cpp +0 -0
  176. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_helpers.hpp +0 -0
  177. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_import_export.cpp +0 -0
  178. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_manifold_cache.cpp +0 -0
  179. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_multi_color_merge.cpp +0 -0
  180. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_primitives.cpp +0 -0
  181. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_profiling.cpp +0 -0
  182. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_scoping.cpp +0 -0
  183. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_surface.cpp +0 -0
  184. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_tail_calls.cpp +0 -0
  185. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_text.cpp +0 -0
  186. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_transforms.cpp +0 -0
  187. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_use.cpp +0 -0
  188. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_value.cpp +0 -0
  189. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_viewport_params.cpp +0 -0
  190. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tests/test_zip_stored.cpp +0 -0
  191. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/CMakeLists.txt +0 -0
  192. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/cli_lib.cpp +0 -0
  193. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/cli_lib.hpp +0 -0
  194. {openscad_cpp_evaluator-0.1.0 → openscad_cpp_evaluator-0.3.0}/tools/cli/main.cpp +0 -0
@@ -82,7 +82,7 @@ jobs:
82
82
  # C:\msys64 (not on PATH); its bison/flex satisfy the parser
83
83
  # submodule's grammar version requirement.
84
84
  CIBW_BEFORE_ALL_WINDOWS: 'C:\msys64\usr\bin\bash.exe -lc "pacman -Sy --noconfirm bison flex"'
85
- CIBW_TEST_COMMAND: "python -c \"import openscad_cpp_evaluator\""
85
+ CIBW_TEST_COMMAND: "python {package}/tests/test_python_bindings.py"
86
86
 
87
87
  - uses: actions/upload-artifact@v7
88
88
  with:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.1.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
@@ -13,12 +13,16 @@
13
13
 
14
14
  #include <nanobind/nanobind.h>
15
15
  #include <nanobind/ndarray.h>
16
+ #include <nanobind/stl/shared_ptr.h>
16
17
  #include <nanobind/stl/string.h>
17
18
 
18
19
  #include "openscad_cpp_evaluator/colored_body.hpp"
20
+ #include "openscad_cpp_evaluator/csg_node.hpp"
19
21
  #include "openscad_cpp_evaluator/eval_context.hpp"
20
22
  #include "openscad_cpp_evaluator/eval_use.hpp"
21
23
  #include "openscad_cpp_evaluator/evaluator.hpp"
24
+ #include "openscad_cpp_evaluator/manifold_cache.hpp"
25
+ #include "openscad_cpp_evaluator/profile.hpp"
22
26
  #include "openscad_cpp_evaluator/value.hpp"
23
27
 
24
28
  #include "openscad_cpp_parser/api.hpp"
@@ -67,6 +71,44 @@ std::unordered_map<std::string, oscadeval::Value> toViewportParams(nb::dict d) {
67
71
  return out;
68
72
  }
69
73
 
74
+ // A facade class handle (openscad_cpp_evaluator.<name>), imported lazily at
75
+ // first use and leaked deliberately -- never destroyed, so no nb::object
76
+ // destructor runs after Python finalization (the app exits via os._exit,
77
+ // which skips finalizers; a normal-shutdown consumer would otherwise crash).
78
+ nb::object facadeAttr(const char* name) {
79
+ static nb::object* mod = new nb::object(nb::module_::import_("openscad_cpp_evaluator"));
80
+ return mod->attr(name);
81
+ }
82
+
83
+ nb::object valueToPy(const oscadeval::Value& v); // recursive
84
+
85
+ nb::object valueToPy(const oscadeval::Value& v) {
86
+ if (std::holds_alternative<std::monostate>(v)) return nb::none();
87
+ if (const bool* b = std::get_if<bool>(&v)) return nb::cast(*b);
88
+ if (const double* d = std::get_if<double>(&v)) return nb::cast(*d);
89
+ if (const std::string* s = std::get_if<std::string>(&v)) return nb::cast(*s);
90
+ if (const oscadeval::ListPtr* lp = std::get_if<oscadeval::ListPtr>(&v)) {
91
+ nb::list out;
92
+ if (*lp)
93
+ for (const oscadeval::Value& item : (*lp)->items) out.append(valueToPy(item));
94
+ return out;
95
+ }
96
+ if (const oscadeval::ObjectPtr* op = std::get_if<oscadeval::ObjectPtr>(&v)) {
97
+ nb::dict data;
98
+ if (*op)
99
+ for (const auto& [k, val] : (*op)->items) data[nb::str(k.c_str())] = valueToPy(val);
100
+ return facadeAttr("OscObject")(data);
101
+ }
102
+ // OscRange / FunctionLiteral: no direct Python analog -- wrap the
103
+ // OpenSCAD text so the debugger displays it cleanly (unquoted).
104
+ return facadeAttr("_ScadValue")(oscadeval::fmtValue(v));
105
+ }
106
+
107
+ nb::object posToPy(const oscad::Position* p) {
108
+ if (!p) return nb::none();
109
+ return facadeAttr("_Position")(p->line, p->column, p->origin, p->start_offset, p->end_offset);
110
+ }
111
+
70
112
  // One evaluated body's mesh + attributes, shaped to match what the renderer
71
113
  // already reads off a manifold3d mesh (vert_properties (nVert, numProp),
72
114
  // tri_verts (nTri, 3), run_original_id (nRun,), run_index (nRun+1,) -- the
@@ -124,30 +166,98 @@ nb::dict idSpansToDict(const std::vector<IdSpan>& idSpans) {
124
166
  return d;
125
167
  }
126
168
 
127
- // (list[body-dict], list[echo-str], dict[int, span]). Raises the C++ error
169
+ // CSGNode -> a facade `_CSGNode` (kind/params/bodies/is_builtin/children),
170
+ // recursively, eagerly converting every field to plain Python data. No
171
+ // lifetime extension needed -- CSGParams (Value-only, see csg_node.hpp) and
172
+ // ColoredBody never hold AST pointers, so the result is fully self-contained
173
+ // once built, unlike CSGNode::node itself (deliberately not exposed: the AST
174
+ // it points into does not outlive this binding call, and the CSG-tree dump
175
+ // consumer only ever needs kind/params/children/bodies -- see
176
+ // format_csg_tree's own doc comment).
177
+ nb::object csgNodeToPy(const oscadeval::CSGNode& node) {
178
+ nb::dict params;
179
+ for (const auto& [k, v] : node.params) params[nb::str(k.c_str())] = valueToPy(v);
180
+ nb::list bodies;
181
+ for (const oscadeval::ColoredBody& cb : node.bodies) {
182
+ if (!cb.body || cb.body->IsEmpty()) continue;
183
+ // ColoredBody here is const in this walk (tree ownership stays with
184
+ // the caller) -- GetMeshGL() itself is logically read-only, so a
185
+ // const_cast here is safe/local, mirroring bodyToDict's mutating
186
+ // signature only because manifold3d's own API isn't const-qualified.
187
+ bodies.append(bodyToDict(const_cast<oscadeval::ColoredBody&>(cb)));
188
+ }
189
+ nb::list children;
190
+ for (const std::unique_ptr<oscadeval::CSGNode>& child : node.children) children.append(csgNodeToPy(*child));
191
+ return facadeAttr("_CSGNode")(node.kind, params, bodies, node.isBuiltin, children);
192
+ }
193
+
194
+ nb::list csgTreeToPy(const std::vector<std::unique_ptr<oscadeval::CSGNode>>& tree) {
195
+ nb::list out;
196
+ for (const std::unique_ptr<oscadeval::CSGNode>& node : tree) out.append(csgNodeToPy(*node));
197
+ return out;
198
+ }
199
+
200
+ nb::object profileResultToPy(const std::optional<oscadeval::ProfileResult>& pr) {
201
+ if (!pr) return nb::none();
202
+ nb::list sites;
203
+ for (const oscadeval::CallSiteProfile& s : pr->callSites) {
204
+ sites.append(facadeAttr("CallSiteProfile")(s.kind, s.name, s.callerName, s.callOrigin, s.callLine, s.declOrigin,
205
+ s.declLine, s.callCount, s.selfTime, s.cumulativeTime));
206
+ }
207
+ return facadeAttr("ProfileResult")(sites, pr->resolveTime, pr->generateTime, pr->totalTime, pr->unattributedTime);
208
+ }
209
+
210
+ // ctx.dyn / ctx.dynExplicit -> (dict[str, Any], set[str]) -- every currently-
211
+ // visible $-prefixed dynamic variable, and the subset the script itself
212
+ // assigned (vs. merely seeded by viewportParams). ctx is caller-owned in this
213
+ // port (see evaluate()'s own doc comment in evaluator.hpp), so this is a
214
+ // direct readback, not a separate tracking mechanism.
215
+ std::pair<nb::dict, nb::object> dynStateToPy(const oscadeval::EvalContext& ctx) {
216
+ nb::dict dyn;
217
+ for (const auto& [name, v] : ctx.dyn.items()) dyn[nb::str(name.c_str())] = valueToPy(v);
218
+ nb::set explicitNames;
219
+ for (const auto& [name, isExplicit] : ctx.dynExplicit.items())
220
+ if (isExplicit) explicitNames.add(nb::str(name.c_str()));
221
+ return {dyn, explicitNames};
222
+ }
223
+
224
+ // (list[body-dict], list[echo-str], dict[int, span], list[_CSGNode],
225
+ // ProfileResult|None, dict[str, Any], set[str]). Raises the C++ error
128
226
  // message as a Python exception on ParseError/EvalError (nanobind maps
129
227
  // std::exception -> RuntimeError, whose str() is the already-formatted
130
228
  // "ERROR:..."/caret diagnostic; the facade re-raises as EvalError).
131
- nb::object evaluate(const std::string& path, nb::dict viewportParams) {
229
+ nb::object evaluate(const std::string& path, nb::dict viewportParams,
230
+ std::shared_ptr<oscadeval::ManifoldCache> manifoldCache, bool profile) {
132
231
  std::unordered_map<std::string, oscadeval::Value> vp = toViewportParams(viewportParams);
133
232
 
134
233
  std::vector<oscadeval::ColoredBody> bodies;
135
234
  std::vector<std::string> echoes;
136
235
  std::vector<IdSpan> idSpans;
236
+ std::vector<std::unique_ptr<oscadeval::CSGNode>> csgTree;
237
+ std::optional<oscadeval::ProfileResult> profileResult;
238
+ nb::dict dyn;
239
+ nb::object dynExplicit;
137
240
  {
138
241
  nb::gil_scoped_release rel;
139
242
  auto logFn = [&echoes](const std::string& m) { echoes.push_back(m); };
140
243
  std::vector<std::unique_ptr<oscad::ASTNode>> ast = oscad::getASTFromFile(path);
141
244
  oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(ast, path, logFn);
142
- oscadeval::Evaluator ev(logFn);
245
+ oscadeval::Evaluator ev(logFn, nullptr, manifoldCache, oscadeval::DebugHooks{}, profile);
143
246
  oscadeval::EvalContext ctx = oscadeval::EvalContext::makeRoot(used.rootScope.get());
144
247
  bodies = oscadeval::toRenderableBodies(ev.evaluate(used.processedNodes, ctx, vp));
145
248
  collectIdSpans(ev, idSpans);
249
+ csgTree = std::move(ev.csgTree);
250
+ profileResult = std::move(ev.profileResult);
251
+ {
252
+ nb::gil_scoped_acquire g; // building Python objects needs the GIL back
253
+ std::tie(dyn, dynExplicit) = dynStateToPy(ctx);
254
+ }
146
255
  }
147
256
 
148
257
  nb::list echoList;
149
258
  for (const std::string& s : echoes) echoList.append(s);
150
- return nb::make_tuple(bodiesToList(bodies), echoList, idSpansToDict(idSpans));
259
+ return nb::make_tuple(bodiesToList(bodies), echoList, idSpansToDict(idSpans), csgTreeToPy(csgTree),
260
+ profileResultToPy(profileResult), dyn, dynExplicit);
151
261
  }
152
262
 
153
263
  // ------------------------------------------------------------------------
@@ -155,44 +265,6 @@ nb::object evaluate(const std::string& path, nb::dict viewportParams) {
155
265
  // Python conversion for locals inspection.
156
266
  // ------------------------------------------------------------------------
157
267
 
158
- // A facade class handle (openscad_cpp_evaluator.<name>), imported lazily at
159
- // first use and leaked deliberately -- never destroyed, so no nb::object
160
- // destructor runs after Python finalization (the app exits via os._exit,
161
- // which skips finalizers; a normal-shutdown consumer would otherwise crash).
162
- nb::object facadeAttr(const char* name) {
163
- static nb::object* mod = new nb::object(nb::module_::import_("openscad_cpp_evaluator"));
164
- return mod->attr(name);
165
- }
166
-
167
- nb::object valueToPy(const oscadeval::Value& v); // recursive
168
-
169
- nb::object valueToPy(const oscadeval::Value& v) {
170
- if (std::holds_alternative<std::monostate>(v)) return nb::none();
171
- if (const bool* b = std::get_if<bool>(&v)) return nb::cast(*b);
172
- if (const double* d = std::get_if<double>(&v)) return nb::cast(*d);
173
- if (const std::string* s = std::get_if<std::string>(&v)) return nb::cast(*s);
174
- if (const oscadeval::ListPtr* lp = std::get_if<oscadeval::ListPtr>(&v)) {
175
- nb::list out;
176
- if (*lp)
177
- for (const oscadeval::Value& item : (*lp)->items) out.append(valueToPy(item));
178
- return out;
179
- }
180
- if (const oscadeval::ObjectPtr* op = std::get_if<oscadeval::ObjectPtr>(&v)) {
181
- nb::dict data;
182
- if (*op)
183
- for (const auto& [k, val] : (*op)->items) data[nb::str(k.c_str())] = valueToPy(val);
184
- return facadeAttr("OscObject")(data);
185
- }
186
- // OscRange / FunctionLiteral: no direct Python analog -- wrap the
187
- // OpenSCAD text so the debugger displays it cleanly (unquoted).
188
- return facadeAttr("_ScadValue")(oscadeval::fmtValue(v));
189
- }
190
-
191
- nb::object posToPy(const oscad::Position* p) {
192
- if (!p) return nb::none();
193
- return facadeAttr("_Position")(p->line, p->column, p->origin, p->start_offset, p->end_offset);
194
- }
195
-
196
268
  // One debug frame -> {"local_scope", "outer_scope", "dyn_names"}, the shape
197
269
  // the debugger pane's _filtered_vars consumes.
198
270
  nb::dict frameToDict(const oscadeval::DebugFrame& f) {
@@ -220,13 +292,34 @@ nb::list callStackToPy(const std::vector<oscadeval::CallStackFrame>& cs) {
220
292
  return out;
221
293
  }
222
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
+
223
315
  // Trampoline: C++ debug hook -> the Python DebugSession hook. Runs with the
224
- // GIL held (reacquired by the caller). `getFrame`/`callStack` are valid only
225
- // for this synchronous call, so the get_frames closure is only ever invoked
226
- // 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.
227
319
  oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, bool forced, const std::string& origin,
228
320
  const std::vector<oscadeval::CallStackFrame>& callStack,
229
- const oscadeval::DebugFramesFn& getFrame) {
321
+ const oscadeval::DebugFramesFn& getFrame,
322
+ const GeneratePartialFn& generatePartial) {
230
323
  auto getFramesPy = nb::cpp_function([&callStack, &getFrame]() -> nb::object {
231
324
  std::vector<oscadeval::DebugFrame> frames = getFrame();
232
325
  nb::list allFrameLocals;
@@ -241,9 +334,11 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
241
334
  }
242
335
  return nb::make_tuple(nb::make_tuple(lastLocals, allFrameLocals), callStackToPy(callStack));
243
336
  });
337
+ nb::object generatePartialPy = generatePartialTrampoline(generatePartial);
244
338
 
245
339
  nb::object ret = hook(line, depth, nb::arg("forced") = forced, nb::arg("expr_level") = false,
246
- 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);
247
342
  nb::tuple t = nb::cast<nb::tuple>(ret);
248
343
  oscadeval::DebugAction action;
249
344
  action.stop = (nb::cast<std::string>(t[0]) == "stop");
@@ -255,57 +350,92 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
255
350
  // Trampoline: C++ errorBreak -> the Python DebugSession error hook.
256
351
  void callPyErrorBreak(nb::handle errorBreak, int line, const std::string& header, const std::string& origin,
257
352
  const std::vector<oscadeval::CallStackFrame>& callStack,
258
- const oscadeval::DebugFramesFn& getFrame) {
353
+ const oscadeval::DebugFramesFn& getFrame, const GeneratePartialFn& generatePartial) {
259
354
  std::vector<oscadeval::DebugFrame> frames = getFrame();
260
355
  nb::list allFrameLocals;
261
356
  for (const oscadeval::DebugFrame& f : frames) allFrameLocals.append(frameToDict(f));
262
- 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);
360
+ }
361
+
362
+ // Trampoline: C++ returnHook -> the Python DebugSession return hook. Only
363
+ // fires for user function/function-literal calls (never modules, never
364
+ // builtins -- see DebugHooks::returnHook's own doc comment).
365
+ void callPyReturnHook(nb::handle returnHook, const std::string& name, const oscadeval::Value& result, int depth) {
366
+ returnHook(name, valueToPy(result), depth);
263
367
  }
264
368
 
265
369
  // evaluate() with the debugger wired in. echo is delivered live via echoFn
266
370
  // (not batched), so a paused session can still print. Returns (bodies, [],
267
- // id_to_node). The GIL is released around the C++ evaluate and reacquired
268
- // inside every callback trampoline; the actual pause blocks on the Python
269
- // side (threading.Event) so the GUI thread keeps running.
371
+ // id_to_node, dyn, dyn_explicit). The GIL is released around the C++
372
+ // evaluate and reacquired inside every callback trampoline; the actual pause
373
+ // blocks on the Python side (threading.Event) so the GUI thread keeps running.
270
374
  nb::object debugEvaluate(const std::string& path, nb::dict viewportParams, nb::callable debugHook,
271
- nb::callable errorBreak, nb::callable echoFn) {
375
+ nb::callable errorBreak, nb::callable echoFn,
376
+ std::shared_ptr<oscadeval::ManifoldCache> manifoldCache, nb::object returnHook) {
272
377
  std::unordered_map<std::string, oscadeval::Value> vp = toViewportParams(viewportParams);
273
378
 
274
379
  std::vector<oscadeval::ColoredBody> bodies;
275
380
  std::vector<IdSpan> idSpans;
276
381
  std::exception_ptr err;
382
+ nb::dict dyn;
383
+ nb::object dynExplicit;
277
384
  {
278
385
  nb::gil_scoped_release rel;
279
386
  auto echoCpp = [&echoFn](const std::string& m) {
280
387
  nb::gil_scoped_acquire g;
281
388
  echoFn(m);
282
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
+ };
283
402
  oscadeval::DebugHooks hooks;
284
403
  hooks.debugHook = [&](int line, int depth, bool forced, const std::string& origin,
285
404
  const std::vector<oscadeval::CallStackFrame>& cs,
286
405
  const oscadeval::DebugFramesFn& gf) -> oscadeval::DebugAction {
287
406
  nb::gil_scoped_acquire g;
288
- return callPyDebugHook(debugHook, line, depth, forced, origin, cs, gf);
407
+ return callPyDebugHook(debugHook, line, depth, forced, origin, cs, gf, generatePartial);
289
408
  };
290
409
  hooks.errorBreak = [&](int line, const std::string& header, const std::string& origin,
291
410
  const std::vector<oscadeval::CallStackFrame>& cs, const oscadeval::DebugFramesFn& gf) {
292
411
  nb::gil_scoped_acquire g;
293
- callPyErrorBreak(errorBreak, line, header, origin, cs, gf);
412
+ callPyErrorBreak(errorBreak, line, header, origin, cs, gf, generatePartial);
294
413
  };
414
+ if (!returnHook.is_none()) {
415
+ hooks.returnHook = [&](const std::string& name, const oscadeval::Value& result, int depth) {
416
+ nb::gil_scoped_acquire g;
417
+ callPyReturnHook(returnHook, name, result, depth);
418
+ };
419
+ }
295
420
  try {
296
421
  std::vector<std::unique_ptr<oscad::ASTNode>> ast = oscad::getASTFromFile(path);
297
422
  oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(ast, path, echoCpp);
298
- oscadeval::Evaluator ev(echoCpp, nullptr, nullptr, hooks, false);
423
+ oscadeval::Evaluator ev(echoCpp, nullptr, manifoldCache, hooks, false);
424
+ evPtr = &ev;
299
425
  oscadeval::EvalContext ctx = oscadeval::EvalContext::makeRoot(used.rootScope.get());
300
426
  bodies = oscadeval::toRenderableBodies(ev.evaluate(used.processedNodes, ctx, vp));
301
427
  collectIdSpans(ev, idSpans);
428
+ {
429
+ nb::gil_scoped_acquire g;
430
+ std::tie(dyn, dynExplicit) = dynStateToPy(ctx);
431
+ }
302
432
  } catch (...) {
303
433
  err = std::current_exception();
304
434
  }
305
435
  }
306
436
  if (err) std::rethrow_exception(err); // re-raised with the GIL held -> Python exception
307
437
 
308
- return nb::make_tuple(bodiesToList(bodies), nb::list(), idSpansToDict(idSpans));
438
+ return nb::make_tuple(bodiesToList(bodies), nb::list(), idSpansToDict(idSpans), dyn, dynExplicit);
309
439
  }
310
440
 
311
441
  // Top-level declaration names + source spans, for the editor's completion and
@@ -358,11 +488,21 @@ nb::list parseDecls(const std::string& path) {
358
488
 
359
489
  NB_MODULE(_openscad_cpp_evaluator, m) {
360
490
  m.doc() = "C++ OpenSCAD evaluator (nanobind binding)";
361
- m.def("evaluate", &evaluate, nb::arg("path"), nb::arg("viewport_params"),
362
- "Evaluate a .scad file; return (list of body dicts with raw mesh arrays, list of echo strings).");
491
+
492
+ // Opt-in, shared across evaluate() calls -- see manifold_cache.hpp.
493
+ // Only a constructor and clear() are ever called from Python (get/put
494
+ // are internal to generateTree()'s own C++ implementation).
495
+ nb::class_<oscadeval::ManifoldCache>(m, "ManifoldCache")
496
+ .def(nb::init<>())
497
+ .def("clear", &oscadeval::ManifoldCache::clear);
498
+
499
+ m.def("evaluate", &evaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("manifold_cache") = nullptr,
500
+ nb::arg("profile") = false,
501
+ "Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit).");
363
502
  m.def("parse_decls", &parseDecls, nb::arg("path"),
364
503
  "Parse a .scad file; return top-level declaration (namespace, name, start, end, line, column, origin) tuples.");
365
504
  m.def("debug_evaluate", &debugEvaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("debug_hook"),
366
- nb::arg("error_break"), nb::arg("echo_fn"),
367
- "Evaluate with the debugger wired in; returns (bodies, [], id_to_node). Callbacks fire under the GIL.");
505
+ nb::arg("error_break"), nb::arg("echo_fn"), nb::arg("manifold_cache") = nullptr,
506
+ nb::arg("return_hook") = nb::none(),
507
+ "Evaluate with the debugger wired in; returns (bodies, [], id_to_node, dyn, dyn_explicit). Callbacks fire under the GIL.");
368
508
  }
@@ -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
@@ -336,6 +356,15 @@ public:
336
356
  // reference's Evaluator.profile_result.
337
357
  std::optional<ProfileResult> profileResult;
338
358
 
359
+ // The resolved (and, after evaluate() runs, generated) CSG tree from the
360
+ // most recent evaluate() call -- moved here from evaluateImpl's local
361
+ // `tree` after generateTree() populates each node's own `bodies`, so a
362
+ // caller can inspect tree shape/params after evaluate() returns (CSG-tree
363
+ // dump, live partial-render). Empty for direct resolveTree()/
364
+ // generateTree() callers that bypass evaluate() -- same scoping as
365
+ // profileResult above. Mirrors the reference's Evaluator.csg_tree.
366
+ std::vector<std::unique_ptr<CSGNode>> csgTree;
367
+
339
368
  private:
340
369
  // Shared body for resolveTree()'s/evaluate()'s two overloads (owning vs.
341
370
  // raw-pointer node list) -- a template rather than duplicating each
@@ -348,6 +377,12 @@ private:
348
377
  std::vector<ColoredBody> evaluateImpl(const NodeList& nodes, EvalContext& ctx,
349
378
  const std::unordered_map<std::string, Value>& viewportParams);
350
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
+
351
386
  // Evaluates one top-level element of a `[...]` list literal (a plain
352
387
  // expression, or one of the 6 list-comprehension clause kinds --
353
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.1.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"