openscad-cpp-evaluator 0.25.0__tar.gz → 0.26.1__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 (199) hide show
  1. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/evaluator.hpp +19 -1
  3. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/debug_profile.cpp +37 -3
  5. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/user_calls.cpp +8 -4
  6. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_bytecode_compiler.cpp +6 -6
  7. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_debug_hooks.cpp +78 -5
  8. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_tail_calls.cpp +6 -3
  9. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/.github/workflows/ci.yml +0 -0
  10. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/.github/workflows/wheels.yml +0 -0
  11. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/.gitignore +0 -0
  12. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/.gitmodules +0 -0
  13. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/CLAUDE.md +0 -0
  14. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/CMakeLists.txt +0 -0
  15. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/LICENSE +0 -0
  16. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/README.md +0 -0
  17. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/bindings/CMakeLists.txt +0 -0
  18. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/bindings/ast_to_py.cpp +0 -0
  19. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/bindings/module.cpp +0 -0
  20. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/cmake/embed_font.cmake +0 -0
  21. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/examples/CMakeLists.txt +0 -0
  22. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/examples/eval_perf_benchmark.cpp +0 -0
  23. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/examples/manifold_cache_reuse.cpp +0 -0
  24. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/examples/minimal_debugger.cpp +0 -0
  25. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  26. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/.gitignore +0 -0
  27. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  28. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  29. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/README.md +0 -0
  30. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  31. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  32. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  33. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  34. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  35. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  36. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  37. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  38. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  39. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  40. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  41. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  42. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  43. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  44. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  45. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  46. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
  47. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  48. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  49. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  50. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  51. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  52. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  53. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  54. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  55. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  56. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  57. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  58. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  59. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  60. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  61. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  62. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  63. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
  64. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  65. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  66. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  67. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  68. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  69. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  70. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  71. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  72. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  73. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  74. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  75. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  76. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  77. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  78. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  79. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  80. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  81. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  82. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  83. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  84. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  85. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  86. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  87. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  88. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  89. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  90. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  91. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  92. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  93. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  94. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  95. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  96. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  97. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  98. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  99. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  100. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  101. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  102. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  103. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  104. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  105. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  106. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
  107. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  108. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  109. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  110. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  111. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  112. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  113. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  114. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  115. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  116. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  117. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  118. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  119. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  120. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
  121. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  122. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
  123. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/resources/fonts/LICENSE +0 -0
  124. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  125. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/CMakeLists.txt +0 -0
  126. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/booleans.cpp +0 -0
  127. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/builtins.hpp +0 -0
  128. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/call_args.cpp +0 -0
  129. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/color.cpp +0 -0
  130. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/control.cpp +0 -0
  131. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/extrude.cpp +0 -0
  132. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/function_builtins.cpp +0 -0
  133. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/import.cpp +0 -0
  134. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/modifiers.cpp +0 -0
  135. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/primitives_2d.cpp +0 -0
  136. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/primitives_3d.cpp +0 -0
  137. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/registry.cpp +0 -0
  138. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/roof.cpp +0 -0
  139. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/segments.cpp +0 -0
  140. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/surface.cpp +0 -0
  141. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/text.cpp +0 -0
  142. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/topology.cpp +0 -0
  143. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/builtins/transforms.cpp +0 -0
  144. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/bytecode_compiler.cpp +0 -0
  145. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/bytecode_vm.cpp +0 -0
  146. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/colored_body.cpp +0 -0
  147. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/csg_generate.cpp +0 -0
  148. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/csg_resolve.cpp +0 -0
  149. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/css_colors.cpp +0 -0
  150. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/debug/debug_repl.cpp +0 -0
  151. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/eval_context.cpp +0 -0
  152. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/eval_error.cpp +0 -0
  153. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/eval_use.cpp +0 -0
  154. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/export.cpp +0 -0
  155. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/expr_eval.cpp +0 -0
  156. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/import/dxf_import.cpp +0 -0
  157. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/import/mesh_import.cpp +0 -0
  158. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/import/surface_load.cpp +0 -0
  159. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/import/svg_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/manifold_cache.cpp +0 -0
  161. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/mesh_check.cpp +0 -0
  162. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/stmt_eval.cpp +0 -0
  163. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/text/stb_font_provider.cpp +0 -0
  164. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/text/text_metrics.cpp +0 -0
  165. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/value.cpp +0 -0
  166. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/src/zip_stored.cpp +0 -0
  167. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/CMakeLists.txt +0 -0
  168. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_booleans.cpp +0 -0
  169. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_cli.cpp +0 -0
  170. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_control_flow.cpp +0 -0
  171. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_csg_tree.cpp +0 -0
  172. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_dxf_svg_import.cpp +0 -0
  173. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_eval_context.cpp +0 -0
  174. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_export.cpp +0 -0
  175. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_expr_eval.cpp +0 -0
  176. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_extrude_roof.cpp +0 -0
  177. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_function_builtins.cpp +0 -0
  178. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_helpers.hpp +0 -0
  179. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_import_export.cpp +0 -0
  180. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_manifold_cache.cpp +0 -0
  181. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_mesh_check.cpp +0 -0
  182. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_multi_color_merge.cpp +0 -0
  183. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_primitives.cpp +0 -0
  184. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_profiling.cpp +0 -0
  185. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_python_bindings.py +0 -0
  186. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_scope_trail.cpp +0 -0
  187. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_scoping.cpp +0 -0
  188. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_surface.cpp +0 -0
  189. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_text.cpp +0 -0
  190. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_transforms.cpp +0 -0
  191. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_use.cpp +0 -0
  192. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_value.cpp +0 -0
  193. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_viewport_params.cpp +0 -0
  194. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tests/test_zip_stored.cpp +0 -0
  195. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tools/cli/CMakeLists.txt +0 -0
  196. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tools/cli/cli_lib.cpp +0 -0
  197. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tools/cli/cli_lib.hpp +0 -0
  198. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/tools/cli/main.cpp +0 -0
  199. {openscad_cpp_evaluator-0.25.0 → openscad_cpp_evaluator-0.26.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.25.0
3
+ Version: 0.26.1
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -425,6 +425,13 @@ public:
425
425
  fastContinueHookSkippable_ = hookSkippable;
426
426
  }
