openscad-cpp-evaluator 0.3.0__tar.gz → 0.4.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 (195) hide show
  1. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/CLAUDE.md +26 -9
  2. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/CMakeLists.txt +15 -14
  3. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/PKG-INFO +1 -1
  4. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/bindings/module.cpp +32 -8
  5. openscad_cpp_evaluator-0.4.1/cmake/embed_font.cmake +28 -0
  6. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/examples/minimal_debugger.cpp +3 -3
  7. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +22 -16
  8. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/debug_repl.hpp +1 -1
  9. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/evaluator.hpp +21 -7
  10. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/pyproject.toml +1 -1
  11. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/CMakeLists.txt +2 -0
  12. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/control.cpp +5 -0
  13. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/csg_resolve.cpp +10 -0
  14. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/debug/debug_repl.cpp +13 -7
  15. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/debug_profile.cpp +2 -2
  16. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/expr_eval.cpp +54 -6
  17. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/stmt_eval.cpp +40 -15
  18. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/text/stb_font_provider.cpp +7 -10
  19. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/user_calls.cpp +24 -6
  20. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_cli.cpp +1 -1
  21. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_control_flow.cpp +2 -2
  22. openscad_cpp_evaluator-0.4.1/tests/test_debug_hooks.cpp +422 -0
  23. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_python_bindings.py +27 -3
  24. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_tail_calls.cpp +22 -10
  25. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tools/cli/cli_lib.cpp +2 -2
  26. openscad_cpp_evaluator-0.3.0/include/openscad_cpp_evaluator/bundled_font_path.hpp.in +0 -6
  27. openscad_cpp_evaluator-0.3.0/tests/test_debug_hooks.cpp +0 -170
  28. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/.github/workflows/ci.yml +0 -0
  29. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/.github/workflows/wheels.yml +0 -0
  30. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/.gitignore +0 -0
  31. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/.gitmodules +0 -0
  32. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/LICENSE +0 -0
  33. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/README.md +0 -0
  34. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/bindings/CMakeLists.txt +0 -0
  35. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/examples/CMakeLists.txt +0 -0
  36. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/examples/eval_perf_benchmark.cpp +0 -0
  37. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/examples/manifold_cache_reuse.cpp +0 -0
  38. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  39. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/.gitignore +0 -0
  40. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  41. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  42. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/README.md +0 -0
  43. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  44. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  45. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  46. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  47. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  48. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  49. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  50. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  51. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  52. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  53. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  54. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  55. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  56. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  57. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  58. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  59. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
  60. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  61. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  62. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  63. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  64. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  65. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  66. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  67. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  68. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  69. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  70. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  71. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  72. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  73. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  74. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  75. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  76. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
  77. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  78. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  79. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  80. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  81. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  82. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  83. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  84. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  85. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  86. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  87. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  88. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  89. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  90. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  91. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  92. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  93. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  94. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  95. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  96. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  97. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  98. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  99. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  100. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  101. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  102. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  103. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  104. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  105. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  106. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  107. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  108. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  109. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  110. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  111. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  112. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  113. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  114. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  115. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  116. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  117. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
  118. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  119. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  120. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  121. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  122. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  123. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  124. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  125. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  126. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  127. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  128. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  129. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  130. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
  131. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  132. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
  133. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/resources/fonts/LICENSE +0 -0
  134. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  135. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/booleans.cpp +0 -0
  136. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/builtins.hpp +0 -0
  137. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/call_args.cpp +0 -0
  138. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/color.cpp +0 -0
  139. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/extrude.cpp +0 -0
  140. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/function_builtins.cpp +0 -0
  141. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/import.cpp +0 -0
  142. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/modifiers.cpp +0 -0
  143. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/primitives_2d.cpp +0 -0
  144. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/primitives_3d.cpp +0 -0
  145. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/registry.cpp +0 -0
  146. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/roof.cpp +0 -0
  147. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/segments.cpp +0 -0
  148. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/surface.cpp +0 -0
  149. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/text.cpp +0 -0
  150. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/topology.cpp +0 -0
  151. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/builtins/transforms.cpp +0 -0
  152. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/bytecode_compiler.cpp +0 -0
  153. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/bytecode_vm.cpp +0 -0
  154. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/colored_body.cpp +0 -0
  155. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/csg_generate.cpp +0 -0
  156. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/css_colors.cpp +0 -0
  157. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/eval_context.cpp +0 -0
  158. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/eval_error.cpp +0 -0
  159. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/eval_use.cpp +0 -0
  160. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/export.cpp +0 -0
  161. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/import/dxf_import.cpp +0 -0
  162. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/import/mesh_import.cpp +0 -0
  163. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/import/surface_load.cpp +0 -0
  164. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/import/svg_import.cpp +0 -0
  165. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/manifold_cache.cpp +0 -0
  166. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/text/text_metrics.cpp +0 -0
  167. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/value.cpp +0 -0
  168. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/src/zip_stored.cpp +0 -0
  169. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/CMakeLists.txt +0 -0
  170. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_booleans.cpp +0 -0
  171. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_bytecode_compiler.cpp +0 -0
  172. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_csg_tree.cpp +0 -0
  173. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_dxf_svg_import.cpp +0 -0
  174. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_eval_context.cpp +0 -0
  175. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_export.cpp +0 -0
  176. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_expr_eval.cpp +0 -0
  177. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_extrude_roof.cpp +0 -0
  178. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_function_builtins.cpp +0 -0
  179. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_helpers.hpp +0 -0
  180. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_import_export.cpp +0 -0
  181. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_manifold_cache.cpp +0 -0
  182. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_multi_color_merge.cpp +0 -0
  183. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_primitives.cpp +0 -0
  184. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_profiling.cpp +0 -0
  185. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_scoping.cpp +0 -0
  186. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_surface.cpp +0 -0
  187. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_text.cpp +0 -0
  188. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_transforms.cpp +0 -0
  189. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_use.cpp +0 -0
  190. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_value.cpp +0 -0
  191. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_viewport_params.cpp +0 -0
  192. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tests/test_zip_stored.cpp +0 -0
  193. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tools/cli/CMakeLists.txt +0 -0
  194. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tools/cli/cli_lib.hpp +0 -0
  195. {openscad_cpp_evaluator-0.3.0 → openscad_cpp_evaluator-0.4.1}/tools/cli/main.cpp +0 -0
