openscad-cpp-evaluator 0.51.0__tar.gz → 0.51.3__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 (208) hide show
  1. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/.github/workflows/wheels.yml +18 -1
  2. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/bytecode_vm.hpp +11 -1
  4. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/pyproject.toml +1 -1
  5. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/topology.cpp +29 -2
  6. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/bytecode_vm.cpp +6 -3
  7. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_booleans.cpp +46 -0
  8. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_debug_hooks.cpp +42 -0
  9. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_profiling.cpp +24 -3
  10. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/uv.lock +1 -1
  11. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/.github/workflows/ci.yml +0 -0
  12. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/.gitignore +0 -0
  13. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/.gitmodules +0 -0
  14. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/CLAUDE.md +0 -0
  15. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/LICENSE +0 -0
  17. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/README.md +0 -0
  18. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/bindings/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/bindings/ast_to_py.cpp +0 -0
  20. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/bindings/module.cpp +0 -0
  21. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/cmake/embed_font.cmake +0 -0
  22. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/examples/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/examples/eval_perf_benchmark.cpp +0 -0
  24. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/examples/manifold_cache_reuse.cpp +0 -0
  25. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/examples/minimal_debugger.cpp +0 -0
  26. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  27. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/.gitignore +0 -0
  28. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  29. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  30. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/README.md +0 -0
  31. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  32. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  33. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  34. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  35. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  36. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  37. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  38. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  39. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  40. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  41. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  42. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  43. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  44. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  45. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  46. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  47. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/api.cpp +0 -0
  48. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  49. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  50. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  51. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  52. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  53. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  54. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  55. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  56. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  57. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  58. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  59. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  60. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  61. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  62. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  63. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  64. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/position.cpp +0 -0
  65. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  66. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  67. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  68. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  69. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  70. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  71. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  72. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  73. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  74. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  75. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  76. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  77. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  78. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  79. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  80. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  81. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  82. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  83. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  84. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  85. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  86. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  87. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  88. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  89. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  90. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  91. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  92. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  93. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  94. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  95. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  96. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  97. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  98. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  99. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  100. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  101. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  102. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  103. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  104. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  105. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  106. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  107. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  108. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/export.hpp +0 -0
  109. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  110. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  111. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  112. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  113. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  114. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  115. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  116. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  117. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  118. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  119. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  120. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  121. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  122. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  123. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/value.hpp +0 -0
  124. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  125. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/python/openscad_cpp_evaluator/__init__.py +0 -0
  126. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/resources/fonts/LICENSE +0 -0
  127. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  128. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/CMakeLists.txt +0 -0
  129. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/booleans.cpp +0 -0
  130. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/builtins.hpp +0 -0
  131. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/call_args.cpp +0 -0
  132. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/color.cpp +0 -0
  133. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/control.cpp +0 -0
  134. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/dxf_dim.cpp +0 -0
  135. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/extrude.cpp +0 -0
  136. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/function_builtins.cpp +0 -0
  137. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/import.cpp +0 -0
  138. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/modifiers.cpp +0 -0
  139. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/primitives_2d.cpp +0 -0
  140. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/primitives_3d.cpp +0 -0
  141. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/registry.cpp +0 -0
  142. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/roof.cpp +0 -0
  143. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/segments.cpp +0 -0
  144. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/surface.cpp +0 -0
  145. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/text.cpp +0 -0
  146. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/builtins/transforms.cpp +0 -0
  147. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/bytecode_compiler.cpp +0 -0
  148. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/colored_body.cpp +0 -0
  149. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/csg_generate.cpp +0 -0
  150. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/csg_resolve.cpp +0 -0
  151. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/css_colors.cpp +0 -0
  152. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/debug/debug_repl.cpp +0 -0
  153. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/debug_profile.cpp +0 -0
  154. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/eval_context.cpp +0 -0
  155. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/eval_error.cpp +0 -0
  156. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/eval_use.cpp +0 -0
  157. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/export.cpp +0 -0
  158. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/expr_eval.cpp +0 -0
  159. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/import/dxf_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/import/mesh_import.cpp +0 -0
  161. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/import/surface_load.cpp +0 -0
  162. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/import/svg_import.cpp +0 -0
  163. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/manifold_cache.cpp +0 -0
  164. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/measure_geometry.cpp +0 -0
  165. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/mesh_check.cpp +0 -0
  166. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/stmt_eval.cpp +0 -0
  167. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/text/font_match.cpp +0 -0
  168. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/text/freetype_font_provider.cpp +0 -0
  169. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/text/text_metrics.cpp +0 -0
  170. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/user_calls.cpp +0 -0
  171. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/value.cpp +0 -0
  172. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/src/zip_stored.cpp +0 -0
  173. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/CMakeLists.txt +0 -0
  174. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_bytecode_compiler.cpp +0 -0
  175. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_children_separate.cpp +0 -0
  176. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_cli.cpp +0 -0
  177. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_control_flow.cpp +0 -0
  178. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_csg_tree.cpp +0 -0
  179. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_dxf_svg_import.cpp +0 -0
  180. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_eval_context.cpp +0 -0
  181. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_export.cpp +0 -0
  182. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_expr_eval.cpp +0 -0
  183. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_extrude_roof.cpp +0 -0
  184. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_feature_detection.cpp +0 -0
  185. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_function_builtins.cpp +0 -0
  186. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_helpers.hpp +0 -0
  187. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_import_export.cpp +0 -0
  188. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_manifold_cache.cpp +0 -0
  189. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_mesh_check.cpp +0 -0
  190. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_multi_color_merge.cpp +0 -0
  191. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_primitives.cpp +0 -0
  192. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_python_bindings.py +0 -0
  193. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_render_expr.cpp +0 -0
  194. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_scope_trail.cpp +0 -0
  195. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_scoping.cpp +0 -0
  196. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_surface.cpp +0 -0
  197. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_tail_calls.cpp +0 -0
  198. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_text.cpp +0 -0
  199. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_transforms.cpp +0 -0
  200. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_unexpected_args.cpp +0 -0
  201. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_use.cpp +0 -0
  202. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_value.cpp +0 -0
  203. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_viewport_params.cpp +0 -0
  204. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tests/test_zip_stored.cpp +0 -0
  205. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tools/cli/CMakeLists.txt +0 -0
  206. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tools/cli/cli_lib.cpp +0 -0
  207. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tools/cli/cli_lib.hpp +0 -0
  208. {openscad_cpp_evaluator-0.51.0 → openscad_cpp_evaluator-0.51.3}/tools/cli/main.cpp +0 -0
