openscad-cpp-evaluator 0.52.0__tar.gz → 0.54.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.52.0 → openscad_cpp_evaluator-0.54.0}/CLAUDE.md +21 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/bytecode.hpp +15 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/function_builtins.cpp +142 -28
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/primitives_3d.cpp +8 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/bytecode_compiler.cpp +26 -27
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/bytecode_vm.cpp +9 -1
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/stmt_eval.cpp +13 -13
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_bytecode_compiler.cpp +11 -5
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_control_flow.cpp +131 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_function_builtins.cpp +68 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_primitives.cpp +26 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.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.
|
|
@@ -644,6 +655,16 @@ grep for `ponytail:`.
|
|
|
644
655
|
accordingly; read its doc comment before touching it, it's the second-trickiest mechanism in this
|
|
645
656
|
codebase after the CSG tree stack), `evalUserModule`/`evalUserFunction`/`evalFunctionLiteral`,
|
|
646
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.
|
|
647
668
|
**`children(..., separate=true)`** is a BelfrySCAD extension handled *before* any of that, by
|
|
648
669
|
`expandChildStatements`: a block is read with each separating forward replaced by one synthetic
|
|
649
670
|
`children(k)` statement per child it selects (`makeChildrenIndexCall`, owned by
|
|
@@ -845,6 +845,21 @@ struct CompiledChunk {
|
|
|
845
845
|
struct ClosureSite {
|
|
846
846
|
const oscad::FunctionLiteral* node = nullptr;
|
|
847
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;
|
|
848
863
|
};
|
|
849
864
|
|
|
850
865
|
// One echo() expression form's own argument names, in source order --
|
{openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.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{})));
|
{openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/src/builtins/primitives_3d.cpp
RENAMED
|
@@ -781,6 +781,14 @@ std::vector<ColoredBody> generatePolyhedron(Evaluator& ev, const CSGParams& para
|
|
|
781
781
|
for (const Value& t : std::get<ListPtr>(params.at("tris"))->items) {
|
|
782
782
|
mesh.triVerts.push_back(static_cast<uint64_t>(std::get<double>(t)));
|
|
783
783
|
}
|
|
784
|
+
// No faces at all is empty geometry, not a failure. BOSL2's debug_vnf()
|
|
785
|
+
// passes [verts, []] to draw vertex labels with no surface, and real
|
|
786
|
+
// OpenSCAD accepts `polyhedron(points=..., faces=[])` silently. Checked
|
|
787
|
+
// before the status branches below, which would otherwise report the
|
|
788
|
+
// empty mesh as NotManifold and discard it with a warning.
|
|
789
|
+
if (mesh.triVerts.empty()) {
|
|
790
|
+
return {ev.tagGenerated(manifold::Manifold(), node, params.at("color"))};
|
|
791
|
+
}
|
|
784
792
|
manifold::Manifold body(mesh);
|
|
785
793
|
if (isDrawableFailure(body, mesh)) {
|
|
786
794
|
// An open surface -- faces that don't close the solid. Manifold
|
|
@@ -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
|
}
|
|
@@ -1561,35 +1564,31 @@ public:
|
|
|
1561
1564
|
return;
|
|
1562
1565
|
}
|
|
1563
1566
|
case NodeKind::ModularLet: {
|
|
1564
|
-
// Mirrors Evaluator::evalLetBlock
|
|
1565
|
-
//
|
|
1566
|
-
//
|
|
1567
|
-
//
|
|
1568
|
-
//
|
|
1569
|
-
//
|
|
1570
|
-
//
|
|
1571
|
-
//
|
|
1572
|
-
//
|
|
1573
|
-
//
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1577
|
-
//
|
|
1578
|
-
//
|
|
1579
|
-
//
|
|
1580
|
-
//
|
|
1567
|
+
// Mirrors Evaluator::evalLetBlock (stmt_eval.cpp): the
|
|
1568
|
+
// child scope opens FIRST, then each assignment's RHS is
|
|
1569
|
+
// compiled and stored in source order, so a later binding
|
|
1570
|
+
// sees an earlier one -- `let(a=1, b=a+1) ...` binds b to
|
|
1571
|
+
// 2, exactly as the let-EXPRESSION form does.
|
|
1572
|
+
//
|
|
1573
|
+
// This used to compile every RHS before Op::OpenLetScope
|
|
1574
|
+
// and then store them back-to-front, deliberately denying
|
|
1575
|
+
// that visibility. Real OpenSCAD grants it, and BOSL2
|
|
1576
|
+
// depends on it (isosurface.scad chains five bindings where
|
|
1577
|
+
// each uses the previous).
|
|
1578
|
+
//
|
|
1579
|
+
// No outer statement-level check for the let-block node
|
|
1580
|
+
// itself (matches evalChildren's own ModularLet exclusion)
|
|
1581
|
+
// -- each assignment gets its own, exactly like
|
|
1582
|
+
// evalLetBlock's own per-assignment checkDebug. The body
|
|
1583
|
+
// (n.children) compiles inline, recursively, against the
|
|
1584
|
+
// now-current child ctx.
|
|
1581
1585
|
auto& n = static_cast<const oscad::ModularLet&>(stmt);
|
|
1582
1586
|
CompileScope exprScope;
|
|
1587
|
+
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1583
1588
|
for (const auto& assign : n.assignments) {
|
|
1584
1589
|
out.push_back({Op::CheckDebugStatement, internNativeStatement(assign.get()), 0, nullptr});
|
|
1585
1590
|
compileIsolatedExpr(*assign->expr, out, exprScope);
|
|
1586
|
-
|
|
1587
|
-
out.push_back({Op::OpenLetScope, 0, 0, nullptr});
|
|
1588
|
-
// Values pop in REVERSE push order (LIFO) -- iterate the
|
|
1589
|
-
// assignment list backwards so each Op::StoreLetVar is
|
|
1590
|
-
// paired with the value that was actually pushed for IT.
|
|
1591
|
-
for (auto it = n.assignments.rbegin(); it != n.assignments.rend(); ++it) {
|
|
1592
|
-
out.push_back({Op::StoreLetVar, internName((*it)->name->name), 0, &(*it)->position()});
|
|
1591
|
+
out.push_back({Op::StoreLetVar, internName(assign->name->name), 0, &assign->position()});
|
|
1593
1592
|
}
|
|
1594
1593
|
compileStatementList(n.children, out);
|
|
1595
1594
|
out.push_back({Op::CloseExprScope, 0, 0, nullptr});
|
|
@@ -599,7 +599,15 @@ Value driveVm(Evaluator& ev, size_t floor) {
|
|
|
599
599
|
}
|
|
600
600
|
case Op::MakeClosure: {
|
|
601
601
|
const CompiledChunk::ClosureSite& site = f.chunk->closureSites[static_cast<size_t>(ins.a)];
|
|
602
|
-
|
|
602
|
+
// A child of the DEFINING ctx.let_ when the body still
|
|
603
|
+
// has unresolved names (see ClosureSite::
|
|
604
|
+
// captureDefiningLet), so they resolve where the closure
|
|
605
|
+
// was written rather than wherever it is later called.
|
|
606
|
+
// openChild, not ctx.let_ itself: the explicit captures
|
|
607
|
+
// below are layered on top and must not leak back into
|
|
608
|
+
// the defining scope.
|
|
609
|
+
auto capturedTrail = site.captureDefiningLet ? ctx.let_->openChild(/*isolate=*/false)
|
|
610
|
+
: TrailView<Value>::makeRoot();
|
|
603
611
|
std::vector<const std::string*> selfNames;
|
|
604
612
|
for (const auto& cap : site.captures) {
|
|
605
613
|
if (cap.isSelfReference) {
|
|
@@ -155,20 +155,20 @@ void Evaluator::evalFor(const oscad::ModularFor& node, EvalContext& ctx) {
|
|
|
155
155
|
|
|
156
156
|
void Evaluator::evalLetBlock(const oscad::ModularLet& node, EvalContext& ctx) {
|
|
157
157
|
EvalContext childCtx = ctx.childCtx(nullptr, std::nullopt, ctx.childrenNodes, ctx.childrenCallerCtx);
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
158
|
+
// Each RHS is evaluated against the GROWING childCtx, so a later
|
|
159
|
+
// binding sees an earlier one -- `let(a=1, b=a+1) ...` binds b to 2.
|
|
160
|
+
// The statement form behaves exactly like the expression form here.
|
|
161
|
+
//
|
|
162
|
+
// This used to evaluate every RHS against the original `ctx` instead,
|
|
163
|
+
// described in a comment as a deliberate port of the reference's
|
|
164
|
+
// _eval_let_block. That reference was this project's own earlier
|
|
165
|
+
// implementation, which had the same bug; real OpenSCAD 2026.02.01
|
|
166
|
+
// echoes 2, and BOSL2 depends on it heavily -- isosurface.scad chains
|
|
167
|
+
// five bindings where each uses the previous, and rounding.scad's
|
|
168
|
+
// join_prism examples do the same.
|
|
167
169
|
for (const auto& assign : node.assignments) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
checkDebug(*assign, ctx);
|
|
171
|
-
Value v = evalExprMaybeCompiled(*assign->expr, ctx);
|
|
170
|
+
checkDebug(*assign, childCtx);
|
|
171
|
+
Value v = evalExprMaybeCompiled(*assign->expr, childCtx);
|
|
172
172
|
const std::string& name = assign->name->name;
|
|
173
173
|
if (!name.empty() && name[0] == '$') {
|
|
174
174
|
childCtx.dyn->set(name, v);
|
{openscad_cpp_evaluator-0.52.0 → openscad_cpp_evaluator-0.54.0}/tests/test_bytecode_compiler.cpp
RENAMED
|
@@ -1889,15 +1889,21 @@ TEST(ModuleBodyCompiles, AssertStatementCompiledFormFailsWithNamedMessage) {
|
|
|
1889
1889
|
}
|
|
1890
1890
|
}
|
|
1891
1891
|
|
|
1892
|
-
TEST(ModuleBodyCompiles,
|
|
1892
|
+
TEST(ModuleBodyCompiles, LetBlockStatementSeesItsOwnEarlierSiblingAssignment) {
|
|
1893
1893
|
ScopedVm vm(true);
|
|
1894
|
-
//
|
|
1895
|
-
//
|
|
1896
|
-
//
|
|
1894
|
+
// b's RHS sees THIS let-block's own a=2, not the outer a=1, so b is 3.
|
|
1895
|
+
// The statement form has the same sequential visibility the expression
|
|
1896
|
+
// form does, and the binding still does not escape the block.
|
|
1897
|
+
//
|
|
1898
|
+
// This test previously asserted "ECHO: 2, 2" -- b seeing the outer a --
|
|
1899
|
+
// and called that a documented divergence. It was not: real OpenSCAD
|
|
1900
|
+
// 2026.02.01 echoes "2, 3" then "1", which is what both engines now
|
|
1901
|
+
// produce. BOSL2 depends on the sequential form (isosurface.scad chains
|
|
1902
|
+
// five bindings where each uses the previous).
|
|
1897
1903
|
EXPECT_EQ(runCapturingEcho("a = 1;\n"
|
|
1898
1904
|
"let (a = 2, b = a + 1) { echo(a, b); }\n"
|
|
1899
1905
|
"echo(a);\n"),
|
|
1900
|
-
"ECHO: 2,
|
|
1906
|
+
"ECHO: 2, 3\nECHO: 1");
|
|
1901
1907
|
}
|
|
1902
1908
|
|
|
1903
1909
|
TEST(ModuleBodyCompiles, LetBlockStatementIsolatesDollarVarOverrideFromLaterStatements) {
|
|
@@ -348,8 +348,139 @@ TEST(OperatorBlockScope, BlockAssignmentCannotFeedBackIntoArgumentsInterpreted)
|
|
|
348
348
|
TEST(OperatorBlockScope, UserModuleChildBlockIsItsOwnScopeCompiled) { checkUserModuleChildBlockIsItsOwnScope(true); }
|
|
349
349
|
TEST(OperatorBlockScope, UserModuleChildBlockIsItsOwnScopeInterpreted) { checkUserModuleChildBlockIsItsOwnScope(false); }
|
|
350
350
|
|
|
351
|
+
// A function literal declared in a MODULE body captures that body's
|
|
352
|
+
// bindings, including the module's own parameters, and keeps them when it
|
|
353
|
+
// escapes -- passed to another module and called there.
|
|
354
|
+
//
|
|
355
|
+
// The interpreter always did this (it captures ctx.let_ wholesale). The VM
|
|
356
|
+
// tries to snapshot only the names it can resolve, and inside a module body
|
|
357
|
+
// it can resolve none: module parameters are bound natively into ctx.let_
|
|
358
|
+
// rather than slot-addressed, and a module chunk compiles with no enclosing
|
|
359
|
+
// level. So the literal was frozen as a captureless constant and its free
|
|
360
|
+
// names resolved against whatever ctx CALLED it -- silently undef.
|
|
361
|
+
//
|
|
362
|
+
// Verified against real OpenSCAD 2026.02.01: every case below echoes 20.
|
|
363
|
+
|
|
364
|
+
namespace {
|
|
365
|
+
|
|
366
|
+
void checkEscapingClosureKeepsItsDefiningScope(bool useVm, const std::string& body) {
|
|
367
|
+
ScopedVm vm(useVm);
|
|
368
|
+
std::vector<std::string> echoed;
|
|
369
|
+
runScript(body, [&](const std::string& msg) { echoed.push_back(msg); });
|
|
370
|
+
EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: r = 20"})) << "in: " << body;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
} // namespace
|
|
374
|
+
|
|
375
|
+
TEST(EscapingClosure, CapturesModuleParameterCompiled) {
|
|
376
|
+
checkEscapingClosureKeepsItsDefiningScope(true,
|
|
377
|
+
"module callee(fn) { echo(r = fn(2)); }\n"
|
|
378
|
+
"module caller(s=100) { f = function (x) s / 8 * x; callee(f); }\n"
|
|
379
|
+
"caller(80);\n");
|
|
380
|
+
}
|
|
381
|
+
TEST(EscapingClosure, CapturesModuleParameterInterpreted) {
|
|
382
|
+
checkEscapingClosureKeepsItsDefiningScope(false,
|
|
383
|
+
"module callee(fn) { echo(r = fn(2)); }\n"
|
|
384
|
+
"module caller(s=100) { f = function (x) s / 8 * x; callee(f); }\n"
|
|
385
|
+
"caller(80);\n");
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// BOSL2's attachable(override=...) shape: the closure travels inside a list.
|
|
389
|
+
TEST(EscapingClosure, SurvivesInsideAListCompiled) {
|
|
390
|
+
checkEscapingClosureKeepsItsDefiningScope(true,
|
|
391
|
+
"module callee(spec) { echo(r = spec[0](2)); }\n"
|
|
392
|
+
"module caller(s=100) { f = function (x) s / 8 * x; callee([f]); }\n"
|
|
393
|
+
"caller(80);\n");
|
|
394
|
+
}
|
|
395
|
+
TEST(EscapingClosure, SurvivesInsideAListInterpreted) {
|
|
396
|
+
checkEscapingClosureKeepsItsDefiningScope(false,
|
|
397
|
+
"module callee(spec) { echo(r = spec[0](2)); }\n"
|
|
398
|
+
"module caller(s=100) { f = function (x) s / 8 * x; callee([f]); }\n"
|
|
399
|
+
"caller(80);\n");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// The callee has its OWN `s`. The closure must still see the one it was
|
|
403
|
+
// written next to -- this is what proves it captures rather than merely
|
|
404
|
+
// resolving late.
|
|
405
|
+
TEST(EscapingClosure, IsNotCapturedByTheCallersOwnBindingCompiled) {
|
|
406
|
+
checkEscapingClosureKeepsItsDefiningScope(true,
|
|
407
|
+
"module callee(fn, s=999) { echo(r = fn(2)); }\n"
|
|
408
|
+
"module caller(s=100) { f = function (x) s / 8 * x; callee(f); }\n"
|
|
409
|
+
"caller(80);\n");
|
|
410
|
+
}
|
|
411
|
+
TEST(EscapingClosure, IsNotCapturedByTheCallersOwnBindingInterpreted) {
|
|
412
|
+
checkEscapingClosureKeepsItsDefiningScope(false,
|
|
413
|
+
"module callee(fn, s=999) { echo(r = fn(2)); }\n"
|
|
414
|
+
"module caller(s=100) { f = function (x) s / 8 * x; callee(f); }\n"
|
|
415
|
+
"caller(80);\n");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// A literal closing over nothing keeps the cheap frozen-constant path.
|
|
419
|
+
TEST(EscapingClosure, CapturelessLiteralStillWorksCompiled) {
|
|
420
|
+
ScopedVm vm(true);
|
|
421
|
+
std::vector<std::string> echoed;
|
|
422
|
+
runScript("module callee(fn) { echo(r = fn(4)); }\n"
|
|
423
|
+
"module caller() { callee(function (x) x * 5); }\n"
|
|
424
|
+
"caller();\n",
|
|
425
|
+
[&](const std::string& msg) { echoed.push_back(msg); });
|
|
426
|
+
EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: r = 20"}));
|
|
427
|
+
}
|
|
428
|
+
|
|
351
429
|
// -- let (statement + expression forms) --------------------------------
|
|
352
430
|
|
|
431
|
+
// The let STATEMENT form has the same sequential visibility as the
|
|
432
|
+
// expression form: a later binding sees an earlier one. Verified against
|
|
433
|
+
// real OpenSCAD 2026.02.01, which echoes 2 for both forms below.
|
|
434
|
+
//
|
|
435
|
+
// This was wrong for a long time, described in-source as a deliberate port
|
|
436
|
+
// of "the reference's _eval_let_block" -- that reference being this
|
|
437
|
+
// project's own earlier implementation, which had the same bug. BOSL2 leans
|
|
438
|
+
// on it: isosurface.scad chains five bindings where each uses the previous,
|
|
439
|
+
// and rounding.scad's join_prism examples do too.
|
|
440
|
+
|
|
441
|
+
namespace {
|
|
442
|
+
|
|
443
|
+
void checkLetStatementIsSequential(bool useVm) {
|
|
444
|
+
ScopedVm vm(useVm);
|
|
445
|
+
std::vector<std::string> echoed;
|
|
446
|
+
runScript("let (a = 1, b = a + 1) echo(b = b);\n",
|
|
447
|
+
[&](const std::string& msg) { echoed.push_back(msg); });
|
|
448
|
+
EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: b = 2"}));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
void checkLetStatementShadowsWithoutEscaping(bool useVm) {
|
|
452
|
+
ScopedVm vm(useVm);
|
|
453
|
+
std::vector<std::string> echoed;
|
|
454
|
+
// The inner a=2 is what b sees, and it does not survive the block.
|
|
455
|
+
runScript("a = 1;\n"
|
|
456
|
+
"let (a = 2, b = a + 1) echo(inside_a = a, inside_b = b);\n"
|
|
457
|
+
"echo(after_a = a);\n",
|
|
458
|
+
[&](const std::string& msg) { echoed.push_back(msg); });
|
|
459
|
+
EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: inside_a = 2, inside_b = 3",
|
|
460
|
+
"ECHO: after_a = 1"}));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
void checkLetStatementChainsSeveralBindings(bool useVm) {
|
|
464
|
+
ScopedVm vm(useVm);
|
|
465
|
+
std::vector<std::string> echoed;
|
|
466
|
+
// The isosurface.scad shape: each binding built from the previous.
|
|
467
|
+
runScript("let (a = 2, b = a * 3, c = b + a, d = c * b) echo(d = d);\n",
|
|
468
|
+
[&](const std::string& msg) { echoed.push_back(msg); });
|
|
469
|
+
EXPECT_EQ(echoed, (std::vector<std::string>{"ECHO: d = 48"}));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
} // namespace
|
|
473
|
+
|
|
474
|
+
TEST(LetStatementScope, IsSequentialCompiled) { checkLetStatementIsSequential(true); }
|
|
475
|
+
TEST(LetStatementScope, IsSequentialInterpreted) { checkLetStatementIsSequential(false); }
|
|
476
|
+
|
|
477
|
+
TEST(LetStatementScope, ShadowsWithoutEscapingCompiled) { checkLetStatementShadowsWithoutEscaping(true); }
|
|
478
|
+
TEST(LetStatementScope, ShadowsWithoutEscapingInterpreted) { checkLetStatementShadowsWithoutEscaping(false); }
|
|
479
|
+
|
|
480
|
+
TEST(LetStatementScope, ChainsSeveralBindingsCompiled) { checkLetStatementChainsSeveralBindings(true); }
|
|
481
|
+
TEST(LetStatementScope, ChainsSeveralBindingsInterpreted) { checkLetStatementChainsSeveralBindings(false); }
|
|
482
|
+
|
|
483
|
+
|
|
353
484
|
TEST(LetStatement, BindsVisibleInsideBlockOnly) {
|
|
354
485
|
Evaluated e = evalSrc("let(s=3) cube(s);");
|
|
355
486
|
manifold::Box bbox = e.bodies[0].body->BoundingBox();
|