427
427
 
428
+ // The statement checkpoint currently in progress at each call depth,
429
+ // for the call-site collapse described on checkDebug. Per depth, not a
430
+ // single slot: `a = [f(1), f(2), f(3)];` runs each callee's own body
431
+ // checkpoints in between, and those must not be mistaken for the
432
+ // caller having moved on to a new statement.
433
+ std::vector<std::pair<int, std::string>> lastStmtByDepth_;
434
+
428
435
  // The other half of hook-skippable mode's safety net. checkDebug()'s
429
436
  // whole premise (see its own doc comment, debug_profile.cpp) is that it
430
437
  // can skip calling into Python for a line with no breakpoint -- but the
@@ -475,7 +482,18 @@ public:
475
482
  // (function body entry), and builtins/control.cpp's
476
483
  // resolveBreakpoint/resolveIntersectionFor (free functions). Mirrors
477
484
  // Evaluator._check_debug, including its parameter defaults.
478
- void checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced = false, bool exprLevel = false);
485
+ // `callSite` marks the stop fired just before descending into a user
486
+ // function or function literal. It is a real, steppable stop -- a
487
+ // debugger's Step Over should pause on the call line -- but it is NOT
488
+ // a second execution of the line it sits on. When it lands on the same
489
+ // line and depth as the statement checkpoint immediately before it (as
490
+ // in `x = f(y);`, where the assignment and the call are one line), it
491
+ // is dropped, so a breakpoint there fires once per execution instead
492
+ // of twice. A call on its own line, or on a different line from the
493
+ // enclosing statement, still stops -- which is what keeps stepping
494
+ // through a list comprehension alternating for-line/call-line.
495
+ void checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced = false, bool exprLevel = false,
496
+ bool callSite = false);
479
497
 
480
498
  // (origin, line) for each top-level, non-declaration child of the
