openscad-cpp-evaluator 0.51.3__tar.gz → 0.53.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.53.0}/CLAUDE.md +48 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/bytecode.hpp +19 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/evaluator.hpp +16 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/booleans.cpp +8 -3
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/color.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/control.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/extrude.cpp +12 -4
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/function_builtins.cpp +142 -28
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/roof.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/topology.cpp +18 -6
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/transforms.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/bytecode_compiler.cpp +35 -3
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/bytecode_vm.cpp +9 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/measure_geometry.cpp +3 -1
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/stmt_eval.cpp +30 -2
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_control_flow.cpp +282 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_function_builtins.cpp +68 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_render_expr.cpp +9 -2
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.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.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.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.53.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.53.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.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/uv.lock +0 -0
|
@@ -577,6 +577,17 @@ grep for `ponytail:`.
|
|
|
577
577
|
profiled is BOSL2's **argument type-checking**: 25–45%, millions of
|
|
578
578
|
`is_finite`/`is_num`/`is_nan` calls.
|
|
579
579
|
|
|
580
|
+
- **Degree trigonometry is a direct port of OpenSCAD's `src/utils/degree_trig.cc`**
|
|
581
|
+
(`sinDegrees`/`cosDegrees`/`tanDegrees` and the inverses, `builtins/function_builtins.cpp`), not
|
|
582
|
+
`std::sin(x * pi / 180)`. The structure is load-bearing: exact values at 30/45/60, the >45 range
|
|
583
|
+
computed through the COMPLEMENT function, and OpenSCAD's own literal `M_DEG2RAD` constant rather
|
|
584
|
+
than `pi / 180.0` (which rounds differently). That is what makes `sin(45) == cos(45)` and
|
|
585
|
+
`cos(30) == sqrt(3)/2` hold EXACTLY, and the exactness matters to real libraries: BOSL2's
|
|
586
|
+
`rect(rounding=...)` filters corner intersections with an exact `isect != seg[0]`, so one ULP of
|
|
587
|
+
drift made a shared vertex compare unequal, kept two corner points instead of one, and killed
|
|
588
|
+
`rect()` on its own `assert(len(cornerpt)==1, "Cannot find corner point to anchor")`. The
|
|
589
|
+
inverses also snap to a whole number of degrees when the forward function reproduces the input
|
|
590
|
+
exactly, so `asin(sin(30))` is `30`. Pinned by `DegreeTrig.*` in `tests/test_function_builtins.cpp`.
|
|
580
591
|
- `include/openscad_cpp_evaluator/value.hpp`, `src/value.cpp` — the `Value` variant (OpenSCAD's
|
|
581
592
|
dynamic value type) and its free-function arithmetic/comparison helpers. See the plan's §1 for
|
|
582
593
|
the type-mapping table and why there's no numpy-equivalent library.
|
|
@@ -584,7 +595,33 @@ grep for `ponytail:`.
|
|
|
584
595
|
mutable state threaded through recursive evaluation. `dyn`/`let_`/`dynPositions`/`dynExplicit`
|
|
585
596
|
are `shared_ptr`-wrapped, not plain value members — `withScope()` relies on that aliasing to
|
|
586
597
|
give sibling statements in the same block correct assignment visibility (see the header comment
|
|
587
|
-
before reworking this; it's not an arbitrary choice).
|
|
598
|
+
before reworking this; it's not an arbitrary choice). **An `if`/`else` branch body is its own
|
|
599
|
+
scope**: assignments (including `$`-var assignments) inside it are visible for the rest of the
|
|
600
|
+
branch but must not survive the closing brace, so both engines evaluate a taken branch against
|
|
601
|
+
`childCtx()` — `branchScope()` in `stmt_eval.cpp`, `Op::OpenLetScope`/`Op::CloseExprScope`
|
|
602
|
+
around each branch on the compiled path. `let_`/`dyn` open a non-isolated child (the branch
|
|
603
|
+
reads everything outside, writes only inside) while `dynPositions` opens an **isolated** one, so
|
|
604
|
+
shadowing an outer name is not mistaken for reassigning it — two assignments to the same name
|
|
605
|
+
*within* one branch still warn, because they share that fresh level. Getting this wrong is not
|
|
606
|
+
just a spurious warning: before the fix a branch's writes escaped and silently changed later
|
|
607
|
+
geometry (BOSL2's `half_of()` reassigns `v` inside an `else if`, clobbering the outer `v`).
|
|
608
|
+
**A builtin operator's braced child block (`translate() { a = 1; }`) is its own scope by the
|
|
609
|
+
same rule.** Each builtin resolve function derives one `Evaluator::blockScope()` context for its
|
|
610
|
+
children (`booleans.cpp` derives ONE spanning both of `resolveCsg`'s passes, since the assignment
|
|
611
|
+
pass writes what the geometry pass reads); `emitBuiltinWrap`/`emitCsgWrap` bracket their inline
|
|
612
|
+
children with `Op::OpenLetScope`/`Op::CloseExprScope`. The bracket goes *inside* Push/Pop, because
|
|
613
|
+
`Op::PopBuiltinWrap` re-resolves the call's arguments and those must still see the OUTER ctx — an
|
|
614
|
+
assignment in the block must never feed back into the operator's own arguments.
|
|
615
|
+
**Do not "simplify" this into `resolveCallArgs`** by having it always return a child ctx, however
|
|
616
|
+
tempting — every builtin's child context does come from there, but so does every *user*-module
|
|
617
|
+
call's and every pure argument resolution, and the extra dyn level breaks BOSL2's `$`-var
|
|
618
|
+
propagation outright (`$tags_shown`/`$attach_to`/`$transform`/`$anchor_override` stop resolving).
|
|
619
|
+
That was tried: the whole unit suite passed and 130 of ~390 BOSL2 doc examples failed. Scope the
|
|
620
|
+
CHILDREN, not the call. `for`/`let`/`intersection_for` bodies, and a *user* module's child block (evaluated
|
|
621
|
+
through `childrenCallerCtx`), were already correct. `tests/test_control_flow.cpp`'s
|
|
622
|
+
`IfBranchScope.*` and `OperatorBlockScope.*` pin every case under both engines. One older test
|
|
623
|
+
(`RenderExpr.MeasuredSubtreeUsesItsOwnCoordinates`) had encoded the leak by reading a block's
|
|
624
|
+
variable from outside it, and was corrected.
|
|
588
625
|
- `include/openscad_cpp_evaluator/eval_error.hpp`, `src/eval_error.cpp` — `EvalError` and the
|
|
589
626
|
exact ERROR/TRACE string formatting real OpenSCAD produces. Also the **unexpected-argument
|
|
590
627
|
warnings** (`warnUnexpectedNamedArg`/`warnTooManyPositionalArgs`/`warnUnexpectedArgs`), the port
|
|
@@ -618,6 +655,16 @@ grep for `ponytail:`.
|
|
|
618
655
|
accordingly; read its doc comment before touching it, it's the second-trickiest mechanism in this
|
|
619
656
|
codebase after the CSG tree stack), `evalUserModule`/`evalUserFunction`/`evalFunctionLiteral`,
|
|
620
657
|
and `builtinChildren` (children()/children(N), deferred evaluation).
|
|
658
|
+
**A function literal declared in a MODULE body captures that body's bindings**, its own
|
|
659
|
+
parameters included, and keeps them when it escapes (passed to another module and called there).
|
|
660
|
+
The interpreter gets this for free — it captures `ctx.let_` wholesale. The VM snapshots only the
|
|
661
|
+
names it can resolve, and inside a module body it can resolve NONE: module parameters are bound
|
|
662
|
+
natively into `ctx.let_` rather than slot-addressed, and a module chunk compiles with no
|
|
663
|
+
enclosing level. So such a literal used to be frozen as a captureless constant whose free names
|
|
664
|
+
resolved against whatever ctx CALLED it — silently `undef`, which is how BOSL2's
|
|
665
|
+
`attachable(override=function(anchor) ...)` broke. `ClosureSite::captureDefiningLet` (set
|
|
666
|
+
whenever the literal's body still contains `Op::LoadFree`) makes `Op::MakeClosure` root the
|
|
667
|
+
captured trail at the DEFINING `ctx.let_`. Pinned by `EscapingClosure.*` under both engines.
|
|
621
668
|
**`children(..., separate=true)`** is a BelfrySCAD extension handled *before* any of that, by
|
|
622
669
|
`expandChildStatements`: a block is read with each separating forward replaced by one synthetic
|
|
623
670
|
`children(k)` statement per child it selects (`makeChildrenIndexCall`, owned by
|
|
@@ -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
|
|
@@ -842,6 +845,21 @@ struct CompiledChunk {
|
|
|
842
845
|
struct ClosureSite {
|
|
843
846
|
const oscad::FunctionLiteral* node = nullptr;
|
|
844
847
|
std::vector<UpvalueRef> captures;
|
|
848
|
+
// True when the literal's body still contains Op::LoadFree -- a name
|
|
849
|
+
// the compiler could not resolve to a slot or an upvalue. Such a
|
|
850
|
+
// closure is NOT self-contained: LoadFree resolves against whatever
|
|
851
|
+
// ctx is current when the closure is CALLED, so if it escapes the
|
|
852
|
+
// scope that defined it, those names silently resolve to the
|
|
853
|
+
// caller's bindings (or to undef).
|
|
854
|
+
//
|
|
855
|
+
// That is exactly what happens to a function literal declared in a
|
|
856
|
+
// MODULE body: module parameters are bound natively into ctx.let_
|
|
857
|
+
// rather than slot-addressed, and a module chunk is compiled with no
|
|
858
|
+
// enclosing level, so the compiler can resolve none of them. With
|
|
859
|
+
// this set, Op::MakeClosure roots the captured trail at the DEFINING
|
|
860
|
+
// ctx.let_ -- which is what the interpreter has always done
|
|
861
|
+
// unconditionally (see the FunctionLiteral case in expr_eval.cpp).
|
|
862
|
+
bool captureDefiningLet = false;
|
|
845
863
|
};
|
|
846
864
|
|
|
847
865
|
// One echo() expression form's own argument names, in source order --
|
|
@@ -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
|
|
{openscad_cpp_evaluator-0.51.3 → openscad_cpp_evaluator-0.53.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -55,28 +55,141 @@ Value numList(const std::vector<double>& xs) {
|
|
|
55
55
|
return listOf(std::move(items));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
58
|
+
// Degree trigonometry, a direct port of real OpenSCAD's
|
|
59
|
+
// src/utils/degree_trig.cc. Not `std::sin(x * pi / 180)`: that is off by an
|
|
60
|
+
// ULP from OpenSCAD for most angles, and OpenSCAD's own extra structure --
|
|
61
|
+
// exact values at 30/45/60, and computing the >45 range through the
|
|
62
|
+
// COMPLEMENT function -- is what makes identities like sin(45) == cos(45)
|
|
63
|
+
// and cos(30) == sqrt(3)/2 hold EXACTLY there.
|
|
64
|
+
//
|
|
65
|
+
// That exactness is load-bearing for real libraries, not cosmetic. BOSL2's
|
|
66
|
+
// rect(rounding=...) builds a corner arc and then filters intersections
|
|
67
|
+
// with `isect != seg[0]`, an exact float comparison. One ULP of drift makes
|
|
68
|
+
// the shared vertex of two adjacent segments compare unequal, the filter
|
|
69
|
+
// keeps two corner points instead of one, and rect() dies on its own
|
|
70
|
+
// `assert(len(cornerpt)==1, "Cannot find corner point to anchor")`.
|
|
71
|
+
//
|
|
72
|
+
// The constants are OpenSCAD's own literals, deliberately -- M_DEG2RAD as a
|
|
73
|
+
// single pre-rounded double is not bitwise identical to `pi / 180.0`.
|
|
74
|
+
constexpr double kSqrt3 = 1.73205080756887719318; // sqrt(3)
|
|
75
|
+
constexpr double kSqrt3_4 = 0.86602540378443859659; // sqrt(3/4) == sqrt(3)/2
|
|
76
|
+
constexpr double kSqrt1_3 = 0.57735026918962573106; // sqrt(1/3) == sqrt(3)/3
|
|
77
|
+
constexpr double kSqrt1_2 = 0.70710678118654752440; // sqrt(1/2)
|
|
78
|
+
constexpr double kRad2Deg = 57.2957795130823208767; // 180/PI
|
|
79
|
+
constexpr double kDeg2Rad = 0.017453292519943295769; // PI/180
|
|
80
|
+
|
|
81
|
+
// Assumes a 26+26=52-bit mantissa; beyond it, reduction loses all accuracy
|
|
82
|
+
// and OpenSCAD returns NaN rather than a meaningless answer.
|
|
83
|
+
constexpr double kTrigHugeVal = (1L << 26) * 360.0 * (1L << 26);
|
|
84
|
+
|
|
85
|
+
double degrees(double rad) { return rad * kRad2Deg; }
|
|
86
|
+
double radians(double deg) { return deg * kDeg2Rad; }
|
|
87
|
+
|
|
88
|
+
double sinDegrees(double x) {
|
|
89
|
+
// Positive tests, so Inf/NaN fall through to the domain check.
|
|
90
|
+
if (x < 360.0 && x >= 0.0) {
|
|
91
|
+
// already reduced
|
|
92
|
+
} else if (x < kTrigHugeVal && x > -kTrigHugeVal) {
|
|
93
|
+
x -= 360.0 * std::floor(x / 360.0);
|
|
94
|
+
} else {
|
|
95
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
96
|
+
}
|
|
97
|
+
bool oppose = x >= 180.0;
|
|
98
|
+
if (oppose) x -= 180.0;
|
|
99
|
+
if (x > 90.0) x = 180.0 - x;
|
|
100
|
+
if (x < 45.0) {
|
|
101
|
+
x = (x == 30.0) ? 0.5 : std::sin(radians(x));
|
|
102
|
+
} else if (x == 45.0) {
|
|
103
|
+
x = kSqrt1_2;
|
|
104
|
+
} else if (x == 60.0) {
|
|
105
|
+
x = kSqrt3_4;
|
|
106
|
+
} else { // Inf/NaN would fall here
|
|
107
|
+
x = std::cos(radians(90.0 - x));
|
|
108
|
+
}
|
|
109
|
+
return oppose ? -x : x;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
double cosDegrees(double x) {
|
|
113
|
+
if (x < 360.0 && x >= 0.0) {
|
|
114
|
+
// already reduced
|
|
115
|
+
} else if (x < kTrigHugeVal && x > -kTrigHugeVal) {
|
|
116
|
+
x -= 360.0 * std::floor(x / 360.0);
|
|
117
|
+
} else {
|
|
118
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
119
|
+
}
|
|
120
|
+
bool oppose = x >= 180.0;
|
|
121
|
+
if (oppose) x -= 180.0;
|
|
122
|
+
if (x > 90.0) {
|
|
123
|
+
x = 180.0 - x;
|
|
124
|
+
oppose = !oppose;
|
|
125
|
+
}
|
|
126
|
+
if (x > 45.0) {
|
|
127
|
+
x = (x == 60.0) ? 0.5 : std::sin(radians(90.0 - x));
|
|
128
|
+
} else if (x == 45.0) {
|
|
129
|
+
x = kSqrt1_2;
|
|
130
|
+
} else if (x == 30.0) {
|
|
131
|
+
x = kSqrt3_4;
|
|
132
|
+
} else { // Inf/NaN would fall here
|
|
133
|
+
x = std::cos(radians(x));
|
|
134
|
+
}
|
|
135
|
+
return oppose ? -x : x;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
double tanDegrees(double x) {
|
|
139
|
+
const double cycles = std::floor(x / 180.0);
|
|
140
|
+
if (x < 180.0 && x >= 0.0) {
|
|
141
|
+
// already reduced
|
|
142
|
+
} else if (x < kTrigHugeVal && x > -kTrigHugeVal) {
|
|
143
|
+
x -= 180.0 * cycles;
|
|
144
|
+
} else {
|
|
145
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
146
|
+
}
|
|
147
|
+
const bool evenCycle = std::fmod(cycles, 2.0) == 0.0;
|
|
148
|
+
bool oppose = x > 90.0;
|
|
149
|
+
if (oppose) x = 180.0 - x;
|
|
150
|
+
if (x == 0.0) {
|
|
151
|
+
x = evenCycle ? 0.0 : -0.0;
|
|
152
|
+
} else if (x == 30.0) {
|
|
153
|
+
x = kSqrt1_3;
|
|
154
|
+
} else if (x == 45.0) {
|
|
155
|
+
x = 1.0;
|
|
156
|
+
} else if (x == 60.0) {
|
|
157
|
+
x = kSqrt3;
|
|
158
|
+
} else if (x == 90.0) {
|
|
159
|
+
x = evenCycle ? std::numeric_limits<double>::infinity()
|
|
160
|
+
: -std::numeric_limits<double>::infinity();
|
|
161
|
+
} else {
|
|
162
|
+
x = std::tan(radians(x));
|
|
78
163
|
}
|
|
79
|
-
return
|
|
164
|
+
return oppose ? -x : x;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// The inverses snap to a whole number of degrees whenever the forward
|
|
168
|
+
// function reproduces the input exactly, so asin(sin(30)) is 30 and not
|
|
169
|
+
// 29.999999999999996.
|
|
170
|
+
double asinDegrees(double x) {
|
|
171
|
+
const double degs = degrees(std::asin(x));
|
|
172
|
+
const double whole = std::round(degs);
|
|
173
|
+
return sinDegrees(whole) == x ? whole : degs;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
double acosDegrees(double x) {
|
|
177
|
+
const double degs = degrees(std::acos(x));
|
|
178
|
+
const double whole = std::round(degs);
|
|
179
|
+
return cosDegrees(whole) == x ? whole : degs;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
double atanDegrees(double x) {
|
|
183
|
+
const double degs = degrees(std::atan(x));
|
|
184
|
+
const double whole = std::round(degs);
|
|
185
|
+
return tanDegrees(whole) == x ? whole : degs;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// atan2 snaps on a tolerance rather than a round-trip: OpenSCAD's own rule.
|
|
189
|
+
double atan2Degrees(double y, double x) {
|
|
190
|
+
const double degs = degrees(std::atan2(y, x));
|
|
191
|
+
const double whole = std::round(degs);
|
|
192
|
+
return std::fabs(degs - whole) < 3.0E-14 ? whole : degs;
|
|
80
193
|
}
|
|
81
194
|
|
|
82
195
|
// UTF-8 encode/decode for chr()/ord() -- OpenSCAD strings are unicode text,
|
|
@@ -1183,20 +1296,21 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
1183
1296
|
return Value{x < 0 ? std::numeric_limits<double>::quiet_NaN() : std::log10(x)};
|
|
1184
1297
|
}
|
|
1185
1298
|
case BuiltinFnId::Exp: return Value{std::exp(toDoubleLenient(getArg(args, 0, "x", Value{})))};
|
|
1186
|
-
case BuiltinFnId::Sin: return Value{
|
|
1187
|
-
case BuiltinFnId::Cos: return Value{
|
|
1188
|
-
case BuiltinFnId::Tan: return Value{
|
|
1299
|
+
case BuiltinFnId::Sin: return Value{sinDegrees(toDoubleLenient(getArg(args, 0, "x", Value{})))};
|
|
1300
|
+
case BuiltinFnId::Cos: return Value{cosDegrees(toDoubleLenient(getArg(args, 0, "x", Value{})))};
|
|
1301
|
+
case BuiltinFnId::Tan: return Value{tanDegrees(toDoubleLenient(getArg(args, 0, "x", Value{})))};
|
|
1189
1302
|
case BuiltinFnId::Asin: {
|
|
1190
1303
|
const double x = toDoubleLenient(getArg(args, 0, "x", Value{}));
|
|
1191
|
-
return Value{std::fabs(x) > 1 ? std::numeric_limits<double>::quiet_NaN() :
|
|
1304
|
+
return Value{std::fabs(x) > 1 ? std::numeric_limits<double>::quiet_NaN() : asinDegrees(x)};
|
|
1192
1305
|
}
|
|
1193
1306
|
case BuiltinFnId::Acos: {
|
|
1194
1307
|
const double x = toDoubleLenient(getArg(args, 0, "x", Value{}));
|
|
1195
|
-
return Value{std::fabs(x) > 1 ? std::numeric_limits<double>::quiet_NaN() :
|
|
1308
|
+
return Value{std::fabs(x) > 1 ? std::numeric_limits<double>::quiet_NaN() : acosDegrees(x)};
|
|
1196
1309
|
}
|
|
1197
|
-
case BuiltinFnId::Atan: return Value{
|
|
1310
|
+
case BuiltinFnId::Atan: return Value{atanDegrees(toDoubleLenient(getArg(args, 0, "x", Value{})))};
|
|
1198
1311
|
case BuiltinFnId::Atan2:
|
|
1199
|
-
return Value{
|
|
1312
|
+
return Value{atan2Degrees(toDoubleLenient(getArg(args, 0, "y", Value{})),
|
|
1313
|
+
toDoubleLenient(getArg(args, 1, "x", Value{})))};
|
|
1200
1314
|
case BuiltinFnId::Max: return builtinMinMax(args, true);
|
|
1201
1315
|
case BuiltinFnId::Min: return builtinMinMax(args, false);
|
|
1202
1316
|
case BuiltinFnId::Pow: return builtinPow(toDoubleLenient(getArg(args, 0, "x", Value{})), toDoubleLenient(getArg(args, 1, "y", Value{})));
|
|
@@ -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
|
|
|
@@ -447,10 +447,12 @@ public:
|
|
|
447
447
|
// `ctx.let_`, which callCtxFor roots at this exact closure's
|
|
448
448
|
// own capturedLet snapshot whenever it's invoked -- the same
|
|
449
449
|
// snapshot `effectiveCaptures` describes below.
|
|
450
|
-
|
|
450
|
+
// Must be read BEFORE literalChunk is moved from below.
|
|
451
|
+
const bool needsDefiningLet = containsLoadFree(literalChunk);
|
|
452
|
+
if (effectiveCaptures.empty() || !needsDefiningLet) {
|
|
451
453
|
chunk_.nestedLiterals.emplace_back(&n, std::move(literalChunk));
|
|
452
454
|
}
|
|
453
|
-
if (effectiveCaptures.empty()) {
|
|
455
|
+
if (effectiveCaptures.empty() && !needsDefiningLet) {
|
|
454
456
|
// Closes over nothing at all, even transitively -- a
|
|
455
457
|
// single frozen constant works for every invocation, no
|
|
456
458
|
// per-call snapshot needed.
|
|
@@ -461,7 +463,8 @@ public:
|
|
|
461
463
|
}
|
|
462
464
|
// Escaping-closure support (Op::MakeClosure, bytecode.hpp).
|
|
463
465
|
int siteIdx = static_cast<int>(chunk_.closureSites.size());
|
|
464
|
-
chunk_.closureSites.push_back(
|
|
466
|
+
chunk_.closureSites.push_back(
|
|
467
|
+
CompiledChunk::ClosureSite{&n, std::move(effectiveCaptures), needsDefiningLet});
|
|
465
468
|
out.push_back({Op::MakeClosure, siteIdx, 0, nullptr});
|
|
466
469
|
return;
|
|
467
470
|
}
|
|
@@ -1289,7 +1292,15 @@ public:
|
|
|
1289
1292
|
chunk_.builtinWrapSites.push_back(std::move(site));
|
|
1290
1293
|
const int idx = static_cast<int>(chunk_.builtinWrapSites.size()) - 1;
|
|
1291
1294
|
out.push_back({Op::PushBuiltinWrap, idx, 0, &wrapperNode.position()});
|
|
1295
|
+
// The child block is its own scope -- same rule as an if-branch, and
|
|
1296
|
+
// the interpreter gets it from resolveCallArgs' child ctx (see
|
|
1297
|
+
// call_args.cpp). Opened INSIDE the Push/Pop bracket so that
|
|
1298
|
+
// Op::PopBuiltinWrap's own resolveCallArgs call still sees the
|
|
1299
|
+
// OUTER ctx: an assignment in the block must not feed back into the
|
|
1300
|
+
// operator's own arguments.
|
|
1301
|
+
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1292
1302
|
compileStatementList(children, out);
|
|
1303
|
+
out.push_back({Op::CloseExprScope, 0, 0, nullptr});
|
|
1293
1304
|
out.push_back({Op::PopBuiltinWrap, idx, 0, &wrapperNode.position()});
|
|
1294
1305
|
}
|
|
1295
1306
|
|
|
@@ -1312,6 +1323,10 @@ public:
|
|
|
1312
1323
|
chunk_.csgWrapSites.push_back(std::move(site));
|
|
1313
1324
|
const int idx = static_cast<int>(chunk_.csgWrapSites.size()) - 1;
|
|
1314
1325
|
out.push_back({Op::PushCsgWrap, idx, 0, &call.position()});
|
|
1326
|
+
// One scope around BOTH passes -- see emitBuiltinWrap's own note.
|
|
1327
|
+
// The assignment pass must write into the same block scope the
|
|
1328
|
+
// geometry pass then reads from, so it cannot be bracketed per-pass.
|
|
1329
|
+
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1315
1330
|
|
|
1316
1331
|
std::vector<const oscad::ASTNode*> assignNodes;
|
|
1317
1332
|
std::vector<const oscad::ASTNode*> geoNodes;
|
|
@@ -1335,6 +1350,7 @@ public:
|
|
|
1335
1350
|
compileStatementList(std::vector<const oscad::ASTNode*>{geoNode}, out);
|
|
1336
1351
|
out.push_back({Op::CsgGroupEnd, 0, 0, nullptr});
|
|
1337
1352
|
}
|
|
1353
|
+
out.push_back({Op::CloseExprScope, 0, 0, nullptr});
|
|
1338
1354
|
out.push_back({Op::PopCsgWrap, idx, 0, &call.position()});
|
|
1339
1355
|
}
|
|
1340
1356
|
|
|
@@ -1688,13 +1704,25 @@ public:
|
|
|
1688
1704
|
emitBuiltinWrap(CompiledChunk::BuiltinWrapSite::Kind::Modifier, "show_only", n, {n.child.get()}, out);
|
|
1689
1705
|
return;
|
|
1690
1706
|
}
|
|
1707
|
+
// Both if-forms bracket the taken branch in Op::OpenLetScope /
|
|
1708
|
+
// Op::CloseExprScope. An if-branch body is its own scope in
|
|
1709
|
+
// OpenSCAD -- assignments inside must not survive the closing
|
|
1710
|
+
// brace -- and the child ctx a let-BLOCK gets is exactly the
|
|
1711
|
+
// one a branch needs, so the same opcode serves both (see
|
|
1712
|
+
// branchScope, stmt_eval.cpp, for the interpreter's identical
|
|
1713
|
+
// rule and the full reasoning). The scope is opened INSIDE the
|
|
1714
|
+
// conditional jump's target range, so a branch that is not
|
|
1715
|
+
// taken never opens one, and each branch's own Close is the
|
|
1716
|
+
// last thing before the jump past its sibling.
|
|
1691
1717
|
case NodeKind::ModularIf: {
|
|
1692
1718
|
auto& n = static_cast<const oscad::ModularIf&>(stmt);
|
|
1693
1719
|
const int jumpFalseIdx = static_cast<int>(out.size());
|
|
1694
1720
|
out.push_back({Op::NativeCondJumpIfFalse, internNativeExpr(n.condition.get()), 0, &n.condition->position()});
|
|
1695
1721
|
const oscad::ASTNode* marker = n.trueBranch.empty() ? &stmt : n.trueBranch.front().get();
|
|
1696
1722
|
out.push_back({Op::NativeCheckDebugExprLevel, internNativeStatement(marker), 0, nullptr});
|
|
1723
|
+
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1697
1724
|
compileStatementList(n.trueBranch, out);
|
|
1725
|
+
out.push_back({Op::CloseExprScope, 0, 0, nullptr});
|
|
1698
1726
|
out[static_cast<size_t>(jumpFalseIdx)].b = static_cast<int>(out.size());
|
|
1699
1727
|
return;
|
|
1700
1728
|
}
|
|
@@ -1704,13 +1732,17 @@ public:
|
|
|
1704
1732
|
out.push_back({Op::NativeCondJumpIfFalse, internNativeExpr(n.condition.get()), 0, &n.condition->position()});
|
|
1705
1733
|
const oscad::ASTNode* trueMarker = n.trueBranch.empty() ? &stmt : n.trueBranch.front().get();
|
|
1706
1734
|
out.push_back({Op::NativeCheckDebugExprLevel, internNativeStatement(trueMarker), 0, nullptr});
|
|
1735
|
+
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1707
1736
|
compileStatementList(n.trueBranch, out);
|
|
1737
|
+
out.push_back({Op::CloseExprScope, 0, 0, nullptr});
|
|
1708
1738
|
const int jumpEndIdx = static_cast<int>(out.size());
|
|
1709
1739
|
out.push_back({Op::Jump, 0, 0, nullptr});
|
|
1710
1740
|
out[static_cast<size_t>(jumpFalseIdx)].b = static_cast<int>(out.size());
|
|
1711
1741
|
const oscad::ASTNode* falseMarker = n.falseBranch.empty() ? &stmt : n.falseBranch.front().get();
|
|
1712
1742
|
out.push_back({Op::NativeCheckDebugExprLevel, internNativeStatement(falseMarker), 0, nullptr});
|
|
1743
|
+
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1713
1744
|
compileStatementList(n.falseBranch, out);
|
|
1745
|
+
out.push_back({Op::CloseExprScope, 0, 0, nullptr});
|
|
1714
1746
|
out[static_cast<size_t>(jumpEndIdx)].a = static_cast<int>(out.size());
|
|
1715
1747
|
return;
|
|
1716
1748
|
}
|