openscad-cpp-evaluator 0.4.6__tar.gz → 0.4.8__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.4.6 → openscad_cpp_evaluator-0.4.8}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/eval_context.hpp +17 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/evaluator.hpp +19 -5
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/value.hpp +55 -6
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/function_builtins.cpp +1 -1
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/bytecode_compiler.cpp +26 -1
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/bytecode_vm.cpp +10 -8
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/eval_context.cpp +25 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/expr_eval.cpp +28 -6
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/manifold_cache.cpp +2 -2
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/user_calls.cpp +47 -17
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/value.cpp +18 -6
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_bytecode_compiler.cpp +34 -20
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_expr_eval.cpp +17 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/README.md +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/tools/cli/main.cpp +0 -0
|
@@ -105,6 +105,23 @@ struct EvalContext {
|
|
|
105
105
|
std::shared_ptr<const ChildrenNodeList> newChildrenNodes = nullptr,
|
|
106
106
|
const EvalContext* newChildrenCallerCtx = nullptr) const;
|
|
107
107
|
|
|
108
|
+
// Identical to callCtx() except `let_`'s new isolated level is opened
|
|
109
|
+
// from `capturedLet` (a closure's own captured defining-scope trail,
|
|
110
|
+
// see Closure's doc comment in value.hpp) instead of `this->let_`.
|
|
111
|
+
// Needed specifically for an ESCAPED closure call -- one where
|
|
112
|
+
// callCtxFor's own closure-nesting check (still-live enclosing call on
|
|
113
|
+
// callStack_) comes back false, meaning the ordinary ancestry walk from
|
|
114
|
+
// `this` (the CALL SITE's context) has no path back to wherever the
|
|
115
|
+
// closure's own captured variables live. `dyn`/`dynPositions` still
|
|
116
|
+
// derive from `this` exactly like callCtx() -- $-vars are dynamically
|
|
117
|
+
// scoped (the caller's own $-vars, not the closure's definer's), so
|
|
118
|
+
// they're unaffected by where the closure came from.
|
|
119
|
+
EvalContext callCtxFromCapturedLet(const std::shared_ptr<TrailView<Value>>& capturedLet,
|
|
120
|
+
const oscad::Scope* newScope = nullptr,
|
|
121
|
+
std::optional<std::array<double, 4>> newColor = std::nullopt,
|
|
122
|
+
std::shared_ptr<const ChildrenNodeList> newChildrenNodes = nullptr,
|
|
123
|
+
const EvalContext* newChildrenCallerCtx = nullptr) const;
|
|
124
|
+
|
|
108
125
|
// let(...)/list-comprehension let() scoping: opens a new level for all
|
|
109
126
|
// four trails, floor unchanged on every one (reads see through to the
|
|
110
127
|
// parent; this let-body's own writes -- including to dyn/dynPositions/
|
|
@@ -303,7 +303,7 @@ public:
|
|
|
303
303
|
// for) -- `bound` is matched against `funcNode`'s OWN parameters
|
|
304
304
|
// (discovered now, not at compile time, since the callee wasn't
|
|
305
305
|
// statically known). Mirrors evalFunctionLiteral exactly otherwise.
|
|
306
|
-
Value evalFunctionLiteralFromBound(const
|
|
306
|
+
Value evalFunctionLiteralFromBound(const Closure& closure, BoundArgs bound, EvalContext& ctx,
|
|
307
307
|
const oscad::Position* callPos);
|
|
308
308
|
|
|
309
309
|
// Testing-only override, checked before the (once-cached) env var --
|
|
@@ -466,9 +466,18 @@ private:
|
|
|
466
466
|
// (see CallStackFrame::upvalueParent's own doc comment for the real
|
|
467
467
|
// scenario this was caught on -- a closure passed through an
|
|
468
468
|
// unrelated intermediary function).
|
|
469
|
+
// `capturedLet`: non-null only for a FunctionLiteral *value* callee
|
|
470
|
+
// (see Closure's doc comment, value.hpp) -- when the closure-nesting
|
|
471
|
+
// check below comes back false (the defining call has already
|
|
472
|
+
// returned -- an ESCAPED closure), the isolated call context is rooted
|
|
473
|
+
// at `capturedLet` instead of `ctx`'s own trail, so the closure's
|
|
474
|
+
// captured variables stay resolvable. Ignored (as if null) when the
|
|
475
|
+
// check comes back true: an actually-still-live enclosing call is
|
|
476
|
+
// already reachable via `ctx`'s own ancestry, the ordinary path.
|
|
469
477
|
EvalContext callCtxFor(const oscad::ASTNode& decl, EvalContext& ctx, const oscad::Scope* scope,
|
|
470
478
|
std::shared_ptr<const ChildrenNodeList> childrenNodes = nullptr,
|
|
471
|
-
const EvalContext* childrenCallerCtx = nullptr, bool* usedChildCtx = nullptr
|
|
479
|
+
const EvalContext* childrenCallerCtx = nullptr, bool* usedChildCtx = nullptr,
|
|
480
|
+
const std::shared_ptr<TrailView<Value>>& capturedLet = nullptr);
|
|
472
481
|
|
|
473
482
|
// Fills in any parameter not already present in `bound` (bindArgs'
|
|
474
483
|
// own return value -- the authoritative "did the caller actually
|
|
@@ -629,7 +638,8 @@ private:
|
|
|
629
638
|
const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params,
|
|
630
639
|
const oscad::Expression& body, bool hasCompiledChunk,
|
|
631
640
|
const std::vector<std::unique_ptr<oscad::Argument>>& arguments,
|
|
632
|
-
EvalContext& ctx, const oscad::Position& callPos
|
|
641
|
+
EvalContext& ctx, const oscad::Position& callPos,
|
|
642
|
+
const std::shared_ptr<TrailView<Value>>& capturedLet = nullptr);
|
|
633
643
|
|
|
634
644
|
// Marker for "not one of the 5 trampolinable node kinds" -- the
|
|
635
645
|
// trampoline falls back to a genuine evalExpr(node, ctx) call for
|
|
@@ -655,7 +665,7 @@ private:
|
|
|
655
665
|
Value evalUserFunction(const std::string& name, const oscad::FunctionDeclaration& decl,
|
|
656
666
|
const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& ctx,
|
|
657
667
|
const oscad::ASTNode* callNode);
|
|
658
|
-
Value evalFunctionLiteral(const
|
|
668
|
+
Value evalFunctionLiteral(const Closure& closure,
|
|
659
669
|
const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& ctx,
|
|
660
670
|
const oscad::ASTNode* callNode);
|
|
661
671
|
|
|
@@ -741,7 +751,11 @@ public:
|
|
|
741
751
|
// nullopt if `declNode` is closure-nested inside the currently-active
|
|
742
752
|
// call, in which case the caller must fall back to a real recursive
|
|
743
753
|
// call (evalUserFunctionFromBound/evalFunctionLiteralFromBound).
|
|
744
|
-
|
|
754
|
+
// `capturedLet`: see callCtxFor's own doc comment -- pass the callee
|
|
755
|
+
// closure's captured trail for a FunctionLiteral-value callee (nullptr
|
|
756
|
+
// for a named FunctionDeclaration callee, which has none).
|
|
757
|
+
std::optional<EvalContext> isolatedCallCtxFor(const oscad::ASTNode& declNode, EvalContext& ctx,
|
|
758
|
+
const std::shared_ptr<TrailView<Value>>& capturedLet = nullptr);
|
|
745
759
|
|
|
746
760
|
// Shared per-hop bookkeeping for BOTH trampolines (evalFunctionBodyTrampoline,
|
|
747
761
|
// user_calls.cpp, AND runCompiledFunctionTrampoline/
|
|
@@ -18,19 +18,23 @@ namespace oscadeval {
|
|
|
18
18
|
|
|
19
19
|
struct ValueList;
|
|
20
20
|
struct ValueObject;
|
|
21
|
+
struct Closure;
|
|
21
22
|
|
|
22
23
|
using ListPtr = std::shared_ptr<const ValueList>;
|
|
23
24
|
using ObjectPtr = std::shared_ptr<const ValueObject>;
|
|
25
|
+
using ClosurePtr = std::shared_ptr<const Closure>;
|
|
24
26
|
|
|
25
27
|
// The OpenSCAD dynamic value type. `bool` is a distinct alternative from
|
|
26
28
|
// `double` throughout the evaluator -- never implicitly coerced, unlike
|
|
27
29
|
// Python's own bool-is-int -- see every free function below.
|
|
28
30
|
//
|
|
29
|
-
// A function-literal *value* (`g = function(x) x*2;`) is the AST
|
|
30
|
-
// pointer
|
|
31
|
-
// (
|
|
32
|
-
//
|
|
33
|
-
//
|
|
31
|
+
// A function-literal *value* (`g = function(x) x*2;`) is a Closure: the AST
|
|
32
|
+
// node pointer plus a captured snapshot of its defining scope's own `let_`
|
|
33
|
+
// trail (see Closure's own doc comment below) -- unlike the Python
|
|
34
|
+
// reference's _expr_function_literal (a bare `return node`, no capture at
|
|
35
|
+
// all), needed for a closure that outlives the call that created it (see
|
|
36
|
+
// Closure's doc comment for the motivating BOSL2 example and why the
|
|
37
|
+
// simpler bare-pointer approach silently breaks that pattern).
|
|
34
38
|
using Value = std::variant<std::monostate, // undef
|
|
35
39
|
bool,
|
|
36
40
|
double, // OpenSCAD has one numeric type
|
|
@@ -38,7 +42,52 @@ using Value = std::variant<std::monostate, // undef
|
|
|
38
42
|
ListPtr,
|
|
39
43
|
OscRange,
|
|
40
44
|
ObjectPtr,
|
|
41
|
-
|
|
45
|
+
ClosurePtr>;
|
|
46
|
+
|
|
47
|
+
// A function-literal value: the AST node (params/body, non-owning -- valid
|
|
48
|
+
// as long as the parsed AST it points into is alive, same ownership
|
|
49
|
+
// contract as every other raw AST pointer this evaluator holds) plus
|
|
50
|
+
// `capturedLet`, a type-erased `shared_ptr<TrailView<Value>>` (see
|
|
51
|
+
// scope_trail.hpp) snapshotting the `let_` trail live at the moment this
|
|
52
|
+
// literal was evaluated into a value. Type-erased here (not
|
|
53
|
+
// `shared_ptr<TrailView<Value>>` directly) purely to avoid a value.hpp <->
|
|
54
|
+
// scope_trail.hpp circular include (scope_trail.hpp already includes
|
|
55
|
+
// value.hpp for Value itself); callers that need the typed pointer back
|
|
56
|
+
// (eval_context.cpp, user_calls.cpp, bytecode_vm.cpp -- anywhere
|
|
57
|
+
// scope_trail.hpp is already visible) use capturedLetTrail() below.
|
|
58
|
+
//
|
|
59
|
+
// WHY this exists: a plain `TrailView` level is popped (and its bindings
|
|
60
|
+
// destroyed) the instant the EvalContext holding it goes out of scope --
|
|
61
|
+
// fine for ordinary nested evaluation, wrong for a closure that ESCAPES
|
|
62
|
+
// its creating call, e.g. BOSL2's `hashmap()`:
|
|
63
|
+
// function make_closure(captured) = function(k) captured + k;
|
|
64
|
+
// c = make_closure(10);
|
|
65
|
+
// echo(c(5)); // must be 15 -- `captured` has to survive make_closure()
|
|
66
|
+
// // having already returned by the time c(5) runs.
|
|
67
|
+
// Holding this shared_ptr copy keeps that trail level's refcount above
|
|
68
|
+
// zero for as long as ANY closure value referencing it is still reachable,
|
|
69
|
+
// so `captured` stays resolvable via the ordinary ancestry-walk lookup
|
|
70
|
+
// long after the call that bound it has returned -- no snapshot/copy of
|
|
71
|
+
// the bindings themselves needed, just extending what already exists.
|
|
72
|
+
// `Closure::operator==` compares `node` only (matching the old bare-
|
|
73
|
+
// pointer equality exactly) -- two closures over the same AST node are
|
|
74
|
+
// still "the same function", regardless of what each captured.
|
|
75
|
+
struct Closure {
|
|
76
|
+
const oscad::FunctionLiteral* node = nullptr;
|
|
77
|
+
std::shared_ptr<void> capturedLet;
|
|
78
|
+
|
|
79
|
+
friend bool operator==(const Closure& a, const Closure& b) { return a.node == b.node; }
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
template <typename T>
|
|
83
|
+
class TrailView;
|
|
84
|
+
|
|
85
|
+
// Typed access to Closure::capturedLet -- see its own doc comment for why
|
|
86
|
+
// the field itself is type-erased. Only ever called where scope_trail.hpp
|
|
87
|
+
// is already included.
|
|
88
|
+
inline std::shared_ptr<TrailView<Value>> capturedLetTrail(const Closure& c) {
|
|
89
|
+
return std::static_pointer_cast<TrailView<Value>>(c.capturedLet);
|
|
90
|
+
}
|
|
42
91
|
|
|
43
92
|
// Defined after Value so both can hold Value by value -- the standard
|
|
44
93
|
// recursive-variant pattern (indirection through a forward-declared,
|
{openscad_cpp_evaluator-0.4.6 → openscad_cpp_evaluator-0.4.8}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -615,7 +615,7 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
615
615
|
}
|
|
616
616
|
case BuiltinFnId::IsFunction: {
|
|
617
617
|
const Value x = getArg(args, 0, "x", Value{});
|
|
618
|
-
return Value{std::holds_alternative<
|
|
618
|
+
return Value{std::holds_alternative<ClosurePtr>(x) && std::get<ClosurePtr>(x) != nullptr};
|
|
619
619
|
}
|
|
620
620
|
case BuiltinFnId::IsObject: {
|
|
621
621
|
const Value x = getArg(args, 0, "x", Value{});
|
|
@@ -261,8 +261,33 @@ public:
|
|
|
261
261
|
*n.body)) {
|
|
262
262
|
throw NotCompilable{};
|
|
263
263
|
}
|
|
264
|
+
// A nested literal that reads ANY enclosing variable
|
|
265
|
+
// (literalChunk.upvalues non-empty) is compiled via
|
|
266
|
+
// Op::LoadUpvalue -- a live-call-stack walk (findUpvalue)
|
|
267
|
+
// that only ever resolves a still-active enclosing call,
|
|
268
|
+
// never a captured environment (see LoadUpvalue/
|
|
269
|
+
// findUpvalue's own doc comments: "this codebase has no
|
|
270
|
+
// escaping closures"). A closure that escapes its creating
|
|
271
|
+
// call (returned, stored, passed on -- see Closure's own
|
|
272
|
+
// doc comment, value.hpp, for the motivating BOSL2
|
|
273
|
+
// example) would silently read undef for every captured
|
|
274
|
+
// variable through this path. Bailing the WHOLE containing
|
|
275
|
+
// compilation here forces such a function to run through
|
|
276
|
+
// the interpreter instead, which DOES support escaping
|
|
277
|
+
// closures correctly (evalExpr's FunctionLiteral case
|
|
278
|
+
// captures ctx.let_ itself). A literal with no upvalues at
|
|
279
|
+
// all (doesn't reference anything from an enclosing scope)
|
|
280
|
+
// has nothing that needs escaping-capture support, so it's
|
|
281
|
+
// unaffected and keeps compiling normally.
|
|
282
|
+
if (!literalChunk.upvalues.empty()) throw NotCompilable{};
|
|
264
283
|
chunk_.nestedLiterals.emplace_back(&n, std::move(literalChunk));
|
|
265
|
-
|
|
284
|
+
// No captured `let_` trail here (nullptr) -- a compile-time
|
|
285
|
+
// constant can't carry per-invocation runtime state, but
|
|
286
|
+
// (per the upvalues check just above) this literal doesn't
|
|
287
|
+
// reference anything outside itself, so there is nothing
|
|
288
|
+
// capture. Not a regression; just not (yet) extended here.
|
|
289
|
+
out.push_back({Op::PushConst, internConst(Value{std::make_shared<const Closure>(Closure{&n, nullptr})}),
|
|
290
|
+
0, nullptr});
|
|
266
291
|
return;
|
|
267
292
|
}
|
|
268
293
|
case NodeKind::RangeLiteral: {
|
|
@@ -342,22 +342,23 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
342
342
|
Value callee = std::move(stack.back());
|
|
343
343
|
stack.pop_back();
|
|
344
344
|
Value result;
|
|
345
|
-
if (const auto*
|
|
345
|
+
if (const auto* closurePtr = std::get_if<ClosurePtr>(&callee); closurePtr && *closurePtr) {
|
|
346
|
+
const Closure& closure = **closurePtr;
|
|
346
347
|
BoundArgs bound;
|
|
347
348
|
bound.reserve(argCount);
|
|
348
349
|
size_t positionalIdx = 0;
|
|
349
|
-
const size_t nparams =
|
|
350
|
+
const size_t nparams = closure.node->parameters.size();
|
|
350
351
|
for (size_t i = 0; i < argCount; ++i) {
|
|
351
352
|
if (site.argNames[i]) {
|
|
352
353
|
bound.set(*site.argNames[i], std::move(args[i]));
|
|
353
354
|
} else {
|
|
354
355
|
if (positionalIdx < nparams) {
|
|
355
|
-
bound.set(
|
|
356
|
+
bound.set(closure.node->parameters[positionalIdx]->name->name, std::move(args[i]));
|
|
356
357
|
}
|
|
357
358
|
++positionalIdx;
|
|
358
359
|
}
|
|
359
360
|
}
|
|
360
|
-
result = ev.evalFunctionLiteralFromBound(
|
|
361
|
+
result = ev.evalFunctionLiteralFromBound(closure, std::move(bound), ctx, ins.pos);
|
|
361
362
|
} else if (!site.calleeName.empty()) {
|
|
362
363
|
ev.warn("Ignoring unknown function '" + site.calleeName + "'", ins.pos);
|
|
363
364
|
}
|
|
@@ -424,8 +425,9 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
424
425
|
Value callee = std::move(stack.back());
|
|
425
426
|
stack.pop_back();
|
|
426
427
|
Value result;
|
|
427
|
-
if (const auto*
|
|
428
|
-
const
|
|
428
|
+
if (const auto* closurePtr = std::get_if<ClosurePtr>(&callee); closurePtr && *closurePtr) {
|
|
429
|
+
const Closure& closure = **closurePtr;
|
|
430
|
+
const oscad::FunctionLiteral& funcNode = *closure.node;
|
|
429
431
|
BoundArgs bound;
|
|
430
432
|
bound.reserve(argCount);
|
|
431
433
|
size_t positionalIdx = 0;
|
|
@@ -441,7 +443,7 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
441
443
|
}
|
|
442
444
|
}
|
|
443
445
|
if (tailOut != nullptr) {
|
|
444
|
-
if (auto hopCtx = ev.isolatedCallCtxFor(funcNode, ctx)) {
|
|
446
|
+
if (auto hopCtx = ev.isolatedCallCtxFor(funcNode, ctx, capturedLetTrail(closure))) {
|
|
445
447
|
if (ev.lookupCompiledLiteralChunk(funcNode) != nullptr) {
|
|
446
448
|
tailOut->decl = nullptr;
|
|
447
449
|
tailOut->literal = &funcNode;
|
|
@@ -453,7 +455,7 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
453
455
|
}
|
|
454
456
|
}
|
|
455
457
|
}
|
|
456
|
-
result = ev.evalFunctionLiteralFromBound(
|
|
458
|
+
result = ev.evalFunctionLiteralFromBound(closure, std::move(bound), ctx, ins.pos);
|
|
457
459
|
} else if (!site.calleeName.empty()) {
|
|
458
460
|
ev.warn("Ignoring unknown function '" + site.calleeName + "'", ins.pos);
|
|
459
461
|
}
|
|
@@ -60,6 +60,31 @@ EvalContext EvalContext::callCtx(const oscad::Scope* newScope, std::optional<std
|
|
|
60
60
|
return result;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
EvalContext EvalContext::callCtxFromCapturedLet(const std::shared_ptr<TrailView<Value>>& capturedLet,
|
|
64
|
+
const oscad::Scope* newScope,
|
|
65
|
+
std::optional<std::array<double, 4>> newColor,
|
|
66
|
+
std::shared_ptr<const ChildrenNodeList> newChildrenNodes,
|
|
67
|
+
const EvalContext* newChildrenCallerCtx) const {
|
|
68
|
+
EvalContext result;
|
|
69
|
+
result.scope = newScope ? newScope : scope;
|
|
70
|
+
auto newDynTrail = dyn.trail()->openChild(/*isolate=*/false); // stays dynamically scoped through the CALL SITE
|
|
71
|
+
result.dyn = DynValueView(newDynTrail);
|
|
72
|
+
result.dynExplicit = DynExplicitView(newDynTrail);
|
|
73
|
+
// isolate=false: continue the ancestry chain THROUGH capturedLet, not
|
|
74
|
+
// terminate at it -- the entire point is inheriting the closure's own
|
|
75
|
+
// captured bindings (isolate=true would sever that immediately,
|
|
76
|
+
// making the capture pointless; the isolation this needs -- NOT
|
|
77
|
+
// seeing the call SITE's own locals -- already comes for free from
|
|
78
|
+
// rooting at capturedLet instead of at `this->let_` in the first
|
|
79
|
+
// place).
|
|
80
|
+
result.let_ = capturedLet->openChild(/*isolate=*/false);
|
|
81
|
+
result.dynPositions = dynPositions->openChild(true);
|
|
82
|
+
result.color = newColor.has_value() ? newColor : color;
|
|
83
|
+
result.childrenNodes = newChildrenNodes ? newChildrenNodes : std::make_shared<const ChildrenNodeList>();
|
|
84
|
+
result.childrenCallerCtx = newChildrenCallerCtx;
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
EvalContext EvalContext::letChildCtx() const {
|
|
64
89
|
EvalContext result = *this;
|
|
65
90
|
auto newDynTrail = dyn.trail()->openChild(/*isolate=*/false);
|
|
@@ -103,7 +103,23 @@ Value Evaluator::evalIdentifier(const std::string& name, const oscad::Position*
|
|
|
103
103
|
if (const Value* v = ctx.let_->find(name)) return *v;
|
|
104
104
|
|
|
105
105
|
if (!name.empty() && name[0] == '$') {
|
|
106
|
-
|
|
106
|
+
// $-prefixed names are dynamic variables: a completely separate
|
|
107
|
+
// namespace from lexically-scoped identifiers, never requiring a
|
|
108
|
+
// scope declaration (unlike a bare identifier). Found via
|
|
109
|
+
// BelfrySCAD/BOSL2's constants.scad `get_slop()`
|
|
110
|
+
// (`is_undef($slop) ? 0 : $slop` with $slop never assigned
|
|
111
|
+
// anywhere) producing a spurious "Ignoring unknown variable
|
|
112
|
+
// '$slop'" warning here but not through the bytecode VM's
|
|
113
|
+
// Op::LoadDyn (bytecode_vm.cpp), which already does exactly
|
|
114
|
+
// this -- find in ctx.dyn, undef if absent, no scope-lookup
|
|
115
|
+
// fallback, no warning, ever. Verified against real OpenSCAD.app:
|
|
116
|
+
// no warning for a never-set $-variable. Must return here
|
|
117
|
+
// unconditionally rather than falling through to the
|
|
118
|
+
// scope->lookupVariable() check below, which can never resolve a
|
|
119
|
+
// $-name anyway (dynamic variables aren't scope-declarable) and
|
|
120
|
+
// exists only for plain identifiers.
|
|
121
|
+
const Value* v = ctx.dyn->find(name);
|
|
122
|
+
return v ? *v : Value{};
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
if (name == "PI") return Value{std::numbers::pi};
|
|
@@ -375,11 +391,17 @@ Value Evaluator::evalExpr(const oscad::Expression& node, EvalContext& ctx) {
|
|
|
375
391
|
case NodeKind::ListComprehension:
|
|
376
392
|
return evalListLiteral(static_cast<const oscad::ListComprehension&>(node), ctx);
|
|
377
393
|
case NodeKind::FunctionLiteral:
|
|
378
|
-
// A function-literal *value*
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
394
|
+
// A function-literal *value* captures ctx.let_ (extending that
|
|
395
|
+
// trail level's lifetime for as long as this Closure is
|
|
396
|
+
// reachable -- see Closure's own doc comment, value.hpp) so a
|
|
397
|
+
// closure that escapes this call (returned, stored, passed on)
|
|
398
|
+
// can still resolve variables from its defining scope after
|
|
399
|
+
// this call has returned. `dyn`/scope are NOT captured: $-vars
|
|
400
|
+
// are dynamically scoped (see the caller's own ctx.dyn at call
|
|
401
|
+
// time, matching real OpenSCAD), and the lexical scope is
|
|
402
|
+
// static AST data already reachable via the node itself.
|
|
403
|
+
return Value{std::make_shared<const Closure>(
|
|
404
|
+
Closure{static_cast<const oscad::FunctionLiteral*>(&node), ctx.let_})};
|
|
383
405
|
case NodeKind::PrimaryCall:
|
|
384
406
|
return evalFunctionCall(static_cast<const oscad::PrimaryCall&>(node), ctx);
|
|
385
407
|
case NodeKind::LetOp:
|
|
@@ -84,9 +84,9 @@ std::string canonValue(const Value& v) {
|
|
|
84
84
|
}
|
|
85
85
|
out += ")";
|
|
86
86
|
return out;
|
|
87
|
-
} else { //
|
|
87
|
+
} else { // ClosurePtr
|
|
88
88
|
char buf[32];
|
|
89
|
-
std::snprintf(buf, sizeof(buf), "F%p", static_cast<const void*>(val));
|
|
89
|
+
std::snprintf(buf, sizeof(buf), "F%p", val ? static_cast<const void*>(val->node) : nullptr);
|
|
90
90
|
return buf;
|
|
91
91
|
}
|
|
92
92
|
},
|
|
@@ -104,7 +104,30 @@ BoundArgs Evaluator::bindArgs(const std::vector<std::unique_ptr<oscad::Parameter
|
|
|
104
104
|
|
|
105
105
|
EvalContext Evaluator::callCtxFor(const oscad::ASTNode& decl, EvalContext& ctx, const oscad::Scope* scope,
|
|
106
106
|
std::shared_ptr<const ChildrenNodeList> childrenNodes,
|
|
107
|
-
const EvalContext* childrenCallerCtx, bool* usedChildCtx
|
|
107
|
+
const EvalContext* childrenCallerCtx, bool* usedChildCtx,
|
|
108
|
+
const std::shared_ptr<TrailView<Value>>& capturedLet) {
|
|
109
|
+
// A closure's own captured environment (capturedLet, non-null for
|
|
110
|
+
// every interpreter-evaluated FunctionLiteral -- see expr_eval.cpp's
|
|
111
|
+
// own FunctionLiteral case) is always the correct root for this call,
|
|
112
|
+
// full stop -- checked BEFORE the live-call-stack walk below.
|
|
113
|
+
// callCtxFromCapturedLet roots the new scope at `capturedLet` itself,
|
|
114
|
+
// never at `ctx.let_`, so it's already correct regardless of whether
|
|
115
|
+
// `ctx` (the CALLER's own context) happens to be ancestor-linked back
|
|
116
|
+
// to the closure's declaring call or not. That distinction matters in
|
|
117
|
+
// exactly the case the live-frame walk below gets wrong: a closure
|
|
118
|
+
// declared inside function A, passed as a Value into a plain function
|
|
119
|
+
// B, and invoked from inside B's body. B's own ctx is isolated from
|
|
120
|
+
// A's (an ordinary call boundary, see EvalContext::callCtx's own
|
|
121
|
+
// isolate=true), so even though A's own frame may still be technically
|
|
122
|
+
// live on callStack_ (matching the containment check below) and this
|
|
123
|
+
// WOULD take the childCtx() branch, `ctx.childCtx()` continues B's own
|
|
124
|
+
// (isolated) ancestry, not A's -- silently losing every variable the
|
|
125
|
+
// closure captured from A. Skipping straight to capturedLet avoids
|
|
126
|
+
// this regardless of which frames happen to still be live.
|
|
127
|
+
if (capturedLet) {
|
|
128
|
+
if (usedChildCtx) *usedChildCtx = false;
|
|
129
|
+
return ctx.callCtxFromCapturedLet(capturedLet, scope, std::nullopt, std::move(childrenNodes), childrenCallerCtx);
|
|
130
|
+
}
|
|
108
131
|
const oscad::Position& declPos = decl.position();
|
|
109
132
|
for (const CallStackFrame& frame : callStack_) {
|
|
110
133
|
const oscad::Position* outer = frame.declPosition;
|
|
@@ -119,9 +142,10 @@ EvalContext Evaluator::callCtxFor(const oscad::ASTNode& decl, EvalContext& ctx,
|
|
|
119
142
|
return ctx.callCtx(scope, std::nullopt, std::move(childrenNodes), childrenCallerCtx);
|
|
120
143
|
}
|
|
121
144
|
|
|
122
|
-
std::optional<EvalContext> Evaluator::isolatedCallCtxFor(const oscad::ASTNode& declNode, EvalContext& ctx
|
|
145
|
+
std::optional<EvalContext> Evaluator::isolatedCallCtxFor(const oscad::ASTNode& declNode, EvalContext& ctx,
|
|
146
|
+
const std::shared_ptr<TrailView<Value>>& capturedLet) {
|
|
123
147
|
bool usedChildCtx = false;
|
|
124
|
-
EvalContext result = callCtxFor(declNode, ctx, ctx.scope, nullptr, nullptr, &usedChildCtx);
|
|
148
|
+
EvalContext result = callCtxFor(declNode, ctx, ctx.scope, nullptr, nullptr, &usedChildCtx, capturedLet);
|
|
125
149
|
if (usedChildCtx) return std::nullopt;
|
|
126
150
|
return result;
|
|
127
151
|
}
|
|
@@ -242,11 +266,11 @@ std::optional<Evaluator::TailStep> Evaluator::tryTailStepFor(
|
|
|
242
266
|
const std::string& calleeName, const oscad::ASTNode& declNode,
|
|
243
267
|
const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params, const oscad::Expression& body,
|
|
244
268
|
bool hasCompiledChunk, const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& ctx,
|
|
245
|
-
const oscad::Position& callPos) {
|
|
269
|
+
const oscad::Position& callPos, const std::shared_ptr<TrailView<Value>>& capturedLet) {
|
|
246
270
|
if (hasCompiledChunk) return std::nullopt;
|
|
247
271
|
const oscad::Scope* fnScope = declNode.scope() ? declNode.scope() : ctx.scope;
|
|
248
272
|
bool usedChildCtx = false;
|
|
249
|
-
EvalContext childCtx = callCtxFor(declNode, ctx, fnScope, nullptr, nullptr, &usedChildCtx);
|
|
273
|
+
EvalContext childCtx = callCtxFor(declNode, ctx, fnScope, nullptr, nullptr, &usedChildCtx, capturedLet);
|
|
250
274
|
if (usedChildCtx) return std::nullopt;
|
|
251
275
|
bindCallArgsInto(params, bindArgs(params, arguments, ctx), childCtx);
|
|
252
276
|
TailStep step;
|
|
@@ -360,14 +384,16 @@ std::variant<Value, Evaluator::TailStep, Evaluator::NotTailStep> Evaluator::simp
|
|
|
360
384
|
// directly. Never re-evaluate n.left below this point -- it
|
|
361
385
|
// may itself be an arbitrary (side-effecting) expression.
|
|
362
386
|
Value funcVal = leftId ? evalIdentifier(leftId->name, &leftId->position(), ctx, false) : evalExpr(*n.left, ctx);
|
|
363
|
-
if (const auto*
|
|
364
|
-
const
|
|
387
|
+
if (const auto* closurePtr = std::get_if<ClosurePtr>(&funcVal); closurePtr && *closurePtr) {
|
|
388
|
+
const Closure& closure = **closurePtr;
|
|
389
|
+
const oscad::FunctionLiteral& funcNode = *closure.node;
|
|
365
390
|
checkDebug(n, ctx); // call-site stop, function-literal callee
|
|
366
391
|
const bool hasChunk = useBytecodeVm() && lookupCompiledLiteralChunk(funcNode) != nullptr;
|
|
367
|
-
std::optional<TailStep> step =
|
|
368
|
-
|
|
392
|
+
std::optional<TailStep> step =
|
|
393
|
+
tryTailStepFor("<function literal>", funcNode, funcNode.parameters, *funcNode.body, hasChunk,
|
|
394
|
+
n.arguments, ctx, n.position(), capturedLetTrail(closure));
|
|
369
395
|
if (step) return std::move(*step);
|
|
370
|
-
return Value{evalFunctionLiteral(
|
|
396
|
+
return Value{evalFunctionLiteral(closure, n.arguments, ctx, &n)};
|
|
371
397
|
}
|
|
372
398
|
|
|
373
399
|
if (leftId) warn("Ignoring unknown function '" + leftId->name + "'", &n.position());
|
|
@@ -484,13 +510,15 @@ Value Evaluator::evalUserFunctionFromBound(const std::string& name, const oscad:
|
|
|
484
510
|
});
|
|
485
511
|
}
|
|
486
512
|
|
|
487
|
-
Value Evaluator::evalFunctionLiteral(const
|
|
513
|
+
Value Evaluator::evalFunctionLiteral(const Closure& closure,
|
|
488
514
|
const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& ctx,
|
|
489
515
|
const oscad::ASTNode* callNode) {
|
|
516
|
+
const oscad::FunctionLiteral& funcNode = *closure.node;
|
|
490
517
|
const oscad::Scope* fnScope = funcNode.scope() ? funcNode.scope() : ctx.scope;
|
|
491
518
|
const int callerFrameIdx = callStack_.empty() ? -1 : static_cast<int>(callStack_.size()) - 1;
|
|
492
519
|
bool usedChildCtx = false;
|
|
493
|
-
EvalContext childCtx =
|
|
520
|
+
EvalContext childCtx =
|
|
521
|
+
callCtxFor(funcNode, ctx, fnScope, nullptr, nullptr, &usedChildCtx, capturedLetTrail(closure));
|
|
494
522
|
const int upvalueParent = usedChildCtx ? callerFrameIdx : -1;
|
|
495
523
|
|
|
496
524
|
// A literal only ever has a compiled chunk if some OTHER declaration's
|
|
@@ -519,12 +547,14 @@ Value Evaluator::evalFunctionLiteral(const oscad::FunctionLiteral& funcNode,
|
|
|
519
547
|
});
|
|
520
548
|
}
|
|
521
549
|
|
|
522
|
-
Value Evaluator::evalFunctionLiteralFromBound(const
|
|
550
|
+
Value Evaluator::evalFunctionLiteralFromBound(const Closure& closure, BoundArgs bound,
|
|
523
551
|
EvalContext& ctx, const oscad::Position* callPos) {
|
|
552
|
+
const oscad::FunctionLiteral& funcNode = *closure.node;
|
|
524
553
|
const oscad::Scope* fnScope = funcNode.scope() ? funcNode.scope() : ctx.scope;
|
|
525
554
|
const int callerFrameIdx = callStack_.empty() ? -1 : static_cast<int>(callStack_.size()) - 1;
|
|
526
555
|
bool usedChildCtx = false;
|
|
527
|
-
EvalContext childCtx =
|
|
556
|
+
EvalContext childCtx =
|
|
557
|
+
callCtxFor(funcNode, ctx, fnScope, nullptr, nullptr, &usedChildCtx, capturedLetTrail(closure));
|
|
528
558
|
const int upvalueParent = usedChildCtx ? callerFrameIdx : -1;
|
|
529
559
|
const CompiledChunk* chunk = useBytecodeVm() ? lookupCompiledLiteralChunk(funcNode) : nullptr;
|
|
530
560
|
if (!chunk) {
|
|
@@ -579,10 +609,10 @@ Value Evaluator::evalFunctionCall(const oscad::PrimaryCall& node, EvalContext& c
|
|
|
579
609
|
// g(3)`). warnIfUndef=false: probing here shouldn't itself warn
|
|
580
610
|
// "unknown variable" if `left` isn't bound to anything -- a genuinely
|
|
581
611
|
// unknown callee gets exactly one warning below, not two.
|
|
582
|
-
Value
|
|
583
|
-
if (const auto*
|
|
612
|
+
Value funcVal = leftId ? evalIdentifier(leftId->name, &leftId->position(), ctx, false) : evalExpr(*node.left, ctx);
|
|
613
|
+
if (const auto* closurePtr = std::get_if<ClosurePtr>(&funcVal); closurePtr && *closurePtr) {
|
|
584
614
|
checkDebug(node, ctx); // same call-site stop, function-literal callee
|
|
585
|
-
return evalFunctionLiteral(**
|
|
615
|
+
return evalFunctionLiteral(**closurePtr, node.arguments, ctx, &node);
|
|
586
616
|
}
|
|
587
617
|
|
|
588
618
|
if (leftId) {
|
|
@@ -53,7 +53,7 @@ std::string oscTypeName(const Value& v) {
|
|
|
53
53
|
if (std::holds_alternative<std::string>(v)) return "string";
|
|
54
54
|
if (std::holds_alternative<ListPtr>(v)) return "vector";
|
|
55
55
|
if (std::holds_alternative<ObjectPtr>(v)) return "object";
|
|
56
|
-
return "undefined"; // OscRange,
|
|
56
|
+
return "undefined"; // OscRange, ClosurePtr
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
bool oscEqual(const Value& a, const Value& b) {
|
|
@@ -88,9 +88,21 @@ bool oscEqual(const Value& a, const Value& b) {
|
|
|
88
88
|
return true;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
91
|
+
// ClosurePtr is a shared_ptr -- its own operator== compares the pointee
|
|
92
|
+
// ADDRESS, not Closure::operator==(), so two independently-created
|
|
93
|
+
// closures over the identical AST node (the semantics this mirrors --
|
|
94
|
+
// see Closure's own doc comment) would wrongly compare unequal via the
|
|
95
|
+
// variant fallthrough below. Dereference and compare explicitly.
|
|
96
|
+
const ClosurePtr* ca = std::get_if<ClosurePtr>(&a);
|
|
97
|
+
const ClosurePtr* cb = std::get_if<ClosurePtr>(&b);
|
|
98
|
+
if (ca || cb) {
|
|
99
|
+
if (!ca || !cb || !*ca || !*cb) return false;
|
|
100
|
+
return **ca == **cb;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Neither operand is a list, object, or closure here, so this only ever
|
|
104
|
+
// compares monostate/bool/double/string/OscRange against its own kind
|
|
105
|
+
// (variant::operator== checks the active index first).
|
|
94
106
|
return a == b;
|
|
95
107
|
}
|
|
96
108
|
|
|
@@ -107,7 +119,7 @@ bool truthy(const Value& v) {
|
|
|
107
119
|
if (const ListPtr* l = std::get_if<ListPtr>(&v)) return *l && !(*l)->items.empty();
|
|
108
120
|
if (const ObjectPtr* o = std::get_if<ObjectPtr>(&v)) return *o && !(*o)->items.empty();
|
|
109
121
|
if (std::holds_alternative<std::monostate>(v)) return false;
|
|
110
|
-
return true; // OscRange,
|
|
122
|
+
return true; // OscRange, ClosurePtr
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
bool oscComparable(const Value& a, const Value& b) {
|
|
@@ -325,7 +337,7 @@ std::string fmtValue(const Value& v) {
|
|
|
325
337
|
return s + ")";
|
|
326
338
|
}
|
|
327
339
|
if (const std::string* s = std::get_if<std::string>(&v)) return "\"" + *s + "\"";
|
|
328
|
-
return "<function-literal>"; //
|
|
340
|
+
return "<function-literal>"; // OscRange handled above; ClosurePtr has no meaningful textual form either
|
|
329
341
|
}
|
|
330
342
|
|
|
331
343
|
// (Range::numValues()'s own count, closed-form) -- 1 + floor((end-start)/step),
|