openscad-cpp-evaluator 0.51.3__tar.gz → 0.52.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/CLAUDE.md +27 -1
  2. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bytecode.hpp +4 -1
  4. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/evaluator.hpp +16 -0
  5. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/pyproject.toml +1 -1
  6. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/booleans.cpp +8 -3
  7. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/color.cpp +3 -1
  8. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/control.cpp +3 -1
  9. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/extrude.cpp +12 -4
  10. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/roof.cpp +3 -1
  11. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/topology.cpp +18 -6
  12. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/transforms.cpp +3 -1
  13. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/bytecode_compiler.cpp +29 -0
  14. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/measure_geometry.cpp +3 -1
  15. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/stmt_eval.cpp +30 -2
  16. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_control_flow.cpp +204 -0
  17. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_render_expr.cpp +9 -2
  18. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.github/workflows/ci.yml +0 -0
  19. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.github/workflows/wheels.yml +0 -0
  20. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.gitignore +0 -0
  21. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.gitmodules +0 -0
  22. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/LICENSE +0 -0
  24. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/README.md +0 -0
  25. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/bindings/CMakeLists.txt +0 -0
  26. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/bindings/ast_to_py.cpp +0 -0
  27. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/bindings/module.cpp +0 -0
  28. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/cmake/embed_font.cmake +0 -0
  29. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/CMakeLists.txt +0 -0
  30. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/eval_perf_benchmark.cpp +0 -0
  31. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/manifold_cache_reuse.cpp +0 -0
  32. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/minimal_debugger.cpp +0 -0
  33. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  34. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/.gitignore +0 -0
  35. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  36. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  37. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/README.md +0 -0
  38. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  39. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  40. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  41. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  42. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  43. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  44. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  45. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  46. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  47. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  48. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  49. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  50. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  51. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  52. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  53. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  54. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  55. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  56. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  57. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  58. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  59. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  60. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  61. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  62. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  63. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  64. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  65. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  66. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  67. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  68. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  69. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  70. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  71. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  72. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  73. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  74. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  75. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  76. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  77. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  78. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  79. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  80. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  81. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  82. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  83. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  84. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  85. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  86. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  87. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  88. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  89. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  90. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  91. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  92. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  93. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  94. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  95. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  96. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  97. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  98. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  99. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  100. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  101. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  102. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  103. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  104. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  105. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  106. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  107. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  108. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  109. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  110. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  111. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  112. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  113. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  114. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  115. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  116. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  117. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  118. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  119. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  120. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  121. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  122. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  123. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  124. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  125. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  126. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  127. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  128. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  129. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  130. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  131. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  132. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/resources/fonts/LICENSE +0 -0
  133. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  134. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/CMakeLists.txt +0 -0
  135. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/builtins.hpp +0 -0
  136. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/call_args.cpp +0 -0
  137. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/dxf_dim.cpp +0 -0
  138. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/function_builtins.cpp +0 -0
  139. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/import.cpp +0 -0
  140. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/modifiers.cpp +0 -0
  141. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/primitives_2d.cpp +0 -0
  142. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/primitives_3d.cpp +0 -0
  143. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/registry.cpp +0 -0
  144. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/segments.cpp +0 -0
  145. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/surface.cpp +0 -0
  146. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/text.cpp +0 -0
  147. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/bytecode_vm.cpp +0 -0
  148. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/colored_body.cpp +0 -0
  149. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/csg_generate.cpp +0 -0
  150. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/csg_resolve.cpp +0 -0
  151. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/css_colors.cpp +0 -0
  152. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/debug/debug_repl.cpp +0 -0
  153. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/debug_profile.cpp +0 -0
  154. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/eval_context.cpp +0 -0
  155. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/eval_error.cpp +0 -0
  156. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/eval_use.cpp +0 -0
  157. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/export.cpp +0 -0
  158. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/expr_eval.cpp +0 -0
  159. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/dxf_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/mesh_import.cpp +0 -0
  161. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/surface_load.cpp +0 -0
  162. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/svg_import.cpp +0 -0
  163. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/manifold_cache.cpp +0 -0
  164. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/mesh_check.cpp +0 -0
  165. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/text/font_match.cpp +0 -0
  166. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/text/freetype_font_provider.cpp +0 -0
  167. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/text/text_metrics.cpp +0 -0
  168. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/user_calls.cpp +0 -0
  169. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/value.cpp +0 -0
  170. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/zip_stored.cpp +0 -0
  171. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/CMakeLists.txt +0 -0
  172. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_booleans.cpp +0 -0
  173. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_bytecode_compiler.cpp +0 -0
  174. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_children_separate.cpp +0 -0
  175. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_cli.cpp +0 -0
  176. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_csg_tree.cpp +0 -0
  177. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_debug_hooks.cpp +0 -0
  178. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_dxf_svg_import.cpp +0 -0
  179. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_eval_context.cpp +0 -0
  180. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_export.cpp +0 -0
  181. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_expr_eval.cpp +0 -0
  182. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_extrude_roof.cpp +0 -0
  183. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_feature_detection.cpp +0 -0
  184. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_function_builtins.cpp +0 -0
  185. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_helpers.hpp +0 -0
  186. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_import_export.cpp +0 -0
  187. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_manifold_cache.cpp +0 -0
  188. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_mesh_check.cpp +0 -0
  189. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_multi_color_merge.cpp +0 -0
  190. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_primitives.cpp +0 -0
  191. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_profiling.cpp +0 -0
  192. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_python_bindings.py +0 -0
  193. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_scope_trail.cpp +0 -0
  194. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_scoping.cpp +0 -0
  195. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_surface.cpp +0 -0
  196. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_tail_calls.cpp +0 -0
  197. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_text.cpp +0 -0
  198. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_transforms.cpp +0 -0
  199. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_unexpected_args.cpp +0 -0
  200. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_use.cpp +0 -0
  201. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_value.cpp +0 -0
  202. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_viewport_params.cpp +0 -0
  203. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_zip_stored.cpp +0 -0
  204. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/CMakeLists.txt +0 -0
  205. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/cli_lib.cpp +0 -0
  206. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/cli_lib.hpp +0 -0
  207. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/main.cpp +0 -0
  208. {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/uv.lock +0 -0
@@ -584,7 +584,33 @@ grep for `ponytail:`.
584
584
  mutable state threaded through recursive evaluation. `dyn`/`let_`/`dynPositions`/`dynExplicit`
585
585
  are `shared_ptr`-wrapped, not plain value members — `withScope()` relies on that aliasing to
586
586
  give sibling statements in the same block correct assignment visibility (see the header comment
587
- before reworking this; it's not an arbitrary choice).
587
+ before reworking this; it's not an arbitrary choice). **An `if`/`else` branch body is its own
588
+ scope**: assignments (including `$`-var assignments) inside it are visible for the rest of the
589
+ branch but must not survive the closing brace, so both engines evaluate a taken branch against
590
+ `childCtx()` — `branchScope()` in `stmt_eval.cpp`, `Op::OpenLetScope`/`Op::CloseExprScope`
591
+ around each branch on the compiled path. `let_`/`dyn` open a non-isolated child (the branch
592
+ reads everything outside, writes only inside) while `dynPositions` opens an **isolated** one, so
593
+ shadowing an outer name is not mistaken for reassigning it — two assignments to the same name
594
+ *within* one branch still warn, because they share that fresh level. Getting this wrong is not
595
+ just a spurious warning: before the fix a branch's writes escaped and silently changed later
596
+ geometry (BOSL2's `half_of()` reassigns `v` inside an `else if`, clobbering the outer `v`).
597
+ **A builtin operator's braced child block (`translate() { a = 1; }`) is its own scope by the
598
+ same rule.** Each builtin resolve function derives one `Evaluator::blockScope()` context for its
599
+ children (`booleans.cpp` derives ONE spanning both of `resolveCsg`'s passes, since the assignment
600
+ pass writes what the geometry pass reads); `emitBuiltinWrap`/`emitCsgWrap` bracket their inline
601
+ children with `Op::OpenLetScope`/`Op::CloseExprScope`. The bracket goes *inside* Push/Pop, because
602
+ `Op::PopBuiltinWrap` re-resolves the call's arguments and those must still see the OUTER ctx — an
603
+ assignment in the block must never feed back into the operator's own arguments.
604
+ **Do not "simplify" this into `resolveCallArgs`** by having it always return a child ctx, however
605
+ tempting — every builtin's child context does come from there, but so does every *user*-module
606
+ call's and every pure argument resolution, and the extra dyn level breaks BOSL2's `$`-var
607
+ propagation outright (`$tags_shown`/`$attach_to`/`$transform`/`$anchor_override` stop resolving).
608
+ That was tried: the whole unit suite passed and 130 of ~390 BOSL2 doc examples failed. Scope the
609
+ CHILDREN, not the call. `for`/`let`/`intersection_for` bodies, and a *user* module's child block (evaluated
610
+ through `childrenCallerCtx`), were already correct. `tests/test_control_flow.cpp`'s
611
+ `IfBranchScope.*` and `OperatorBlockScope.*` pin every case under both engines. One older test
612
+ (`RenderExpr.MeasuredSubtreeUsesItsOwnCoordinates`) had encoded the leak by reading a block's
613
+ variable from outside it, and was corrected.
588
614
  - `include/openscad_cpp_evaluator/eval_error.hpp`, `src/eval_error.cpp` — `EvalError` and the
589
615
  exact ERROR/TRACE string formatting real OpenSCAD produces. Also the **unexpected-argument
590
616
  warnings** (`warnUnexpectedNamedArg`/`warnTooManyPositionalArgs`/`warnUnexpectedArgs`), the port
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.51.3
3
+ Version: 0.52.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -687,7 +687,10 @@ enum class Op {
687
687
  // just "pop the current ctx level", identical either way.
688
688
  CloseExprScope,
689
689
 
690
- // ModularLet's own (statement-form) child scope -- mirrors
690
+ // ModularLet's own (statement-form) child scope, ALSO used to bracket
691
+ // an if/else branch body (see ModularIf/ModularIfElse in
692
+ // bytecode_compiler.cpp) -- a branch is its own scope in OpenSCAD and
693
+ // needs exactly this same child ctx, so one opcode covers both -- mirrors
691
694
  // Evaluator::evalLetBlock's own `ctx.childCtx(nullptr, std::nullopt,
692
695
  // ctx.childrenNodes, ctx.childrenCallerCtx)` call exactly, pushed onto
693
696
  // f.ctxChain (Op::CloseExprScope, above, pops it back off once the
@@ -144,6 +144,22 @@ public:
144
144
  // sub-groups of an existing children list without needing ownership.
145
145
  void evalChildren(const std::vector<const oscad::ASTNode*>& children, EvalContext& ctx);
146
146
 
147
+ // The child context a BRACED BLOCK's statements run in. A `{ ... }`
148
+ // block is a scope in OpenSCAD: assignments (and `$`-var assignments)
149
+ // inside it are visible for the rest of the block but must not survive
150
+ // the closing brace. Every builtin operator's resolve function derives
151
+ // one of these for its children; the compiled path does the same with
152
+ // an Op::OpenLetScope/Op::CloseExprScope bracket.
153
+ //
154
+ // Deliberately NOT folded into resolveCallArgs, though that is the one
155
+ // place every builtin's child context comes from: resolveCallArgs also
156
+ // serves user-module calls and pure argument resolution, and giving
157
+ // those an extra dyn level breaks BOSL2's `$`-var propagation
158
+ // ($tags_shown/$attach_to/$transform all stop resolving). Measured, not
159
+ // reasoned: 130 of ~390 BOSL2 doc examples failed that way. Scope the
160
+ // CHILDREN, not the call.
161
+ EvalContext blockScope(const EvalContext& ctx) const;
162
+
147
163
  // Top-level entry point: resolveTree() + generateTree(), then the
148
164
  // top-level `!` (show_only) filter -- if any returned body has
149
165
  // BodyRole::ShowOnly, the result is filtered down to just
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.51.3"
7
+ version = "0.52.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -91,22 +91,27 @@ CSGParams resolveCsg(Evaluator& ev, const oscad::ModularCall& node, EvalContext&
91
91
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
92
92
  (void)args;
93
93
 
94
+ // ONE scope around both passes -- see Evaluator::blockScope. The
95
+ // assignment pass writes into the same block scope the geometry pass
96
+ // then reads from, so it cannot be scoped per-pass.
97
+ EvalContext blockCtx = ev.blockScope(effCtx);
98
+
94
99
  std::vector<const oscad::ASTNode*> assignNodes;
95
100
  std::vector<const oscad::ASTNode*> geoNodes;
96
- for (const oscad::ASTNode* c : ev.expandChildStatements(node.children, effCtx)) {
101
+ for (const oscad::ASTNode* c : ev.expandChildStatements(node.children, blockCtx)) {
97
102
  if (c->kind() == oscad::NodeKind::Assignment) {
98
103
  assignNodes.push_back(c);
99
104
  } else if (c->kind() != oscad::NodeKind::ModuleDeclaration && c->kind() != oscad::NodeKind::FunctionDeclaration) {
100
105
  geoNodes.push_back(c);
101
106
  }
102
107
  }
103
- if (!assignNodes.empty()) ev.evalChildren(assignNodes, effCtx);
108
+ if (!assignNodes.empty()) ev.evalChildren(assignNodes, blockCtx);
104
109
 
105
110
  std::vector<Value> groupSizes;
106
111
  groupSizes.reserve(geoNodes.size());
107
112
  for (const oscad::ASTNode* geoNode : geoNodes) {
108
113
  const size_t before = ev.currentTreeFrameSize();
109
- ev.evalChildren(std::vector<const oscad::ASTNode*>{geoNode}, effCtx);
114
+ ev.evalChildren(std::vector<const oscad::ASTNode*>{geoNode}, blockCtx);
110
115
  groupSizes.push_back(Value{static_cast<double>(ev.currentTreeFrameSize() - before)});
111
116
  }
112
117
 
@@ -44,7 +44,9 @@ CSGParams resolveColor(Evaluator& ev, const oscad::ModularCall& node, EvalContex
44
44
  // under the color-tagged context -- the returned bodies themselves are
45
45
  // unused here; generate reads them back via each child's own .bodies
46
46
  // and stamps `rgba` onto all of them. Mirrors _resolve_color.
47
- ev.evalChildren(node.children, result.ctx);
47
+ // The child block is its own scope -- see Evaluator::blockScope.
48
+ EvalContext blockCtx = ev.blockScope(result.ctx);
49
+ ev.evalChildren(node.children, blockCtx);
48
50
  return std::move(result.params);
49
51
  }
50
52
 
@@ -31,7 +31,9 @@ std::vector<ColoredBody> generateChildren(Evaluator&, const CSGParams&, const st
31
31
  CSGParams resolveRender(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
32
32
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
33
33
  (void)args;
34
- ev.evalChildren(node.children, effCtx);
34
+ // The child block is its own scope -- see Evaluator::blockScope.
35
+ EvalContext blockCtx = ev.blockScope(effCtx);
36
+ ev.evalChildren(node.children, blockCtx);
35
37
  return CSGParams{};
36
38
  }
37
39
 
@@ -56,7 +56,9 @@ BuiltinWrapParams computeLinearExtrudeParams(Evaluator& ev, const oscad::Modular
56
56
 
57
57
  CSGParams resolveLinearExtrude(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
58
58
  BuiltinWrapParams result = computeLinearExtrudeParams(ev, node, ctx);
59
- ev.evalChildren(node.children, result.ctx);
59
+ // The child block is its own scope -- see Evaluator::blockScope.
60
+ EvalContext blockCtx = ev.blockScope(result.ctx);
61
+ ev.evalChildren(node.children, blockCtx);
60
62
  return std::move(result.params);
61
63
  }
62
64
 
@@ -107,7 +109,9 @@ BuiltinWrapParams computeRotateExtrudeParams(Evaluator& ev, const oscad::Modular
107
109
 
108
110
  CSGParams resolveRotateExtrude(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
109
111
  BuiltinWrapParams result = computeRotateExtrudeParams(ev, node, ctx);
110
- ev.evalChildren(node.children, result.ctx);
112
+ // The child block is its own scope -- see Evaluator::blockScope.
113
+ EvalContext blockCtx = ev.blockScope(result.ctx);
114
+ ev.evalChildren(node.children, blockCtx);
111
115
  return std::move(result.params);
112
116
  }
113
117
 
@@ -143,7 +147,9 @@ BuiltinWrapParams computeProjectionParams(Evaluator& ev, const oscad::ModularCal
143
147
 
144
148
  CSGParams resolveProjection(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
145
149
  BuiltinWrapParams result = computeProjectionParams(ev, node, ctx);
146
- ev.evalChildren(node.children, result.ctx);
150
+ // The child block is its own scope -- see Evaluator::blockScope.
151
+ EvalContext blockCtx = ev.blockScope(result.ctx);
152
+ ev.evalChildren(node.children, blockCtx);
147
153
  return std::move(result.params);
148
154
  }
149
155
 
@@ -209,7 +215,9 @@ BuiltinWrapParams computeOffsetParams(Evaluator& ev, const oscad::ModularCall& n
209
215
 
210
216
  CSGParams resolveOffset(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
211
217
  BuiltinWrapParams result = computeOffsetParams(ev, node, ctx);
212
- ev.evalChildren(node.children, result.ctx);
218
+ // The child block is its own scope -- see Evaluator::blockScope.
219
+ EvalContext blockCtx = ev.blockScope(result.ctx);
220
+ ev.evalChildren(node.children, blockCtx);
213
221
  return std::move(result.params);
214
222
  }
215
223
 
@@ -464,7 +464,9 @@ CSGParams computeRoofParams(Evaluator& ev, const CallArgs& args, EvalContext& ef
464
464
 
465
465
  CSGParams resolveRoof(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
466
466
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
467
- ev.evalChildren(node.children, effCtx);
467
+ // The child block is its own scope -- see Evaluator::blockScope.
468
+ EvalContext blockCtx = ev.blockScope(effCtx);
469
+ ev.evalChildren(node.children, blockCtx);
468
470
  return computeRoofParams(ev, args, effCtx);
469
471
  }
470
472
 
@@ -23,7 +23,9 @@ static constexpr double kDefaultSimplifyFraction = 0.001;
23
23
  CSGParams resolveHull(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
24
24
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
25
25
  (void)args;
26
- ev.evalChildren(node.children, effCtx);
26
+ // The child block is its own scope -- see Evaluator::blockScope.
27
+ EvalContext blockCtx = ev.blockScope(effCtx);
28
+ ev.evalChildren(node.children, blockCtx);
27
29
  return CSGParams{};
28
30
  }
29
31
 
@@ -85,7 +87,9 @@ std::vector<ColoredBody> generateHull(Evaluator&, const CSGParams&, const std::v
85
87
  CSGParams resolveFill(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
86
88
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
87
89
  (void)args;
88
- ev.evalChildren(node.children, effCtx);
90
+ // The child block is its own scope -- see Evaluator::blockScope.
91
+ EvalContext blockCtx = ev.blockScope(effCtx);
92
+ ev.evalChildren(node.children, blockCtx);
89
93
  return CSGParams{};
90
94
  }
91
95
 
@@ -146,7 +150,9 @@ std::vector<ColoredBody> generateFill(Evaluator& ev, const CSGParams&,
146
150
  CSGParams resolveMinkowski(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
147
151
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
148
152
  (void)args;
149
- ev.evalChildren(node.children, effCtx);
153
+ // The child block is its own scope -- see Evaluator::blockScope.
154
+ EvalContext blockCtx = ev.blockScope(effCtx);
155
+ ev.evalChildren(node.children, blockCtx);
150
156
  return CSGParams{};
151
157
  }
152
158
 
@@ -334,7 +340,9 @@ std::vector<ColoredBody> generateMinkowski(Evaluator& ev, const CSGParams&, cons
334
340
  CSGParams resolveMinkowskiDifference(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
335
341
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
336
342
  (void)args;
337
- ev.evalChildren(node.children, effCtx);
343
+ // The child block is its own scope -- see Evaluator::blockScope.
344
+ EvalContext blockCtx = ev.blockScope(effCtx);
345
+ ev.evalChildren(node.children, blockCtx);
338
346
  return CSGParams{};
339
347
  }
340
348
 
@@ -394,7 +402,9 @@ CSGParams resolveSimplify(Evaluator& ev, const oscad::ModularCall& node, EvalCon
394
402
  CSGParams params;
395
403
  // Position 0 so simplify(0.1) reads naturally.
396
404
  params["tolerance"] = getArg(args, 0, "tolerance", Value{});
397
- ev.evalChildren(node.children, effCtx);
405
+ // The child block is its own scope -- see Evaluator::blockScope.
406
+ EvalContext blockCtx = ev.blockScope(effCtx);
407
+ ev.evalChildren(node.children, blockCtx);
398
408
  return params;
399
409
  }
400
410
 
@@ -715,7 +725,9 @@ CSGParams resolveLevelSet(Evaluator& ev, const oscad::ModularCall& node, EvalCon
715
725
  params["isovalue"] = getArg(args, 2, "isovalue", Value{0.0});
716
726
  params["invert"] = getArg(args, 3, "invert", Value{false});
717
727
  params["edge"] = getArg(args, 4, "edge", Value{});
718
- ev.evalChildren(node.children, effCtx);
728
+ // The child block is its own scope -- see Evaluator::blockScope.
729
+ EvalContext blockCtx = ev.blockScope(effCtx);
730
+ ev.evalChildren(node.children, blockCtx);
719
731
  return params;
720
732
  }
721
733
 
@@ -219,7 +219,9 @@ BuiltinWrapParams computeTransformParams(Evaluator& ev, const oscad::ModularCall
219
219
 
220
220
  CSGParams resolveTransform(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
221
221
  BuiltinWrapParams result = computeTransformParams(ev, node, ctx);
222
- ev.evalChildren(node.children, result.ctx);
222
+ // The child block is its own scope -- see Evaluator::blockScope.
223
+ EvalContext blockCtx = ev.blockScope(result.ctx);
224
+ ev.evalChildren(node.children, blockCtx);
223
225
  return std::move(result.params);
224
226
  }
225
227
 
@@ -1289,7 +1289,15 @@ public:
1289
1289
  chunk_.builtinWrapSites.push_back(std::move(site));
1290
1290
  const int idx = static_cast<int>(chunk_.builtinWrapSites.size()) - 1;
1291
1291
  out.push_back({Op::PushBuiltinWrap, idx, 0, &wrapperNode.position()});
1292
+ // The child block is its own scope -- same rule as an if-branch, and
1293
+ // the interpreter gets it from resolveCallArgs' child ctx (see
1294
+ // call_args.cpp). Opened INSIDE the Push/Pop bracket so that
1295
+ // Op::PopBuiltinWrap's own resolveCallArgs call still sees the
1296
+ // OUTER ctx: an assignment in the block must not feed back into the
1297
+ // operator's own arguments.
1298
+ out.push_back({Op::OpenLetScope, 0, 0, nullptr});
1292
1299
  compileStatementList(children, out);
1300
+ out.push_back({Op::CloseExprScope, 0, 0, nullptr});
1293
1301
  out.push_back({Op::PopBuiltinWrap, idx, 0, &wrapperNode.position()});
1294
1302
  }
1295
1303
 
@@ -1312,6 +1320,10 @@ public:
1312
1320
  chunk_.csgWrapSites.push_back(std::move(site));
1313
1321
  const int idx = static_cast<int>(chunk_.csgWrapSites.size()) - 1;
1314
1322
  out.push_back({Op::PushCsgWrap, idx, 0, &call.position()});
1323
+ // One scope around BOTH passes -- see emitBuiltinWrap's own note.
1324
+ // The assignment pass must write into the same block scope the
1325
+ // geometry pass then reads from, so it cannot be bracketed per-pass.
1326
+ out.push_back({Op::OpenLetScope, 0, 0, nullptr});
1315
1327
 
1316
1328
  std::vector<const oscad::ASTNode*> assignNodes;
1317
1329
  std::vector<const oscad::ASTNode*> geoNodes;
@@ -1335,6 +1347,7 @@ public:
1335
1347
  compileStatementList(std::vector<const oscad::ASTNode*>{geoNode}, out);
1336
1348
  out.push_back({Op::CsgGroupEnd, 0, 0, nullptr});
1337
1349
  }
1350
+ out.push_back({Op::CloseExprScope, 0, 0, nullptr});
1338
1351
  out.push_back({Op::PopCsgWrap, idx, 0, &call.position()});
1339
1352
  }
1340
1353
 
@@ -1688,13 +1701,25 @@ public:
1688
1701
  emitBuiltinWrap(CompiledChunk::BuiltinWrapSite::Kind::Modifier, "show_only", n, {n.child.get()}, out);
1689
1702
  return;
1690
1703
  }
1704
+ // Both if-forms bracket the taken branch in Op::OpenLetScope /
1705
+ // Op::CloseExprScope. An if-branch body is its own scope in
1706
+ // OpenSCAD -- assignments inside must not survive the closing
1707
+ // brace -- and the child ctx a let-BLOCK gets is exactly the
1708
+ // one a branch needs, so the same opcode serves both (see
1709
+ // branchScope, stmt_eval.cpp, for the interpreter's identical
1710
+ // rule and the full reasoning). The scope is opened INSIDE the
1711
+ // conditional jump's target range, so a branch that is not
1712
+ // taken never opens one, and each branch's own Close is the
1713
+ // last thing before the jump past its sibling.
1691
1714
  case NodeKind::ModularIf: {
1692
1715
  auto& n = static_cast<const oscad::ModularIf&>(stmt);
1693
1716
  const int jumpFalseIdx = static_cast<int>(out.size());
1694
1717
  out.push_back({Op::NativeCondJumpIfFalse, internNativeExpr(n.condition.get()), 0, &n.condition->position()});
1695
1718
  const oscad::ASTNode* marker = n.trueBranch.empty() ? &stmt : n.trueBranch.front().get();
1696
1719
  out.push_back({Op::NativeCheckDebugExprLevel, internNativeStatement(marker), 0, nullptr});
1720
+ out.push_back({Op::OpenLetScope, 0, 0, nullptr});
1697
1721
  compileStatementList(n.trueBranch, out);
1722
+ out.push_back({Op::CloseExprScope, 0, 0, nullptr});
1698
1723
  out[static_cast<size_t>(jumpFalseIdx)].b = static_cast<int>(out.size());
1699
1724
  return;
1700
1725
  }
@@ -1704,13 +1729,17 @@ public:
1704
1729
  out.push_back({Op::NativeCondJumpIfFalse, internNativeExpr(n.condition.get()), 0, &n.condition->position()});
1705
1730
  const oscad::ASTNode* trueMarker = n.trueBranch.empty() ? &stmt : n.trueBranch.front().get();
1706
1731
  out.push_back({Op::NativeCheckDebugExprLevel, internNativeStatement(trueMarker), 0, nullptr});
1732
+ out.push_back({Op::OpenLetScope, 0, 0, nullptr});
1707
1733
  compileStatementList(n.trueBranch, out);
1734
+ out.push_back({Op::CloseExprScope, 0, 0, nullptr});
1708
1735
  const int jumpEndIdx = static_cast<int>(out.size());
1709
1736
  out.push_back({Op::Jump, 0, 0, nullptr});
1710
1737
  out[static_cast<size_t>(jumpFalseIdx)].b = static_cast<int>(out.size());
1711
1738
  const oscad::ASTNode* falseMarker = n.falseBranch.empty() ? &stmt : n.falseBranch.front().get();
1712
1739
  out.push_back({Op::NativeCheckDebugExprLevel, internNativeStatement(falseMarker), 0, nullptr});
1740
+ out.push_back({Op::OpenLetScope, 0, 0, nullptr});
1713
1741
  compileStatementList(n.falseBranch, out);
1742
+ out.push_back({Op::CloseExprScope, 0, 0, nullptr});
1714
1743
  out[static_cast<size_t>(jumpEndIdx)].a = static_cast<int>(out.size());
1715
1744
  return;
1716
1745
  }
@@ -228,7 +228,9 @@ Value Evaluator::evalRenderExpr(const oscad::RenderExpression& node, EvalContext
228
228
  treeStack_.emplace_back();
229
229
  std::vector<std::unique_ptr<CSGNode>> sub;
230
230
  try {
231
- evalChildren(node.children, effCtx);
231
+ // The child block is its own scope -- see blockScope.
232
+ EvalContext blockCtx = blockScope(effCtx);
233
+ evalChildren(node.children, blockCtx);
232
234
  sub = std::move(treeStack_.back());
233
235
  treeStack_.pop_back();
234
236
  } catch (...) {
@@ -6,6 +6,32 @@
6
6
 
7
7
  namespace oscadeval {
8
8
 
9
+ // A braced block is its own scope in OpenSCAD: assignments (and `$`-var
10
+ // assignments) inside it are visible to the rest of the block and to
11
+ // anything it calls, but must NOT survive the closing brace.
12
+ //
13
+ // a = 1;
14
+ // if (true) { a = 2; }
15
+ // echo(a); // 1 in OpenSCAD -- was 2 here
16
+ //
17
+ // Evaluating a block against the enclosing ctx let those writes escape,
18
+ // which silently changed geometry (BOSL2's half_of() reassigns `v` inside
19
+ // an `else if`, clobbering the outer `v` for everything after it) and also
20
+ // produced a bogus "assigned on line N but was overwritten" warning for
21
+ // what is really a shadow, not an overwrite. Verified against OpenSCAD
22
+ // 2026.02.01 for values, `$`-vars, nesting, and the same-scope warning
23
+ // that must still fire.
24
+ //
25
+ // This is exactly the child context a let-BLOCK gets (evalLetBlock, and
26
+ // Op::OpenLetScope on the compiled path): let_/dyn open a non-isolated
27
+ // child so the block reads everything outside and writes only inside,
28
+ // while dynPositions opens an ISOLATED one so shadowing an outer name is
29
+ // not mistaken for reassigning it -- two assignments to the same name
30
+ // within the one block still warn, because they share this fresh level.
31
+ EvalContext Evaluator::blockScope(const EvalContext& ctx) const {
32
+ return ctx.childCtx(nullptr, std::nullopt, ctx.childrenNodes, ctx.childrenCallerCtx);
33
+ }
34
+
9
35
  void Evaluator::evalAssignment(const oscad::Assignment& node, EvalContext& ctx) {
10
36
  const std::string& name = node.name->name;
11
37
  if (!name.empty() && name[0] == '$') {
@@ -196,7 +222,8 @@ void Evaluator::evalStatement(const oscad::ASTNode& node, EvalContext& ctx) {
196
222
  if (truthy(evalExprMaybeCompiled(*n.condition, ctx))) {
197
223
  checkDebug(n.trueBranch.empty() ? node : *n.trueBranch.front(), ctx, /*forced=*/false,
198
224
  /*exprLevel=*/true);
199
- evalChildren(n.trueBranch, ctx);
225
+ EvalContext branchCtx = blockScope(ctx);
226
+ evalChildren(n.trueBranch, branchCtx);
200
227
  }
201
228
  return;
202
229
  }
@@ -204,7 +231,8 @@ void Evaluator::evalStatement(const oscad::ASTNode& node, EvalContext& ctx) {
204
231
  auto& n = static_cast<const oscad::ModularIfElse&>(node);
205
232
  const auto& branch = truthy(evalExprMaybeCompiled(*n.condition, ctx)) ? n.trueBranch : n.falseBranch;
206
233
  checkDebug(branch.empty() ? node : *branch.front(), ctx, /*forced=*/false, /*exprLevel=*/true);
207
- evalChildren(branch, ctx);
234
+ EvalContext branchCtx = blockScope(ctx);
235
+ evalChildren(branch, branchCtx);
208
236
  return;
209
237
  }
210
238
  case oscad::NodeKind::ModularFor:
@@ -144,6 +144,210 @@ TEST(IfElseStatement, PicksCorrectBranch) {
144
144
  EXPECT_EQ(e.tree[0]->kind, "sphere");
145
145
  }
146
146
 
147
+ // An if/else branch body is its own scope in OpenSCAD: assignments inside
148
+ // it are visible for the rest of the branch, but must NOT survive the
149
+ // closing brace. Regression tests for a real bug -- the branch used to be
150
+ // evaluated against the ENCLOSING context, so its writes escaped and
151
+ // silently changed later geometry. BOSL2's half_of() hit exactly this:
152
+ // it reassigns `v` inside an `else if`, which clobbered the outer `v` for
153
+ // everything after it, and also produced a bogus "assigned on line N but
154
+ // was overwritten" warning for what is really a shadow.
155
+ //
156
+ // Every case below was verified against real OpenSCAD 2026.02.01 first.
157
+ // Pinned under both VM states explicitly: evalStatement's ModularIf/
158
+ // ModularIfElse and compileOneStatement's own copies are independent.
159
+
160
+ namespace {
161
+
162
+ void checkBranchIsItsOwnScope(bool useVm) {
163
+ ScopedVm vm(useVm);
164
+ std::vector<std::string> echoed;
165
+ runScript("a = 1;\n"
166
+ "if (true) { a = 2; if (true) { a = 3; echo(deep=a); } echo(mid=a); }\n"
167
+ "echo(outer=a);\n",
168
+ [&](const std::string& msg) { echoed.push_back(msg); });
169
+ // Each nesting level shadows independently: the inner writes are gone
170
+ // by the time control leaves each brace.
171
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: deep = 3", "ECHO: mid = 2", "ECHO: outer = 1"}));
172
+ }
173
+
174
+ void checkBranchDollarVarDoesNotEscape(bool useVm) {
175
+ ScopedVm vm(useVm);
176
+ std::vector<std::string> echoed;
177
+ runScript("$fn = 8;\n"
178
+ "if (true) { $fn = 64; echo(inside=$fn); }\n"
179
+ "echo(after=$fn);\n",
180
+ [&](const std::string& msg) { echoed.push_back(msg); });
181
+ // `$`-vars are scoped by the branch too, not just plain names.
182
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: inside = 64", "ECHO: after = 8"}));
183
+ }
184
+
185
+ void checkDoubleAssignmentWithinOneBranchStillWarns(bool useVm) {
186
+ ScopedVm vm(useVm);
187
+ std::vector<std::string> messages;
188
+ runScript("if (true) { z = 1; z = 2; echo(z=z); }\n",
189
+ [&](const std::string& msg) { messages.push_back(msg); });
190
+ // The branch gets a FRESH dynPositions level, so shadowing an outer
191
+ // name is not reported -- but two assignments to the same name inside
192
+ // the one branch share that level and must still warn, exactly as
193
+ // OpenSCAD does. Guards against "fixing" the leak by disabling the
194
+ // check outright.
195
+ const bool warned = std::any_of(messages.begin(), messages.end(), [](const std::string& m) {
196
+ return m.find("z was assigned on line 1 but was overwritten") != std::string::npos;
197
+ });
198
+ EXPECT_TRUE(warned) << "expected the same-scope reassignment warning";
199
+ EXPECT_NE(std::find(messages.begin(), messages.end(), "ECHO: z = 2"), messages.end());
200
+ }
201
+
202
+ void checkShadowingAnOuterNameDoesNotWarn(bool useVm) {
203
+ ScopedVm vm(useVm);
204
+ std::vector<std::string> messages;
205
+ runScript("y = 1;\n"
206
+ "if (true) { y = 99; }\n",
207
+ [&](const std::string& msg) { messages.push_back(msg); });
208
+ const bool warned = std::any_of(messages.begin(), messages.end(), [](const std::string& m) {
209
+ return m.find("was overwritten") != std::string::npos;
210
+ });
211
+ EXPECT_FALSE(warned) << "shadowing an outer name in a branch is not an overwrite";
212
+ }
213
+
214
+ void checkElseIfBranchIsItsOwnScope(bool useVm) {
215
+ ScopedVm vm(useVm);
216
+ std::vector<std::string> echoed;
217
+ // The shape BOSL2's half_of() has: a module-body assignment, then a
218
+ // reassignment of the same name inside an `else if` branch.
219
+ runScript("module m(v=1) {\n"
220
+ " v = v + 1;\n"
221
+ " if (false) { echo(unused=v); } else if (true) { v = v * 10; echo(inner=v); }\n"
222
+ " echo(outer=v);\n"
223
+ "}\n"
224
+ "m(2);\n",
225
+ [&](const std::string& msg) { echoed.push_back(msg); });
226
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: inner = 30", "ECHO: outer = 3"}));
227
+ }
228
+
229
+ } // namespace
230
+
231
+ TEST(IfBranchScope, BranchIsItsOwnScopeCompiled) { checkBranchIsItsOwnScope(true); }
232
+ TEST(IfBranchScope, BranchIsItsOwnScopeInterpreted) { checkBranchIsItsOwnScope(false); }
233
+
234
+ TEST(IfBranchScope, DollarVarDoesNotEscapeCompiled) { checkBranchDollarVarDoesNotEscape(true); }
235
+ TEST(IfBranchScope, DollarVarDoesNotEscapeInterpreted) { checkBranchDollarVarDoesNotEscape(false); }
236
+
237
+ TEST(IfBranchScope, DoubleAssignmentWithinOneBranchStillWarnsCompiled) {
238
+ checkDoubleAssignmentWithinOneBranchStillWarns(true);
239
+ }
240
+ TEST(IfBranchScope, DoubleAssignmentWithinOneBranchStillWarnsInterpreted) {
241
+ checkDoubleAssignmentWithinOneBranchStillWarns(false);
242
+ }
243
+
244
+ TEST(IfBranchScope, ShadowingAnOuterNameDoesNotWarnCompiled) { checkShadowingAnOuterNameDoesNotWarn(true); }
245
+ TEST(IfBranchScope, ShadowingAnOuterNameDoesNotWarnInterpreted) { checkShadowingAnOuterNameDoesNotWarn(false); }
246
+
247
+ TEST(IfBranchScope, ElseIfBranchIsItsOwnScopeCompiled) { checkElseIfBranchIsItsOwnScope(true); }
248
+ TEST(IfBranchScope, ElseIfBranchIsItsOwnScopeInterpreted) { checkElseIfBranchIsItsOwnScope(false); }
249
+
250
+ // A builtin operator's braced child block is its own scope too -- the same
251
+ // OpenSCAD rule as an if-branch, on a different code path (the interpreter
252
+ // gets it from resolveCallArgs' child ctx, the compiled path from an
253
+ // Op::OpenLetScope/Op::CloseExprScope bracket inside emitBuiltinWrap /
254
+ // emitCsgWrap). Regression tests for the same class of bug: these writes
255
+ // used to escape into the enclosing scope.
256
+ //
257
+ // User-module child blocks were always correct (their children are
258
+ // evaluated through childrenCallerCtx), and are pinned here so that stays
259
+ // true.
260
+
261
+ namespace {
262
+
263
+ void checkOperatorBlockIsItsOwnScope(bool useVm, const std::string& opCall) {
264
+ ScopedVm vm(useVm);
265
+ std::vector<std::string> echoed;
266
+ runScript("a = 1;\n" + opCall + " { a = 99; cube(1); }\n" + "echo(after=a);\n",
267
+ [&](const std::string& msg) { echoed.push_back(msg); });
268
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: after = 1"})) << "in: " << opCall;
269
+ }
270
+
271
+ void checkOperatorBlockDollarVarDoesNotEscape(bool useVm) {
272
+ ScopedVm vm(useVm);
273
+ std::vector<std::string> echoed;
274
+ runScript("$fn = 8;\n"
275
+ "translate([0,0,0]) { $fn = 64; echo(inside=$fn); cube(1); }\n"
276
+ "echo(after=$fn);\n",
277
+ [&](const std::string& msg) { echoed.push_back(msg); });
278
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: inside = 64", "ECHO: after = 8"}));
279
+ }
280
+
281
+ void checkOperatorBlockAssignmentIsVisibleWithinTheBlock(bool useVm) {
282
+ ScopedVm vm(useVm);
283
+ std::vector<std::string> echoed;
284
+ // Scoping the block must not break the ordinary case: a name assigned
285
+ // in the block is still visible to the rest of it (and, for a CSG
286
+ // operator, across resolveCsg's assignment/geometry two-pass split).
287
+ runScript("union() { s = 3; echo(seen=s); cube(s); }\n",
288
+ [&](const std::string& msg) { echoed.push_back(msg); });
289
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: seen = 3"}));
290
+ }
291
+
292
+ void checkBlockAssignmentCannotFeedBackIntoOperatorArguments(bool useVm) {
293
+ ScopedVm vm(useVm);
294
+ std::vector<std::string> messages;
295
+ // The operator's own arguments are resolved against the OUTER ctx,
296
+ // before the block scope opens -- so this translates by 1, not by 99.
297
+ // (That is why the compiled bracket sits INSIDE Push/PopBuiltinWrap.)
298
+ RunResult r = runScript("d = 1;\n"
299
+ "translate([d,0,0]) { d = 99; cube(1); }\n",
300
+ [&](const std::string& msg) { messages.push_back(msg); });
301
+ (void)messages;
302
+ EXPECT_DOUBLE_EQ(asNum(varValue(r, "d")), 1.0);
303
+ }
304
+
305
+ void checkUserModuleChildBlockIsItsOwnScope(bool useVm) {
306
+ ScopedVm vm(useVm);
307
+ std::vector<std::string> echoed;
308
+ runScript("c = 1;\n"
309
+ "module wrap() { children(); }\n"
310
+ "wrap() { c = 99; cube(1); }\n"
311
+ "echo(after=c);\n",
312
+ [&](const std::string& msg) { echoed.push_back(msg); });
313
+ EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: after = 1"}));
314
+ }
315
+
316
+ } // namespace
317
+
318
+ TEST(OperatorBlockScope, TransformBlockIsItsOwnScopeCompiled) {
319
+ checkOperatorBlockIsItsOwnScope(true, "translate([0,0,0])");
320
+ }
321
+ TEST(OperatorBlockScope, TransformBlockIsItsOwnScopeInterpreted) {
322
+ checkOperatorBlockIsItsOwnScope(false, "translate([0,0,0])");
323
+ }
324
+
325
+ TEST(OperatorBlockScope, CsgBlockIsItsOwnScopeCompiled) { checkOperatorBlockIsItsOwnScope(true, "union()"); }
326
+ TEST(OperatorBlockScope, CsgBlockIsItsOwnScopeInterpreted) { checkOperatorBlockIsItsOwnScope(false, "union()"); }
327
+
328
+ TEST(OperatorBlockScope, ColorBlockIsItsOwnScopeCompiled) { checkOperatorBlockIsItsOwnScope(true, "color(\"red\")"); }
329
+ TEST(OperatorBlockScope, ColorBlockIsItsOwnScopeInterpreted) { checkOperatorBlockIsItsOwnScope(false, "color(\"red\")"); }
330
+
331
+ TEST(OperatorBlockScope, DollarVarDoesNotEscapeCompiled) { checkOperatorBlockDollarVarDoesNotEscape(true); }
332
+ TEST(OperatorBlockScope, DollarVarDoesNotEscapeInterpreted) { checkOperatorBlockDollarVarDoesNotEscape(false); }
333
+
334
+ TEST(OperatorBlockScope, AssignmentVisibleWithinTheBlockCompiled) {
335
+ checkOperatorBlockAssignmentIsVisibleWithinTheBlock(true);
336
+ }
337
+ TEST(OperatorBlockScope, AssignmentVisibleWithinTheBlockInterpreted) {
338
+ checkOperatorBlockAssignmentIsVisibleWithinTheBlock(false);
339
+ }
340
+
341
+ TEST(OperatorBlockScope, BlockAssignmentCannotFeedBackIntoArgumentsCompiled) {
342
+ checkBlockAssignmentCannotFeedBackIntoOperatorArguments(true);
343
+ }
344
+ TEST(OperatorBlockScope, BlockAssignmentCannotFeedBackIntoArgumentsInterpreted) {
345
+ checkBlockAssignmentCannotFeedBackIntoOperatorArguments(false);
346
+ }
347
+
348
+ TEST(OperatorBlockScope, UserModuleChildBlockIsItsOwnScopeCompiled) { checkUserModuleChildBlockIsItsOwnScope(true); }
349
+ TEST(OperatorBlockScope, UserModuleChildBlockIsItsOwnScopeInterpreted) { checkUserModuleChildBlockIsItsOwnScope(false); }
350
+
147
351
  // -- let (statement + expression forms) --------------------------------
148
352
 
149
353
  TEST(LetStatement, BindsVisibleInsideBlockOnly) {