481
499
  // node checkDebug() was just called with -- i.e., if that node is a
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.25.0"
7
+ version = "0.26.1"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -9,9 +9,31 @@ namespace {
9
9
  // call), if any. See Evaluator::lastChildrenPositions()'s own doc comment
10
10
  // for what this feeds. Mirrors the reference's own
11
11
  // Evaluator._child_statement_positions.
12
- std::optional<std::vector<std::pair<std::string, int>>> childStatementPositions(const oscad::ASTNode& node) {
12
+ // Where a debugger's "step into child" should aim from `node`.
13
+ //
14
+ // Normally the node's own children -- `framed(20) leaf(3);` aims at
15
+ // `leaf(3)`. But a `children()` call has no children of its own: it
16
+ // forwards the enclosing invocation's, which live on the context. Without
17
+ // that case, stepping to a child from the one statement whose entire job
18
+ // is to run the children found nothing to aim at, and the step silently
19
+ // degraded into a continue -- running to the next breakpoint or off the
20
+ // end of the script.
21
+ std::optional<std::vector<std::pair<std::string, int>>> childStatementPositions(const oscad::ASTNode& node,
22
+ const EvalContext& ctx) {
13
23
  if (node.kind() != oscad::NodeKind::ModularCall) return std::nullopt;
14
24
  const auto& call = static_cast<const oscad::ModularCall&>(node);
25
+ if (call.name && call.name->name == "children") {
26
+ if (!ctx.childrenNodes || ctx.childrenNodes->empty()) return std::nullopt;
27
+ std::vector<std::pair<std::string, int>> forwarded;
28
+ for (const oscad::ASTNode* c : *ctx.childrenNodes) {
29
+ if (c) forwarded.emplace_back(c->position().origin, c->position().line);
30
+ }
31
+ if (forwarded.empty()) return std::nullopt;
32
+ // Every forwarded child, even when an index argument (children(0),
33
+ // children([1:2])) will run only some: a position that is never
34
+ // reached can never be stopped at, so the extra targets are inert.
35
+ return forwarded;
36
+ }
15
37
  std::vector<std::pair<std::string, int>> positions;
16
38
  for (const auto& c : call.children) {
17
39
  if (c->kind() == oscad::NodeKind::Assignment || c->kind() == oscad::NodeKind::ModuleDeclaration ||
@@ -74,9 +96,21 @@ std::vector<DebugFrame> Evaluator::buildDebugFrames(const EvalContext* ctx) cons
74
96
  return frames;
75
97
  }
76
98
 
77
- void Evaluator::checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced, bool exprLevel) {
99
+ void Evaluator::checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced, bool exprLevel,
100
+ bool callSite) {
78
101
  if (!debugHooks_.debugHook) return;
79
102
  const oscad::Position& pos = node.position();
103
+ const size_t stmtDepth = callStack_.size();
104
+ if (lastStmtByDepth_.size() <= stmtDepth) lastStmtByDepth_.resize(stmtDepth + 1, {-1, std::string{}});
105
+ if (callSite && !forced && lastStmtByDepth_[stmtDepth] == std::make_pair(pos.line, pos.origin)) {
106
+ // The statement this call belongs to already stopped on this exact
107
+ // line, at this depth. See checkDebug's declaration for why this
108
+ // one is dropped.
109
+ return;
110
+ }
111
+ if (!exprLevel && !callSite) {
112
+ lastStmtByDepth_[stmtDepth] = {pos.line, pos.origin};
113
+ }
80
114
  // Fast-continue's hook-skippable mode (setFastContinueBreakpoints' own
81
115
  // doc comment): a plain "Continue" with no step pending needs the debug
82
116
  // hook called ONLY for a line that actually has a breakpoint -- every
@@ -109,7 +143,7 @@ void Evaluator::checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool fo
109
143
  }
110
144
  const int depth = static_cast<int>(callStack_.size());
111
145
  const DebugFramesFn getFrame = [this, &ctx]() { return buildDebugFrames(&ctx); };
112
- lastChildrenPositions_ = childStatementPositions(node);
146
+ lastChildrenPositions_ = childStatementPositions(node, ctx);
113
147
 
114
148
  DebugAction action = debugHooks_.debugHook(pos.line, depth, forced, exprLevel, pos.origin, callStack_, getFrame);
115
149
  for (auto& [k, v] : action.mods) ctx.let_->set(k, v);
@@ -547,7 +547,11 @@ std::variant<Value, Evaluator::TailStep, Evaluator::NotTailStep> Evaluator::simp
547
547
  const oscad::ASTNode* declNode = ctx.scope->lookupFunction(leftId->name);
548
548
  if (declNode && declNode->kind() == oscad::NodeKind::FunctionDeclaration) {
549
549
  const auto& decl = static_cast<const oscad::FunctionDeclaration&>(*declNode);
550
- checkDebug(n, ctx); // call-site stop, per hop (see evalFunctionCall's)
550
+ // Expression-level: a call sitting inside a larger
551
+ // statement is not its own statement, and pausing here
552
+ // as well as at the statement makes a breakpoint on
553
+ // that line fire twice per execution.
554
+ checkDebug(n, ctx, /*forced=*/false, /*exprLevel=*/false, /*callSite=*/true); // call-site stop, per hop
551
555
  const bool hasChunk = useBytecodeVm() && lookupOrCompileChunk(decl) != nullptr;
552
556
  std::optional<TailStep> step = tryTailStepFor(leftId->name, decl, decl.parameters, *decl.expr,
553
557
  hasChunk, n.arguments, ctx, n.position());
@@ -570,7 +574,7 @@ std::variant<Value, Evaluator::TailStep, Evaluator::NotTailStep> Evaluator::simp
570
574
  if (const auto* closurePtr = std::get_if<ClosurePtr>(&funcVal); closurePtr && *closurePtr) {
571
575
  const Closure& closure = **closurePtr;
572
576
  const oscad::FunctionLiteral& funcNode = *closure.node;
573
- checkDebug(n, ctx); // call-site stop, function-literal callee
577
+ checkDebug(n, ctx, /*forced=*/false, /*exprLevel=*/false, /*callSite=*/true); // call-site stop, function-literal callee
574
578
  const bool hasChunk = useBytecodeVm() && lookupCompiledLiteralChunk(funcNode) != nullptr;
575
579
  std::optional<TailStep> step =
576
580
  tryTailStepFor("<function literal>", funcNode, funcNode.parameters, *funcNode.body, hasChunk,
@@ -886,7 +890,7 @@ Value Evaluator::evalFunctionCall(const oscad::PrimaryCall& node, EvalContext& c
886
890
  // evalUserFunctionCore). Builtins deliberately get none --
887
891
  // mirrors _eval_function_call, where only the user-function and
888
892
  // function-literal branches call _check_debug.
889
- checkDebug(node, ctx);
893
+ checkDebug(node, ctx, /*forced=*/false, /*exprLevel=*/false, /*callSite=*/true);
890
894
  return evalUserFunction(leftId->name, static_cast<const oscad::FunctionDeclaration&>(*decl), node.arguments,
891
895
  ctx, &node);
892
896
  }
@@ -899,7 +903,7 @@ Value Evaluator::evalFunctionCall(const oscad::PrimaryCall& node, EvalContext& c
899
903
  // unknown callee gets exactly one warning below, not two.
900
904
  Value funcVal = leftId ? evalIdentifier(leftId->name, &leftId->position(), ctx, false) : evalExpr(*node.left, ctx);
901
905
  if (const auto* closurePtr = std::get_if<ClosurePtr>(&funcVal); closurePtr && *closurePtr) {
902
- checkDebug(node, ctx); // same call-site stop, function-literal callee
906
+ checkDebug(node, ctx, /*forced=*/false, /*exprLevel=*/false, /*callSite=*/true); // same call-site stop, function-literal callee
903
907
  return evalFunctionLiteral(**closurePtr, node.arguments, ctx, &node);
904
908
  }
905
909
 
@@ -197,13 +197,13 @@ TEST(BytecodeCompiler, DollarPrefixedParameterCompiles) {
197
197
  EXPECT_EQ(runCapturingEcho("function withFn($fn) = $fn;\necho(let($fn = 99) withFn());"), "ECHO: undef");
198
198
  // The proof this is actually running compiled, not silently falling
199
199
  // back: same ternary-bodied shape and technique as
200
- // FastContinueWithNoBreakpointInFunctionUsesVm above (3 stops =
201
- // compiled, 5 = interpreted), with $fn substituted in for the plain
200
+ // FastContinueWithNoBreakpointInFunctionUsesVm above (2 stops =
201
+ // compiled, 4 = interpreted), with $fn substituted in for the plain
202
202
  // parameter both as the declared name and every body reference.
203
203
  const int stops = countDebugHookStops("function f($fn) = $fn > 0 ? $fn + 1 : $fn - 1;\n"
204
204
  "echo(f(5));",
205
205
  std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
206
- EXPECT_EQ(stops, 3);
206
+ EXPECT_EQ(stops, 2);
207
207
  }
208
208
 
209
209
  TEST(BytecodeCompiler, UndeclaredDollarNamedArgumentReachesDynInsideCompiledFunction) {
@@ -828,7 +828,7 @@ TEST(BytecodeCompiler, ClosureWithDollarParameterNowCompilesToo) {
828
828
  // silent even for a closure capturing an enclosing binding with its
829
829
  // own $-parameter.
830
830
  const int stops = countDebugHookStops(script, std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
831
- EXPECT_EQ(stops, 3);
831
+ EXPECT_EQ(stops, 2);
832
832
  }
833
833
 
834
834
  // -- Tail-call optimization, VM path (Phase B) -----------------------------
@@ -1156,7 +1156,7 @@ TEST(BytecodeCompiler, DebugAttachedWithoutFastContinueAlwaysInterprets) {
1156
1156
  const int stops = countDebugHookStops("function f(x) = x > 0 ? x + 1 : x - 1;\n"
1157
1157
  "echo(f(5));",
1158
1158
  std::nullopt);
1159
- EXPECT_EQ(stops, 5);
1159
+ EXPECT_EQ(stops, 4);
1160
1160
  }
1161
1161
 
1162
1162
  TEST(BytecodeCompiler, FastContinueWithNoBreakpointInFunctionUsesVm) {
@@ -1167,7 +1167,7 @@ TEST(BytecodeCompiler, FastContinueWithNoBreakpointInFunctionUsesVm) {
1167
1167
  const int stops = countDebugHookStops("function f(x) = x > 0 ? x + 1 : x - 1;\n"
1168
1168
  "echo(f(5));",
1169
1169
  std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
1170
- EXPECT_EQ(stops, 3);
1170
+ EXPECT_EQ(stops, 2);
1171
1171
  }
1172
1172
 
1173
1173
  TEST(BytecodeCompiler, FastContinueWithBreakpointInsideFunctionStillInterprets) {
@@ -595,17 +595,90 @@ TEST(DebugHooksParity, ListCompCForFiresEveryInitAndIncrSeparately) {
595
595
  EXPECT_EQ(stops.size(), 14u); // + 3 condition checks + 2 body expressions
596
596
  }
597
597
 
598
- // (13) User function / function-literal call sites: a statement-level stop
599
- // in the CALLER's context, before the callee's own body-entry stop.
598
+ // (13) User function / function-literal call sites: a stop in the CALLER's
599
+ // context, before the callee's own body-entry stop.
600
+ //
601
+ // DELIBERATE DIVERGENCE from the reference, and only for the case that
602
+ // duplicates: when the call sits on the same line and depth as the
603
+ // statement that just stopped, its call-site stop is dropped. Otherwise
604
+ // every consumer treating a statement-level stop as a breakpoint hit
605
+ // pauses twice for one execution of `a = double(5);` -- BelfrySCAD's
606
+ // debugger stuttered on Continue and reported the same loop iteration
607
+ // twice. Consumers cannot collapse it themselves: fast-continue skips the
608
+ // checkpoints in between, so a duplicate is indistinguishable from a
609
+ // genuine second visit to the line.
610
+ //
611
+ // A call on a different line from its enclosing statement still stops --
612
+ // see CallSiteOnItsOwnLineStillStops, which is what keeps Step Over
613
+ // through a list comprehension alternating for-line/call-line.
600
614
  TEST(DebugHooksParity, UserFunctionCallSiteStopsBeforeDescendingIntoTheCallee) {
601
- // line 2 twice (assignment, then call site), then line 1 (body entry).
615
+ // line 2 (assignment; the call site on the same line is collapsed),
616
+ // then line 1 (body entry).
602
617
  EXPECT_EQ(recordStops("function double(x) = x * 2;\na = double(5);\n"),
603
- (std::vector<Stop>{{2, false, false}, {2, false, false}, {1, false, false}}));
618
+ (std::vector<Stop>{{2, false, false}, {1, false, false}}));
604
619
  }
605
620
 
606
621
  TEST(DebugHooksParity, FunctionLiteralCallSiteAlsoStops) {
607
622
  EXPECT_EQ(recordStops("f = function(x) x * 2;\na = f(5);\n"),
608
- (std::vector<Stop>{{1, false, false}, {2, false, false}, {2, false, false}, {1, false, false}}));
623
+ (std::vector<Stop>{{1, false, false}, {2, false, false}, {1, false, false}}));
624
+ }
625
+
626
+ // The other half of the rule above: a call that is NOT on its enclosing
627
+ // statement's line keeps its own stop, so stepping still pauses on the
628
+ // call before descending. This is the list-comprehension shape.
629
+ TEST(DebugHooksParity, CallSiteOnItsOwnLineStillStops) {
630
+ const std::vector<Stop> stops = recordStops("function fx(x) = x*2;\n"
631
+ "a = [\n"
632
+ " for (i = [0:1])\n"
633
+ " fx(i)\n"
634
+ "];\n");
635
+ // Line 4 holds only the call. Two iterations, so two call-site stops,
636
+ // both statement-level and none collapsed -- the statement that
637
+ // stopped before each was the `for` on line 3.
638
+ int line4 = 0;
639
+ for (const Stop& s : stmtStops(stops))
640
+ if (s.line == 4) ++line4;
641
+ EXPECT_EQ(line4, 2);
642
+ }
643
+
644
+ // Step-into-child aims at the children a `children()` call forwards, not
645
+ // at its own (it has none). Without this, the one statement whose entire
646
+ // job is to run the children offered a debugger nothing to aim at, and the
647
+ // step degraded into a continue -- running off the end of the script.
648
+ TEST(DebugHooksChildren, ChildrenCallForwardsItsCallersChildrenAsStepTargets) {
649
+ // Line 5 is the child; line 2 is the `children();` that forwards it.
650
+ const char* src = "module framed(gap) {\n"
651
+ " children();\n"
652
+ "}\n"
653
+ "framed(20)\n"
654
+ " leaf();\n"
655
+ "module leaf() { cube(1); }\n";
656
+ std::vector<std::pair<int, std::vector<std::pair<std::string, int>>>> seen;
657
+ DebugHooks hooks;
658
+ Evaluator* evp = nullptr;
659
+ hooks.debugHook = [&](int line, int, bool, bool exprLevel, const std::string&,
660
+ const std::vector<CallStackFrame>&, const DebugFramesFn&) {
661
+ if (!exprLevel && evp && evp->lastChildrenPositions())
662
+ seen.emplace_back(line, *evp->lastChildrenPositions());
663
+ return DebugAction{};
664
+ };
665
+ Evaluator ev(EchoFn{}, nullptr, nullptr, hooks);
666
+ evp = &ev;
667
+ auto ast = parseSrc(src);
668
+ auto scope = oscad::buildScopes(ast);
669
+ EvalContext ctx = EvalContext::makeRoot(scope.get());
670
+ ev.evaluate(ast, ctx);
671
+
672
+ // At `children();` on line 2, the target is the forwarded child on
673
+ // line 5 -- not nothing, and not the children() call's own position.
674
+ bool found = false;
675
+ for (const auto& [line, positions] : seen) {
676
+ if (line != 2) continue;
677
+ found = true;
678
+ ASSERT_EQ(positions.size(), 1u);
679
+ EXPECT_EQ(positions.front().second, 5);
680
+ }
681
+ EXPECT_TRUE(found) << "no statement-level stop at the children() call";
609
682
  }
610
683
 
611
684
  TEST(DebugHooksParity, BuiltinFunctionCallGetsNoCallSiteStop) {
@@ -283,9 +283,12 @@ TEST(TailCalls, DebugHookFiresPerHopInsideATailChain) {
283
283
  // abandoning TCO whenever a debugger is attached.
284
284
  EXPECT_GT(calls, 1000);
285
285
  // All statement-level stops on line 1 (the whole function declaration):
286
- // 501 ternary stops (n = 500..0), 500 recursive call-site stops, and the
287
- // single body-entry stop.
288
- EXPECT_EQ(bodyEntry, 501 + 500 + 1);
286
+ // 501 ternary stops (n = 500..0) and the single body-entry stop. The
287
+ // 500 recursive call-site stops still fire -- they are counted in
288
+ // `calls` above -- but as expression-level, since a call inside a
289
+ // larger expression is not a statement of its own (see
290
+ // DebugHooksParity.UserFunctionCallSiteStopsBeforeDescendingIntoTheCallee).
291
+ EXPECT_EQ(bodyEntry, 501 + 1);
289
292
  }
290
293
 
291
294
  TEST(TailCalls, DeepNonTailRecursionHitsAControlledErrorInsteadOfCrashingInterpreted) {