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.
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/CLAUDE.md +27 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bytecode.hpp +4 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/evaluator.hpp +16 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/booleans.cpp +8 -3
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/color.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/control.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/extrude.cpp +12 -4
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/roof.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/topology.cpp +18 -6
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/transforms.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/bytecode_compiler.cpp +29 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/measure_geometry.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/stmt_eval.cpp +30 -2
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_control_flow.cpp +204 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_render_expr.cpp +9 -2
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {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
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {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
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.52.0}/tools/cli/main.cpp +0 -0
- {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
|
|
@@ -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
|
|
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
|
|
@@ -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,
|
|
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,
|
|
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},
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|