@@ -241,15 +241,32 @@ Roman:style=Bold"`) silently gets Liberation Sans instead under this default pro
241
241
  path needed in this repo: the fix is a Qt host application supplying its own `FontProvider`, which
242
242
  is exactly what the injection seam exists for.
243
243
 
244
- **Debugger/profiler, Phase 9 (the last phase)**: `checkDebug()` fires from exactly one call site
245
- (`evalChildren`'s `runAll`, plus one explicit call each at user-function/function-literal body entry
246
- and inside `breakpoint()`'s resolve) rather than the reference's several dozen `_check_debug` call
247
- sites scattered across nearly every statement/expression-eval function — a deliberate scope
248
- reduction (see `debug_hooks.hpp`'s `DebugHookFn` doc comment for the full reasoning): the reference's
249
- extra sites exist for `expr_level` sub-statement stepping (individual list-comprehension clauses,
250
- ternary branches) that neither this port's own `debug_repl.cpp` REPL nor the reference's own
251
- `_debug_repl.py` REPL ever actually uses (both only ever pause/step at statement granularity in
252
- practice). Verified this is a faithful, not just simplified, port by running an *identical*
244
+ **Debugger/profiler, Phase 9 (the last phase)**: `checkDebug()` fires at **full parity with the
245
+ reference's `_check_debug` call sites** — the statement checkpoint in `evalChildren`'s `runAll`,
246
+ user-function/function-literal body entry, `breakpoint()`'s resolve, *and* the reference's
247
+ sub-statement sites: if/else branch entry (`evalStatement`), for-loop per-variable bindings and
248
+ per-iteration body entry (`evalFor`), `intersection_for` body entry, statement- and
249
+ expression-form `let()` assignments, expression-form `echo()`/`assert()`, ternary
250
+ condition/chosen-branch, every list-comprehension clause including the C-style `for`
251
+ (`evalListElement`), modifier-wrapped children (`evalModifier`), and user function/function-literal
252
+ call sites (`evalFunctionCall`, plus their duplicates in `simplifyTailStep` so a tail chain isn't
253
+ debug-invisible). Each carries the reference's `expr_level` flag, threaded all the way through
254
+ `DebugHookFn` and the nanobind trampoline. `expr_depth` is *not* tracked (hardcoded 0 through the
255
+ Python surface) — no consumer reads it.
256
+
257
+ An earlier revision of this file argued the sub-statement sites were unnecessary because no REPL
258
+ steps at that granularity. That was wrong: BelfrySCAD's debugger UI consumes them, and one case was
259
+ an outright bug — a C-style `for` *inside a list comprehension* got zero debug checks anywhere,
260
+ so a breakpoint set on it never fired. Two consequences of full parity worth knowing:
261
+ `useBytecodeVm()` returns false whenever a debug hook is installed (compiled chunks flatten away the
262
+ AST nodes these checkpoints hang off, so debugging always takes the interpreter path), and
263
+ `evalChildren` deliberately skips its own check for `ModularLet` — the reference's
264
+ `_eval_statement_impl` skips it too, pausing on the let's assignments instead. Parity is verified
265
+ test-by-test in `tests/test_debug_hooks.cpp`'s `DebugHooksParity` suite, whose expected
266
+ `(line, expr_level, forced)` sequences were captured by running the Python reference over the same
267
+ sources, not written down from this port's own output.
268
+
269
+ The port was also verified end to end by running an *identical*
253
270
  `--debug` session (same script, same `break`/`run`/`print`/`continue`/`step`/`backtrace`/`set`
254
271
  sequence) against both this port's CLI and the real Python reference's own `openscad-evaluator
255
272
  --debug` side by side: transcripts matched exactly, **including** an unexpected extra pause neither
@@ -147,21 +147,22 @@ FetchContent_MakeAvailable(stb)
147
147
  FetchContent_Declare(cpp_linenoise GIT_REPOSITORY https://github.com/yhirose/cpp-linenoise.git GIT_TAG master GIT_SHALLOW TRUE)
148
148
  FetchContent_MakeAvailable(cpp_linenoise)
149
149
 
150
- # Bakes an absolute path to the bundled default font (resources/fonts/) into
151
- # a generated header, so StbFontProvider's default constructor doesn't need
152
- # a runtime-configurable resource search path.
153
- #
154
- # ponytail: fine for this repo's own tests/CLI (always built from this
155
- # source tree) but not how a packaged/installed build should ship a font --
156
- # a real install needs either the resource file copied alongside the
157
- # installed binary or the font bytes embedded directly into the binary
158
- # (e.g. via a generated byte-array header). Upgrade when packaging is
159
- # actually needed; not worth building now for a resource this project only
160
- # consumes from its own build tree so far.
161
- configure_file(
162
- ${CMAKE_CURRENT_SOURCE_DIR}/include/openscad_cpp_evaluator/bundled_font_path.hpp.in
163
- ${CMAKE_CURRENT_BINARY_DIR}/generated/openscad_cpp_evaluator/bundled_font_path.hpp
150
+ # Embeds the bundled default font's (resources/fonts/) raw bytes into a
151
+ # generated header (kBundledFontData/kBundledFontDataSize), so
152
+ # StbFontProvider's default constructor needs no runtime file path at all --
153
+ # a real installed/packaged build (e.g. the PyPI wheel) has no guarantee the
154
+ # source tree's resources/fonts/ is present or at any predictable path
155
+ # alongside the installed shared library.
156
+ set(BUNDLED_FONT_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts/LiberationSans-Regular.ttf)
157
+ set(BUNDLED_FONT_HEADER ${CMAKE_CURRENT_BINARY_DIR}/generated/openscad_cpp_evaluator/bundled_font_data.hpp)
158
+ add_custom_command(
159
+ OUTPUT ${BUNDLED_FONT_HEADER}
160
+ COMMAND ${CMAKE_COMMAND} -DIN_FILE=${BUNDLED_FONT_SRC} -DOUT_FILE=${BUNDLED_FONT_HEADER}
161
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_font.cmake
162
+ DEPENDS ${BUNDLED_FONT_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_font.cmake
163
+ COMMENT "Embedding bundled font bytes into bundled_font_data.hpp"
164
164
  )
165
+ add_custom_target(generate_bundled_font DEPENDS ${BUNDLED_FONT_HEADER})
165
166
 
166
167
  option(BUILD_TESTING "Build tests" ON)
167
168
  option(BUILD_EXAMPLES "Build the examples/ programs (e.g. minimal_debugger)" ON)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -313,13 +313,21 @@ nb::object generatePartialTrampoline(const GeneratePartialFn& generatePartial) {
313
313
  }
314
314
 
315
315
  // Trampoline: C++ debug hook -> the Python DebugSession hook. Runs with the
316
- // GIL held (reacquired by the caller). `getFrame`/`callStack`/`generatePartial`
317
- // are valid only for this synchronous call, so the get_frames/generate_partial
316
+ // (origin, line) targets children()/children(N) might forward control to
317
+ // from the just-checked debug-hook node, for the "step to child" command.
318
+ // See Evaluator::lastChildrenPositions()'s own doc comment: valid only
319
+ // synchronously within the current hook call, same as get_frames.
320
+ using GetChildrenPositionsFn = std::function<const std::optional<std::vector<std::pair<std::string, int>>>&()>;
321
+
322
+ // GIL held (reacquired by the caller). `getFrame`/`callStack`/`generatePartial`/
323
+ // `getChildrenPositions` are valid only for this synchronous call, so their
318
324
  // closures are only ever invoked from within the Python hook, before it returns.
319
- oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, bool forced, const std::string& origin,
325
+ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, bool forced, bool exprLevel,
326
+ const std::string& origin,
320
327
  const std::vector<oscadeval::CallStackFrame>& callStack,
321
328
  const oscadeval::DebugFramesFn& getFrame,
322
- const GeneratePartialFn& generatePartial) {
329
+ const GeneratePartialFn& generatePartial,
330
+ const GetChildrenPositionsFn& getChildrenPositions) {
323
331
  auto getFramesPy = nb::cpp_function([&callStack, &getFrame]() -> nb::object {
324
332
  std::vector<oscadeval::DebugFrame> frames = getFrame();
325
333
  nb::list allFrameLocals;
@@ -335,10 +343,21 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
335
343
  return nb::make_tuple(nb::make_tuple(lastLocals, allFrameLocals), callStackToPy(callStack));
336
344
  });
337
345
  nb::object generatePartialPy = generatePartialTrampoline(generatePartial);
346
+ auto getChildrenPositionsPy = nb::cpp_function([&getChildrenPositions]() -> nb::object {
347
+ const std::optional<std::vector<std::pair<std::string, int>>>& positions = getChildrenPositions();
348
+ if (!positions) return nb::none();
349
+ nb::list out;
350
+ for (const auto& [origin, line] : *positions) out.append(nb::make_tuple(origin, line));
351
+ return out;
352
+ });
338
353
 
339
- nb::object ret = hook(line, depth, nb::arg("forced") = forced, nb::arg("expr_level") = false,
354
+ // expr_depth stays hardcoded at 0 -- this port deliberately doesn't
355
+ // track the reference's _expr_depth counter (no consumer reads it);
356
+ // expr_level, which consumers do read, is the real threaded value.
357
+ nb::object ret = hook(line, depth, nb::arg("forced") = forced, nb::arg("expr_level") = exprLevel,
340
358
  nb::arg("expr_depth") = 0, nb::arg("origin") = origin, nb::arg("get_frames") = getFramesPy,
341
- nb::arg("generate_partial") = generatePartialPy);
359
+ nb::arg("generate_partial") = generatePartialPy,
360
+ nb::arg("get_children_positions") = getChildrenPositionsPy);
342
361
  nb::tuple t = nb::cast<nb::tuple>(ret);
343
362
  oscadeval::DebugAction action;
344
363
  action.stop = (nb::cast<std::string>(t[0]) == "stop");
@@ -399,12 +418,17 @@ nb::object debugEvaluate(const std::string& path, nb::dict viewportParams, nb::c
399
418
  GeneratePartialFn generatePartial = [&evPtr]() -> std::vector<oscadeval::ColoredBody> {
400
419
  return evPtr->generatePartialTree();
401
420
  };
421
+ GetChildrenPositionsFn getChildrenPositions =
422
+ [&evPtr]() -> const std::optional<std::vector<std::pair<std::string, int>>>& {
423
+ return evPtr->lastChildrenPositions();
424
+ };
402
425
  oscadeval::DebugHooks hooks;
403
- hooks.debugHook = [&](int line, int depth, bool forced, const std::string& origin,
426
+ hooks.debugHook = [&](int line, int depth, bool forced, bool exprLevel, const std::string& origin,
404
427
  const std::vector<oscadeval::CallStackFrame>& cs,
405
428
  const oscadeval::DebugFramesFn& gf) -> oscadeval::DebugAction {
406
429
  nb::gil_scoped_acquire g;
407
- return callPyDebugHook(debugHook, line, depth, forced, origin, cs, gf, generatePartial);
430
+ return callPyDebugHook(debugHook, line, depth, forced, exprLevel, origin, cs, gf, generatePartial,
431
+ getChildrenPositions);
408
432
  };
409
433
  hooks.errorBreak = [&](int line, const std::string& header, const std::string& origin,
410
434
  const std::vector<oscadeval::CallStackFrame>& cs, const oscadeval::DebugFramesFn& gf) {
@@ -0,0 +1,28 @@
1
+ # Reads IN_FILE (binary) and writes OUT_FILE: a C++ header exposing its
2
+ # bytes as a static array (kBundledFontData/kBundledFontDataSize). Run via
3
+ # `cmake -DIN_FILE=... -DOUT_FILE=... -P embed_font.cmake` as a build-time
4
+ # add_custom_command, not configure_file -- a font baked in as bytes has no
5
+ # runtime file path to go stale once the shared library is installed
6
+ # somewhere other than its own build tree (see this script's own call site
7
+ # in the top-level CMakeLists.txt for the packaging bug this replaces).
8
+ if(NOT DEFINED IN_FILE OR NOT DEFINED OUT_FILE)
9
+ message(FATAL_ERROR "embed_font.cmake requires -DIN_FILE=... -DOUT_FILE=...")
10
+ endif()
11
+
12
+ file(READ "${IN_FILE}" _hex HEX)
13
+ string(LENGTH "${_hex}" _hexLen)
14
+ math(EXPR _byteCount "${_hexLen} / 2")
15
+ string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," _bytesCsv "${_hex}")
16
+
17
+ file(WRITE "${OUT_FILE}"
18
+ "// Auto-generated by cmake/embed_font.cmake from ${IN_FILE} -- do not edit.
19
+ #pragma once
20
+ #include <cstddef>
21
+
22
+ namespace oscadeval {
23
+ inline constexpr unsigned char kBundledFontData[] = {
24
+ ${_bytesCsv}
25
+ };
26
+ inline constexpr std::size_t kBundledFontDataSize = ${_byteCount};
27
+ } // namespace oscadeval
28
+ ")
@@ -46,7 +46,7 @@ std::string fmtLocals(const std::unordered_map<std::string, Value>& locals) {
46
46
  void traceEveryStatement() {
47
47
  std::cout << "1. trace every statement:\n";
48
48
  DebugHooks hooks;
49
- hooks.debugHook = [](int line, int depth, bool, const std::string&, const std::vector<CallStackFrame>&,
49
+ hooks.debugHook = [](int line, int depth, bool, bool, const std::string&, const std::vector<CallStackFrame>&,
50
50
  const DebugFramesFn& getFrame) {
51
51
  std::vector<DebugFrame> frames = getFrame();
52
52
  DebugFrame frame = frames.empty() ? DebugFrame{} : frames.front();
@@ -69,7 +69,7 @@ void stopAtBreakpoint() {
69
69
  constexpr int kBreakLine = 3; // the `translate(...) sphere(...)` statement
70
70
 
71
71
  DebugHooks hooks;
72
- hooks.debugHook = [](int line, int, bool, const std::string&, const std::vector<CallStackFrame>&, const DebugFramesFn&) {
72
+ hooks.debugHook = [](int line, int, bool, bool, const std::string&, const std::vector<CallStackFrame>&, const DebugFramesFn&) {
73
73
  DebugAction action;
74
74
  if (line == kBreakLine) {
75
75
  std::cout << " breakpoint hit at line " << line << "\n";
@@ -97,7 +97,7 @@ void stopAtBreakpoint() {
97
97
  void overrideVariableViaMods() {
98
98
  std::cout << "3. override a variable via mods:\n";
99
99
  DebugHooks hooks;
100
- hooks.debugHook = [](int line, int, bool, const std::string&, const std::vector<CallStackFrame>&, const DebugFramesFn&) {
100
+ hooks.debugHook = [](int line, int, bool, bool, const std::string&, const std::vector<CallStackFrame>&, const DebugFramesFn&) {
101
101
  DebugAction action;
102
102
  if (line == 2) action.mods["width"] = Value{2.0}; // about to run `cube([width, width, width])`
103
103
  return action;
@@ -86,25 +86,31 @@ inline constexpr const char* kDebuggingStoppedMessage = "Debugging stopped.";
86
86
  // genuine (non-debugger-triggered) EvalError leaves it at.
87
87
  enum class PostRunAction { None, Stopped, Restart, Quit };
88
88
 
89
- // Called at (approximately) every top-level statement in every block --
90
- // see Evaluator::evalChildren's single call site for the exact mechanism,
91
- // and its own comment for why this port checks at statement granularity
92
- // only, not the reference's additional expr_level sub-expression checks
93
- // (list-comprehension clauses, ternary branches, ...): those exist in the
94
- // reference to let a debugger single-step through individual expression
95
- // evaluation, which no consumer of this seam (debug_repl.cpp's REPL,
96
- // nor a plausible future GUI one) actually needs -- "pause on any
97
- // statement, inspect locals, set a breakpoint" is fully served by
98
- // statement-level granularity alone, at a small fraction of the
99
- // reference's own call-site count. Also called once, with `forced=true`,
100
- // right before evaluating a user function/function-literal's body
101
- // expression (functions have no statement-level granularity to hang a
102
- // hook off otherwise) and by breakpoint() (forced=true, unconditionally,
103
- // matching the reference's own _check_debug(..., forced=True) call).
89
+ // Called at every point the Python reference calls _check_debug: every
90
+ // top-level statement in every block (Evaluator::evalChildren's single
91
+ // statement checkpoint), plus the reference's finer-grained sub-statement
92
+ // sites -- ternary condition and chosen branch, if/else branch entry,
93
+ // for-loop per-variable bindings and per-iteration body entry,
94
+ // statement- and expression-form let() assignments, expression-form
95
+ // echo()/assert(), every list-comprehension clause (if / if-else / let /
96
+ // each / for / C-style-for, plus bare elements), modifier-wrapped
97
+ // children, and user function / function-literal call sites. Also called
98
+ // once right before evaluating a user function/function-literal's body
99
+ // expression, and by breakpoint() (forced=true, unconditionally, matching
100
+ // the reference's own _check_debug(..., forced=True) call).
101
+ //
102
+ // `exprLevel` mirrors the reference's `expr_level` keyword exactly: true
103
+ // for the sub-expression markers a debugger should NOT treat as a
104
+ // steppable/breakpointable statement (a chosen ternary or if branch, a
105
+ // for-loop body entry, a list-comprehension body element, a C-style-for
106
+ // condition re-check). Both this port's own debug_repl.cpp and the
107
+ // reference's _debug_repl.py gate every step/breakpoint decision on
108
+ // `!exprLevel`, while still pausing unconditionally on `forced`.
109
+ //
104
110
  // `depth` is the live user-call-stack depth (Evaluator::callStack_.size())
105
111
  // at the pause point. Mirrors Evaluator._check_debug/the debug_hook
106
112
  // callback contract.
107
- using DebugHookFn = std::function<DebugAction(int line, int depth, bool forced, const std::string& origin,
113
+ using DebugHookFn = std::function<DebugAction(int line, int depth, bool forced, bool exprLevel, const std::string& origin,
108
114
  const std::vector<CallStackFrame>& callStack, const DebugFramesFn& getFrame)>;
109
115
 
110
116
  // Called from Evaluator::error() right before it throws, if set -- gives a
@@ -128,7 +128,7 @@ public:
128
128
  bool runPrompt();
129
129
 
130
130
  // Wire these three into Evaluator's DebugHooks (see debug_hooks.hpp).
131
- DebugAction debugHook(int line, int depth, bool forced, const std::string& origin,
131
+ DebugAction debugHook(int line, int depth, bool forced, bool exprLevel, const std::string& origin,
132
132
  const std::vector<CallStackFrame>& callStack, const DebugFramesFn& getFrame);
133
133
  void errorBreak(int line, const std::string& header, const std::string& origin, const std::vector<CallStackFrame>& callStack,
134
134
  const DebugFramesFn& getFrame);
@@ -323,13 +323,16 @@ public:
323
323
  // filtering, which lives entirely in the injected hook, not here) --
324
324
  // it's simply passed through so the hook itself can distinguish an
325
325
  // unconditional breakpoint()/function-entry pause from a normal
326
- // statement-boundary check. Public: called from evalChildren() (every
327
- // ordinary statement), evalUserFunction()/evalFunctionLiteral() (function
328
- // body entry), and builtins/control.cpp's resolveBreakpoint (a free
329
- // function). See debug_hooks.hpp's DebugHookFn doc comment for the
330
- // exact statement-level-only scope decision. Mirrors
331
- // Evaluator._check_debug.
332
- void checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced = false);
326
+ // statement-boundary check. `exprLevel=true` marks a sub-statement
327
+ // (expression-granularity) checkpoint that a stepping debugger should
328
+ // not treat as a statement boundary -- see debug_hooks.hpp's
329
+ // DebugHookFn doc comment for the full list of call sites and the
330
+ // exprLevel contract. Public: called from evalChildren() (every
331
+ // ordinary statement), evalUserFunction()/evalFunctionLiteral()
332
+ // (function body entry), and builtins/control.cpp's
333
+ // resolveBreakpoint/resolveIntersectionFor (free functions). Mirrors
334
+ // Evaluator._check_debug, including its parameter defaults.
335
+ void checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced = false, bool exprLevel = false);
333
336
 
334
337
  // (origin, line) for each top-level, non-declaration child of the
335
338
  // node checkDebug() was just called with -- i.e., if that node is a
@@ -789,6 +792,17 @@ private:
789
792
  // strategy this enables).
790
793
  static bool bytecodeVmEnabled();
791
794
 
795
+ // Per-evaluator gate actually consulted before using a compiled chunk.
796
+ // Compiled bytecode has no per-AST-node debug checkpoints (its whole
797
+ // point is to flatten those nodes away), so a function body run on the
798
+ // VM would silently skip every checkDebug() call site the interpreter
799
+ // path makes -- ternary branches, let()/echo()/assert() expression
800
+ // forms, list-comprehension clauses, nested call sites. Debugging
801
+ // therefore always takes the interpreter path, where the port matches
802
+ // the reference's _check_debug placement exactly. Costs nothing when
803
+ // no debugger is attached, which is every non-debug render.
804
+ bool useBytecodeVm() const { return !debugHooks_.debugHook && bytecodeVmEnabled(); }
805
+
792
806
  // -- Profiling (Phase 9) --------------------------------------------
793
807
 
794
808
  // (kind, name, callOrigin, callLine) -- the exact call-site identity 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.3.0"
7
+ version = "0.4.1"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -47,6 +47,8 @@ target_include_directories(openscad_cpp_evaluator PUBLIC
47
47
  )
48
48
  target_include_directories(openscad_cpp_evaluator PRIVATE ${stb_SOURCE_DIR} ${CMAKE_BINARY_DIR}/generated)
49
49
 
50
+ add_dependencies(openscad_cpp_evaluator generate_bundled_font)
51
+
50
52
  target_link_libraries(openscad_cpp_evaluator PUBLIC openscad_cpp_parser manifold Boost::polygon)
51
53
  target_link_libraries(openscad_cpp_evaluator PRIVATE linenoise)
52
54
  target_compile_options(openscad_cpp_evaluator PRIVATE
@@ -112,6 +112,11 @@ CSGParams resolveIntersectionFor(Evaluator& ev, const oscad::ModularIntersection
112
112
 
113
113
  std::function<void(size_t, EvalContext&)> recurse = [&](size_t depth, EvalContext& parentCtx) {
114
114
  if (depth == varSeqs.size()) {
115
+ // One body-entry marker per full cartesian-product iteration
116
+ // and nothing per individual variable binding (unlike
117
+ // evalFor) -- mirrors _resolve_intersection_for's single
118
+ // `_check_debug(body_node[0], loop_ctx, expr_level=True)`.
119
+ if (!bodyNodes.empty()) ev.checkDebug(*bodyNodes.front(), parentCtx, /*forced=*/false, /*exprLevel=*/true);
115
120
  const size_t before = ev.currentTreeFrameSize();
116
121
  ev.evalChildren(bodyNodes, parentCtx);
117
122
  const size_t after = ev.currentTreeFrameSize();
@@ -140,6 +140,16 @@ void Evaluator::evalModifier(const oscad::ModuleInstantiation& child, const osca
140
140
  // always empty (role tagging happens entirely in the matching
141
141
  // generate function -- generateHighlight/Background/ShowOnly).
142
142
  buildTreeNode(kind, wrapperNode, [&]() {
143
+ // The wrapped child gets its own statement-level stop, in addition
144
+ // to the modifier node's (which evalChildren already fired before
145
+ // dispatching here) -- so `#cube(1);` pauses twice, like the
146
+ // reference. There, _resolve_modifier_child has no _check_debug of
147
+ // its own: it calls _eval_statement(node.child, ctx), and _that_
148
+ // dispatcher independently checks every _TREE_NODE_TYPES node it
149
+ // is handed. This port's evalStatement has no such blanket check
150
+ // (evalChildren owns it), so the equivalent stop is made explicit
151
+ // here.
152
+ checkDebug(child, ctx);
143
153
  evalStatement(child, ctx);
144
154
  return CSGParams{};
145
155
  });
@@ -425,18 +425,24 @@ bool DebugRepl::runPrompt() {
425
425
  }
426
426
  }
427
427
 
428
- DebugAction DebugRepl::debugHook(int line, int depth, bool forced, const std::string& origin,
428
+ DebugAction DebugRepl::debugHook(int line, int depth, bool forced, bool exprLevel, const std::string& origin,
429
429
  const std::vector<CallStackFrame>& callStack, const DebugFramesFn& getFrame) {
430
430
  if (quit_) return DebugAction{true, {}};
431
431
 
432
+ // Every step/breakpoint/first-statement decision below is gated on
433
+ // !exprLevel -- a sub-expression marker (chosen ternary/if branch,
434
+ // loop body entry, list-comprehension body element) is not a statement
435
+ // this REPL steps to or breaks on. Only `forced` (breakpoint()) and an
436
+ // async pause request ignore it. Mirrors _debug_repl.py's debug_hook
437
+ // exactly, clause for clause.
432
438
  const std::string resolved = resolveOrigin(origin);
433
439
  bool stepHit = false;
434
440
  if (stepCmd_ == "over") {
435
- stepHit = depth <= stepDepth_ && resolved == stepOrigin_ && line != stepLine_;
441
+ stepHit = depth <= stepDepth_ && resolved == stepOrigin_ && line != stepLine_ && !exprLevel;
436
442
  } else if (stepCmd_ == "into") {
437
- stepHit = line != stepLine_ || resolved != stepOrigin_;
443
+ stepHit = (line != stepLine_ || resolved != stepOrigin_) && !exprLevel;
438
444
  } else if (stepCmd_ == "out") {
439
- stepHit = depth < stepDepth_;
445
+ stepHit = depth < stepDepth_ && !exprLevel;
440
446
  } else if (stepCmd_ == "to_child") {
441
447
  // Pause the first time control reaches one of the paused call's
442
448
  // own children (wherever children()/children(N) forwards to
@@ -444,7 +450,7 @@ DebugAction DebugRepl::debugHook(int line, int depth, bool forced, const std::st
444
450
  // back to the same "call returned" safety net step-out uses, so
445
451
  // this can never hang. Mirrors BelfrySCAD's DebugSession::
446
452
  // _make_hook exactly.
447
- stepHit = stepToChildTargets_.count({resolved, line}) > 0 || depth < stepDepth_;
453
+ stepHit = !exprLevel && (stepToChildTargets_.count({resolved, line}) > 0 || depth < stepDepth_);
448
454
  }
449
455
 
450
456
  // Read-and-clear, same as BelfrySCAD's own DebugSession.pause()/
@@ -454,8 +460,8 @@ DebugAction DebugRepl::debugHook(int line, int depth, bool forced, const std::st
454
460
  // immediate re-pause; a harmless quirk, not a hang or crash.
455
461
  const bool pauseRequested = pauseRequested_.exchange(false, std::memory_order_relaxed);
456
462
 
457
- const bool shouldPause = forced || pauseRequested || (breakOnFirst_ && resolved == sourcePath_) ||
458
- breakpoints_[resolved].count(line) > 0 || stepHit;
463
+ const bool shouldPause = forced || pauseRequested || (breakOnFirst_ && !exprLevel && resolved == sourcePath_) ||
464
+ (breakpoints_[resolved].count(line) > 0 && !exprLevel) || stepHit;
459
465
  if (!shouldPause) return DebugAction{};
460
466
 
461
467
  breakOnFirst_ = false;
@@ -74,14 +74,14 @@ std::vector<DebugFrame> Evaluator::buildDebugFrames(const EvalContext* ctx) cons
74
74
  return frames;
75
75
  }
76
76
 
77
- void Evaluator::checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced) {
77
+ void Evaluator::checkDebug(const oscad::ASTNode& node, EvalContext& ctx, bool forced, bool exprLevel) {
78
78
  if (!debugHooks_.debugHook) return;
79
79
  const oscad::Position& pos = node.position();
80
80
  const int depth = static_cast<int>(callStack_.size());
81
81
  const DebugFramesFn getFrame = [this, &ctx]() { return buildDebugFrames(&ctx); };
82
82
  lastChildrenPositions_ = childStatementPositions(node);
83
83
 
84
- DebugAction action = debugHooks_.debugHook(pos.line, depth, forced, pos.origin, callStack_, getFrame);
84
+ DebugAction action = debugHooks_.debugHook(pos.line, depth, forced, exprLevel, pos.origin, callStack_, getFrame);
85
85
  for (auto& [k, v] : action.mods) ctx.let_->set(k, v);
86
86
  if (action.stop) throw EvalError(kDebuggingStoppedMessage);
87
87
  }
@@ -119,6 +119,11 @@ Value Evaluator::evalIdentifier(const std::string& name, const oscad::Position*
119
119
 
120
120
  // -- List comprehensions ----------------------------------------------
121
121
 
122
+ // Debug checkpoints below mirror the reference's _eval_list_comp AND its
123
+ // near-duplicate twin _eval_list_comp_body (both dispatch over the same
124
+ // ListComp* kinds with identical _check_debug placement; this port folds
125
+ // them into this one function plus evalListCompBody's ListComprehension
126
+ // early-out, so one set of calls covers both).
122
127
  void Evaluator::evalListElement(const oscad::ASTNode& elem, EvalContext& ctx, std::vector<Value>& out) {
123
128
  switch (elem.kind()) {
124
129
  case oscad::NodeKind::ListCompFor: {
@@ -147,6 +152,11 @@ void Evaluator::evalListElement(const oscad::ASTNode& elem, EvalContext& ctx, st
147
152
  for (const Value& val : pairs[depth].values) {
148
153
  EvalContext childCtx = parentCtx.letChildCtx();
149
154
  childCtx.let_->set(pairs[depth].name, val);
155
+ // Per-binding stop, same shape as evalFor's -- but with
156
+ // NO separate body-entry marker at depth == pairs.size()
157
+ // (_eval_listcomp_for has none; the body's own element
158
+ // check below supplies the expr-level stop instead).
159
+ checkDebug(*n.assignments[depth], childCtx);
150
160
  recurse(depth + 1, childCtx);
151
161
  }
152
162
  };
@@ -154,47 +164,73 @@ void Evaluator::evalListElement(const oscad::ASTNode& elem, EvalContext& ctx, st
154
164
  return;
155
165
  }
156
166
  case oscad::NodeKind::ListCompCFor: {
167
+ // Stop order per _eval_listcomp_cfor, exactly:
168
+ // init(s) -> [cond(true) -> body -> incr(s)]* -> cond(false)
169
+ // The condition is checked (expr-level) on EVERY pass including
170
+ // the final false one that ends the loop; body entry uses the
171
+ // whole ListCompCFor node, not the body expression.
157
172
  auto& n = static_cast<const oscad::ListCompCFor&>(elem);
158
173
  EvalContext loopCtx = ctx.letChildCtx();
159
- for (const auto& assign : n.inits) loopCtx.let_->set(assign->name->name, evalExpr(*assign->expr, loopCtx));
174
+ for (const auto& assign : n.inits) {
175
+ checkDebug(*assign, loopCtx);
176
+ loopCtx.let_->set(assign->name->name, evalExpr(*assign->expr, loopCtx));
177
+ }
160
178
 
161
179
  const bool isNestedLc = (n.body->kind() == oscad::NodeKind::ListComprehension);
162
180
  constexpr int kMaxCForIterations = 1'000'000;
163
181
  int iterations = 0;
164
- while (truthy(evalExpr(*n.condition, loopCtx))) {
182
+ while (true) {
183
+ checkDebug(*n.condition, loopCtx, /*forced=*/false, /*exprLevel=*/true);
184
+ if (!truthy(evalExpr(*n.condition, loopCtx))) break;
165
185
  ++iterations;
166
186
  if (iterations > kMaxCForIterations) {
167
187
  error("C-style for loop exceeded maximum iteration count", n);
168
188
  }
189
+ checkDebug(n, loopCtx);
169
190
  if (isNestedLc) {
170
191
  out.push_back(evalListLiteral(static_cast<const oscad::ListComprehension&>(*n.body), loopCtx));
171
192
  } else {
172
193
  appendAll(out, evalListCompBody(*n.body, loopCtx));
173
194
  }
174
- for (const auto& assign : n.incrs) loopCtx.let_->set(assign->name->name, evalExpr(*assign->expr, loopCtx));
195
+ for (const auto& assign : n.incrs) {
196
+ checkDebug(*assign, loopCtx);
197
+ loopCtx.let_->set(assign->name->name, evalExpr(*assign->expr, loopCtx));
198
+ }
175
199
  }
176
200
  return;
177
201
  }
178
202
  case oscad::NodeKind::ListCompIf: {
179
203
  auto& n = static_cast<const oscad::ListCompIf&>(elem);
180
- if (truthy(evalExpr(*n.condition, ctx))) appendAll(out, evalListCompBody(*n.trueExpr, ctx));
204
+ checkDebug(n, ctx);
205
+ if (truthy(evalExpr(*n.condition, ctx))) {
206
+ checkDebug(*n.trueExpr, ctx, /*forced=*/false, /*exprLevel=*/true);
207
+ appendAll(out, evalListCompBody(*n.trueExpr, ctx));
208
+ }
181
209
  return;
182
210
  }
183
211
  case oscad::NodeKind::ListCompIfElse: {
184
212
  auto& n = static_cast<const oscad::ListCompIfElse&>(elem);
213
+ checkDebug(n, ctx);
185
214
  const oscad::ASTNode& branch = truthy(evalExpr(*n.condition, ctx)) ? *n.trueExpr : *n.falseExpr;
215
+ checkDebug(branch, ctx, /*forced=*/false, /*exprLevel=*/true);
186
216
  appendAll(out, evalListCompBody(branch, ctx));
187
217
  return;
188
218
  }
189
219
  case oscad::NodeKind::ListCompLet: {
190
220
  auto& n = static_cast<const oscad::ListCompLet&>(elem);
191
221
  EvalContext letCtx = ctx.letChildCtx();
192
- for (const auto& assign : n.assignments) bindLetName(letCtx, assign->name->name, evalExpr(*assign->expr, letCtx));
222
+ // Per-assignment stops only -- no expr-level body marker here,
223
+ // unlike the if/if-else/each clauses (matches the reference).
224
+ for (const auto& assign : n.assignments) {
225
+ checkDebug(*assign, letCtx);
226
+ bindLetName(letCtx, assign->name->name, evalExpr(*assign->expr, letCtx));
227
+ }
193
228
  appendAll(out, evalListCompBody(*n.body, letCtx));
194
229
  return;
195
230
  }
196
231
  case oscad::NodeKind::ListCompEach: {
197
232
  auto& n = static_cast<const oscad::ListCompEach&>(elem);
233
+ checkDebug(n, ctx, /*forced=*/false, /*exprLevel=*/true);
198
234
  const oscad::ASTNode& inner = *n.body;
199
235
  if (isListCompClauseKind(inner.kind())) {
200
236
  for (const Value& item : evalListCompBody(inner, ctx)) appendEachInto(out, item);
@@ -204,6 +240,7 @@ void Evaluator::evalListElement(const oscad::ASTNode& elem, EvalContext& ctx, st
204
240
  return;
205
241
  }
206
242
  default:
243
+ checkDebug(elem, ctx, /*forced=*/false, /*exprLevel=*/true);
207
244
  out.push_back(evalExpr(static_cast<const oscad::Expression&>(elem), ctx));
208
245
  return;
209
246
  }
@@ -265,6 +302,9 @@ Value Evaluator::evalLetExpr(const oscad::LetOp& node, EvalContext& ctx) {
265
302
  // Unlike the *statement* form (evalLetBlock), which does not --
266
303
  // see that method's own doc comment for why this isn't a copy/paste
267
304
  // mistake in one direction or the other.
305
+ // Checked against childCtx (not ctx) -- _expr_let does the same,
306
+ // the mirror image of _eval_let_block's `_check_debug(assign, ctx)`.
307
+ checkDebug(*assign, childCtx);
268
308
  Value v = evalExpr(*assign->expr, childCtx);
269
309
  bindLetName(childCtx, assign->name->name, v);
270
310
  }
@@ -272,6 +312,7 @@ Value Evaluator::evalLetExpr(const oscad::LetOp& node, EvalContext& ctx) {
272
312
  }
273
313
 
274
314
  Value Evaluator::evalEchoExpr(const oscad::EchoOp& node, EvalContext& ctx) {
315
+ checkDebug(node, ctx); // _expr_echo
275
316
  doEcho(node.arguments, ctx);
276
317
  return evalExpr(*node.body, ctx);
277
318
  }
@@ -281,6 +322,7 @@ Value Evaluator::evalAssertExpr(const oscad::AssertOp& node, EvalContext& ctx) {
281
322
  // arguments via getArg()/CallArgs, the expression form indexes raw
282
323
  // arguments positionally -- mirrors the reference's _expr_assert
283
324
  // exactly (`raw[0].expr`/`raw[1].expr`, not _get_arg).
325
+ checkDebug(node, ctx); // _expr_assert, before the condition is evaluated
284
326
  const auto& raw = node.arguments;
285
327
  const bool condition = raw.empty() || truthy(evalExpr(*argExpr(*raw[0]), ctx));
286
328
  if (!condition) {
@@ -431,8 +473,14 @@ Value Evaluator::evalExpr(const oscad::Expression& node, EvalContext& ctx) {
431
473
  }
432
474
 
433
475
  case NodeKind::TernaryOp: {
476
+ // Mirrors _expr_ternary: a statement-level stop on the whole
477
+ // ternary before the condition runs, then an expr-level one on
478
+ // whichever branch was chosen, before evaluating it.
434
479
  auto& n = static_cast<const oscad::TernaryOp&>(node);
435
- return truthy(evalExpr(*n.condition, ctx)) ? evalExpr(*n.trueExpr, ctx) : evalExpr(*n.falseExpr, ctx);
480
+ checkDebug(n, ctx);
481
+ const oscad::Expression& branch = truthy(evalExpr(*n.condition, ctx)) ? *n.trueExpr : *n.falseExpr;
482
+ checkDebug(branch, ctx, /*forced=*/false, /*exprLevel=*/true);
483
+ return evalExpr(branch, ctx);
436
484
  }
437
485
 
438
486
  case NodeKind::BitwiseOrOp: {