@@ -85,9 +85,26 @@ jobs:
85
85
  # grammar requires %require "3.8". No newer bison package is
86
86
  # available via dnf on this base image, so build one from source
87
87
  # (fast: a few seconds) rather than switching manylinux images.
88
+ # Mirrors first, ftp.gnu.org last: ftp.gnu.org went unreachable
89
+ # (curl 7, connection timed out on 443) on both architectures for
90
+ # two consecutive runs of the v0.51.3 release, and it is a single
91
+ # point of failure for every Linux wheel we ship.
92
+ # ftpmirror.gnu.org is GNU's own redirector to a nearby mirror.
93
+ # Every line stays at THIS indent on purpose -- a more-indented
94
+ # line in a `>-` folded scalar keeps its newline instead of
95
+ # folding to a space, which would split a URL from its curl.
96
+ # CURL FLAG CEILING: this runs against AlmaLinux 8's curl 7.61,
97
+ # NOT the host's. --retry (7.12.3), --retry-connrefused (7.52)
98
+ # and --connect-timeout (7.7) are fine; --retry-all-errors is
99
+ # 7.71 and made every Linux wheel fail with
100
+ # "option --retry-all-errors: is unknown". Check `Added:` in
101
+ # curl/docs/cmdline-opts/<opt>.md before adding a flag here --
102
+ # testing the command on a Mac proves nothing about this curl.
88
103
  CIBW_BEFORE_ALL_LINUX: >-
89
104
  dnf install -y flex &&
90
- curl -fsSL https://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz -o /tmp/bison.tar.gz &&
105
+ { curl -fsSL --retry 3 --retry-connrefused --connect-timeout 20 https://ftpmirror.gnu.org/bison/bison-3.8.2.tar.gz -o /tmp/bison.tar.gz ||
106
+ curl -fsSL --retry 3 --retry-connrefused --connect-timeout 20 https://mirrors.kernel.org/gnu/bison/bison-3.8.2.tar.gz -o /tmp/bison.tar.gz ||
107
+ curl -fsSL --retry 3 --retry-connrefused --connect-timeout 20 https://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz -o /tmp/bison.tar.gz ; } &&
91
108
  cd /tmp && tar xzf bison.tar.gz && cd bison-3.8.2 &&
92
109
  ./configure --prefix=/usr/local && make -j$(nproc) && make install
93
110
  # Mirrors ci.yml's macOS step: the hosted runner image ships an
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.51.0
3
+ Version: 0.51.3
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -12,6 +12,7 @@
12
12
  #include <cstdint>
13
13
  #include <memory>
14
14
  #include <optional>
15
+ #include <deque>
15
16
  #include <vector>
16
17
 
17
18
  namespace oscadeval {
@@ -155,7 +156,16 @@ struct VmFrame {
155
156
  std::vector<bool> bound;
156
157
  std::vector<std::vector<Value>> accumStack;
157
158
  std::vector<IterList> iterLists;
158
- std::vector<EvalContext> ctxChain;
159
+ // std::deque, NOT vector: Evaluator::enterUserCall stores
160
+ // CallStackFrame::bodyCtx as a pointer to ctxChain.back() (see
161
+ // pushBracketedCallFrame/pushBracketedModuleFrame), and that pointer is
162
+ // read LATER, by any checkDebug() that walks the whole call stack to
163
+ // build per-frame debugger locals. A vector reallocates on the next
164
+ // push -- which every transform inside a compiled body does, via
165
+ // Op::PushBuiltinWrap -- leaving bodyCtx pointing at the moved-from
166
+ // element (null let_) and segfaulting buildDebugFrame. deque never
167
+ // invalidates references to existing elements on push_back/pop_back.
168
+ std::deque<EvalContext> ctxChain;
159
169
  // Still-open Op::PushBuiltinWrap brackets, LIFO, scoped to THIS frame's
160
170
  // own instruction stream -- mirrors accumStack/iterLists' own role for
161
171
  // their respective bracket pairs. Normal execution always drains this
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.51.0"
7
+ version = "0.51.3"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -970,6 +970,19 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
970
970
  if (i0[a] > n[a] - 2) i0[a] = n[a] - 2;
971
971
  t[a] = g[a] - static_cast<double>(i0[a]);
972
972
  }
973
+ // Outside the sampled block the answer is "outside", falling away
974
+ // with distance. Clamping instead (which is what the interpolation
975
+ // above does on its own) would smear the boundary values outward and
976
+ // extend the surface rather than close it.
977
+ double outside = 0.0;
978
+ for (int a = 0; a < 3; ++a) {
979
+ const double maxg = static_cast<double>(n[a] - 1);
980
+ const double raw = (pos[a] - origin[a]) / spacing[a];
981
+ if (raw < 0.0) outside = std::max(outside, -raw);
982
+ else if (raw > maxg) outside = std::max(outside, raw - maxg);
983
+ }
984
+ if (outside > 0.0) return invert ? (1.0 + outside) : -(1.0 + outside);
985
+
973
986
  double acc = 0.0;
974
987
  for (int c = 0; c < 8; ++c) {
975
988
  const size_t di = static_cast<size_t>(c & 1), dj = static_cast<size_t>((c >> 1) & 1),
@@ -980,8 +993,18 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
980
993
  return bandDistance(acc, isoLo, isoHi, invert);
981
994
  };
982
995
 
983
- manifold::Box bounds(manifold::vec3(origin[0], origin[1], origin[2]),
984
- manifold::vec3((*hi)[0], (*hi)[1], (*hi)[2]));
996
+ // Manifold closes the mesh where the surface meets the edge of the box it
997
+ // is given, and that closure follows the sample lattice -- a visible
998
+ // staircase on anything that reaches the boundary, which a gyroid does
999
+ // everywhere. So sample a PADDED box, let the ragged closure happen out
1000
+ // there, and cut back to the box actually asked for. The cut is then a
1001
+ // clean plane.
1002
+ //
1003
+ // Exactly the fix already applied to the 2D path (clipToBounds); it was
1004
+ // not applied here at the time because the 2D error was the one measured.
1005
+ const double pad = 2.0 * edge;
1006
+ manifold::Box bounds(manifold::vec3(origin[0] - pad, origin[1] - pad, origin[2] - pad),
1007
+ manifold::vec3((*hi)[0] + pad, (*hi)[1] + pad, (*hi)[2] + pad));
985
1008
  // tolerance -1: a positive value makes Manifold do EXTRA evaluations per
986
1009
  // output vertex to snap nearer the true surface. Against a fixed grid
987
1010
  // those only re-interpolate data already used -- cost, no information.
@@ -996,6 +1019,10 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
996
1019
  : manifold::Manifold::LevelSet(sampleGrid, bounds, edge, 0.0, -1.0, /*canParallel=*/true);
997
1020
 
998
1021
  if (solid.IsEmpty()) return {};
1022
+ solid = solid ^ manifold::Manifold::Cube(
1023
+ manifold::vec3((*hi)[0] - origin[0], (*hi)[1] - origin[1], (*hi)[2] - origin[2]))
1024
+ .Translate(manifold::vec3(origin[0], origin[1], origin[2]));
1025
+ if (solid.IsEmpty()) return {};
999
1026
  ColoredBody b;
1000
1027
  b.body = std::move(solid);
1001
1028
  std::vector<ColoredBody> out;
@@ -270,8 +270,11 @@ void pushBracketedCallFrame(Evaluator& ev, const CompiledChunk& chunk, const osc
270
270
  applyCompiledDefaultsToFrame(ev, chunk, *frame);
271
271
 
272
272
  // enterUserCall's own bodyCtx must point at frame->ctxChain's OWN
273
- // storage (stable: this VmFrame lives behind a unique_ptr, so its
274
- // address never moves even if vmCallStack_ itself reallocates), not
273
+ // storage (stable on two counts: this VmFrame lives behind a
274
+ // unique_ptr, so its address never moves even if vmCallStack_ itself
275
+ // reallocates, AND ctxChain is a std::deque, so a later push onto it
276
+ // never moves the element handed over here -- see its own doc comment,
277
+ // bytecode_vm.hpp, for the segfault a vector caused), not
275
278
  // a plain local -- a local `childCtx` here would be destroyed the
276
279
  // instant this function returns, but CallStackFrame::bodyCtx is read
277
280
  // LATER, by ANY subsequent checkDebug() call (this call's own nested
@@ -328,7 +331,7 @@ void pushBracketedModuleFrame(Evaluator& ev, const CompiledChunk& chunk, const o
328
331
  frame->logicalName = decl.name->name;
329
332
  frame->ownsModuleSplice = true;
330
333
  // enterUserCall's own bodyCtx must point at frame->ctxChain's OWN
331
- // storage, not the caller's own (about-to-be-destroyed) local
334
+ // (deque-stable) storage, not the caller's own (about-to-be-destroyed) local
332
335
  // `childCtx` -- see pushBracketedCallFrame's own doc comment, above,
333
336
  // for the full dangling-pointer hazard this avoids (same fix, same
334
337
  // root cause, module side).
@@ -1143,3 +1143,49 @@ TEST(LevelSet2d, A3dArrayWithA2dBoundsWarns) {
1143
1143
  EXPECT_FALSE(levelsetWarnings("levelset([[[1,2],[3,4]],[[5,6],[7,8]]], bounds=[[0,0],[1,1]]);")
1144
1144
  .empty());
1145
1145
  }
1146
+
1147
+ TEST(LevelSet, ASurfaceMeetingTheBoxIsCutCleanly) {
1148
+ // Manifold closes the mesh where the surface reaches the edge of the box
1149
+ // it is given, and that closure follows the SAMPLE LATTICE -- a visible
1150
+ // staircase on anything that touches the boundary. A gyroid touches it
1151
+ // everywhere, which is how this was found: the shape looked ragged next
1152
+ // to BOSL2's isosurface() of the same field.
1153
+ //
1154
+ // Sampling a padded box and cutting back makes the cut a plane. The
1155
+ // numeric form of that: a half-space is EXACTLY half the box, at every
1156
+ // resolution. Before the fix it was resolution-dependent.
1157
+ //
1158
+ // The 2D path had this fixed already (clipToBounds); 3D did not, because
1159
+ // 2D was the case that got measured at the time.
1160
+ for (const char* edge : {"2", "1", "0.5"}) {
1161
+ Evaluated e = evalSrc(std::string("levelset(function(x,y,z) x, "
1162
+ "bounds=[[-20,-20,-20],[20,20,20]], "
1163
+ "isovalue=[-1e18,0], edge=") +
1164
+ edge + ");");
1165
+ ASSERT_EQ(e.bodies.size(), 1u) << "edge=" << edge;
1166
+ EXPECT_NEAR(soleBody(e).Volume(), 32000.0, 1e-6) << "edge=" << edge;
1167
+ }
1168
+ }
1169
+
1170
+ TEST(LevelSet, PaddingDoesNotDisturbAShapeInsideTheBox) {
1171
+ // The padding is sampled outside the requested bounds, so a shape that
1172
+ // never reaches the boundary must come out exactly as before.
1173
+ Evaluated e = evalSrc(sphereFieldSrc(50, 30) +
1174
+ "levelset(f, bounds=[[-30,-30,-30],[30,30,30]], isovalue=20);");
1175
+ ASSERT_EQ(e.bodies.size(), 1u);
1176
+ const double analytic = 4.0 / 3.0 * 3.14159265358979 * 8000;
1177
+ EXPECT_NEAR(soleBody(e).Volume(), analytic, 0.01 * analytic);
1178
+ EXPECT_EQ(soleBody(e).Genus(), 0);
1179
+ }
1180
+
1181
+ TEST(LevelSet, AGridFieldIsAlsoCutCleanlyAtTheBox) {
1182
+ // The grid intake cannot sample beyond its data, so the sampler reads
1183
+ // "outside" past the block rather than clamping and smearing the edge
1184
+ // values outward. Same clean cut as the function form.
1185
+ Evaluated e = evalSrc(
1186
+ "N = 41;\nfunction co(t) = -20 + 40*t/(N-1);\n"
1187
+ "f = [for (i=[0:N-1]) [for (j=[0:N-1]) [for (k=[0:N-1]) co(i) ]]];\n"
1188
+ "levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=[-1e18,0]);");
1189
+ ASSERT_EQ(e.bodies.size(), 1u);
1190
+ EXPECT_NEAR(soleBody(e).Volume(), 32000.0, 40.0); // one sample layer of slack
1191
+ }
@@ -694,3 +694,45 @@ TEST(DebugHooksParity, BuiltinFunctionCallGetsNoCallSiteStop) {
694
694
  TEST(DebugHooksParity, ModifierAndItsWrappedChildBothStop) {
695
695
  EXPECT_EQ(recordStops("#cube(1);\n"), (std::vector<Stop>{{1, false, false}, {1, false, false}}));
696
696
  }
697
+
698
+ // An enclosing COMPILED module frame's debugger locals must survive the
699
+ // frame growing its own ctxChain. `enterUserCall` stores
700
+ // CallStackFrame::bodyCtx as a pointer into VmFrame::ctxChain, and any
701
+ // later push onto that chain -- which every transform inside a compiled
702
+ // body does, via Op::PushBuiltinWrap -- used to reallocate the container
703
+ // and leave bodyCtx dangling. buildDebugFrames then read a moved-from
704
+ // EvalContext (null let_) and segfaulted: the crash a user hit by setting
705
+ // a breakpoint inside a module wrapped in translate() and continuing.
706
+ TEST(DebugHooksParity, EnclosingCompiledFrameLocalsSurviveCtxChainGrowth) {
707
+ ScopedVm vm(true);
708
+ // The breakpoint is what makes this mixed: `inner` (line 1) is forced
709
+ // to interpret, while `mid` -- called from inside compiled `top` --
710
+ // keeps running compiled, so its callStack_ entry's bodyCtx comes from
711
+ // pushBracketedModuleFrame and points into VmFrame::ctxChain. The
712
+ // translate() in mid's body then pushes onto that same chain.
713
+ const std::string src = "module inner(b) { c = b; }\n"
714
+ "module mid(a) { translate([a,0,0]) inner(a); }\n"
715
+ "module top() { mid(7); }\n"
716
+ "top();\n";
717
+ bool sawMid = false;
718
+ DebugHooks hooks;
719
+ hooks.debugHook = [&](int line, int, bool, bool exprLevel, const std::string&,
720
+ const std::vector<CallStackFrame>&, const DebugFramesFn& getFrame) {
721
+ if (exprLevel || line != 1) return DebugAction{};
722
+ std::vector<DebugFrame> frames = getFrame();
723
+ for (const DebugFrame& f : frames) {
724
+ auto it = f.locals.find("a");
725
+ if (it == f.locals.end()) continue;
726
+ sawMid = true;
727
+ EXPECT_EQ(std::get<double>(it->second), 7.0);
728
+ }
729
+ return DebugAction{};
730
+ };
731
+ Evaluator ev(EchoFn{}, nullptr, nullptr, hooks);
732
+ ev.setFastContinueBreakpoints(std::unordered_map<std::string, std::set<int>>{{"<string>", {1}}});
733
+ auto ast = parseSrc(src);
734
+ auto scope = oscad::buildScopes(ast);
735
+ EvalContext ctx = EvalContext::makeRoot(scope.get());
736
+ ev.resolveTree(ast, ctx);
737
+ EXPECT_TRUE(sawMid) << "mid()'s own frame never appeared in the debugger frames";
738
+ }
@@ -182,9 +182,30 @@ TEST(ProfilePaths, SameCalleeUnderTwoCallersGetsSeparateNodes) {
182
182
  ASSERT_NE(workUnderHeavy, nullptr);
183
183
  // Two distinct nodes for the same callee -- the whole point.
184
184
  EXPECT_NE(workUnderLight, workUnderHeavy);
185
- // ...and the expensive path is attributed the larger share. This is the
186
- // fact the aggregated view cannot express at all.
187
- EXPECT_GT(workUnderHeavy->cumulativeTime, workUnderLight->cumulativeTime);
185
+
186
+ // ...and each carries its OWN attribution, which is the fact the
187
+ // aggregated view cannot express at all.
188
+ //
189
+ // Asserted on call counts, not on elapsed time. The comparison here used
190
+ // to be EXPECT_GT on cumulativeTime, and it was flaky: the ratio is
191
+ // normally about 9x but was measured collapsing to 1.79x, and it failed
192
+ // a macOS CI run outright. Wall-clock on a shared runner is not a thing
193
+ // to assert an ordering on when an exact quantity is available.
194
+ //
195
+ // The recursion folds onto a child node (see RecursionFoldsOntoASingleNode),
196
+ // and THAT is where the difference in work shows up: 2 recursive calls
197
+ // under light against 20 under heavy. Exact, identical on both engines.
198
+ const auto* foldedLight = childNamed(r, *workUnderLight, "work");
199
+ const auto* foldedHeavy = childNamed(r, *workUnderHeavy, "work");
200
+ ASSERT_NE(foldedLight, nullptr);
201
+ ASSERT_NE(foldedHeavy, nullptr);
202
+ EXPECT_EQ(foldedLight->callCount, 2);
203
+ EXPECT_EQ(foldedHeavy->callCount, 20);
204
+
205
+ // Times are still recorded per path -- just not compared against each
206
+ // other, since only their existence is deterministic.
207
+ EXPECT_GT(workUnderLight->cumulativeTime, 0.0);
208
+ EXPECT_GT(workUnderHeavy->cumulativeTime, 0.0);
188
209
 
189
210
  // The flat view still totals both, unchanged.
190
211
  double siteTotal = 0.0;
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "0.37.0"
58
+ version = "0.51.3"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },