openscad-cpp-evaluator 0.4.10__tar.gz → 0.6.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.4.10 → openscad_cpp_evaluator-0.6.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/bindings/module.cpp +55 -3
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/bytecode.hpp +96 -9
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/evaluator.hpp +67 -1
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/bytecode_compiler.cpp +90 -42
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/bytecode_vm.cpp +28 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/user_calls.cpp +10 -2
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_bytecode_compiler.cpp +235 -21
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_python_bindings.py +4 -4
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_tail_calls.cpp +16 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.4.10 → openscad_cpp_evaluator-0.6.0}/tools/cli/main.cpp +0 -0
|
@@ -349,6 +349,51 @@ nb::object generatePartialTrampoline(const GeneratePartialFn& generatePartial) {
|
|
|
349
349
|
// synchronously within the current hook call, same as get_frames.
|
|
350
350
|
using GetChildrenPositionsFn = std::function<const std::optional<std::vector<std::pair<std::string, int>>>&()>;
|
|
351
351
|
|
|
352
|
+
// Trampoline: lets the Python hook tell the Evaluator it's safe to speed
|
|
353
|
+
// up function calls with the bytecode VM right now -- see
|
|
354
|
+
// Evaluator::setFastContinueBreakpoints's own doc comment (evaluator.hpp)
|
|
355
|
+
// for the exact contract (only when no step is pending/no pause was
|
|
356
|
+
// requested/the initial break-on-first stop is already consumed) and
|
|
357
|
+
// Evaluator::chunkEligibleNow for how the breakpoint set is used (a
|
|
358
|
+
// per-function span check, not a blanket switch). `breakpoints=None`
|
|
359
|
+
// means "no exception right now" -- i.e. every function call runs
|
|
360
|
+
// interpreted, same as if this were never called at all. A callable
|
|
361
|
+
// (not a return-tuple field) so DebugSession's own hook() can call it
|
|
362
|
+
// exactly where it already computes should_pause, without needing to
|
|
363
|
+
// widen hook()'s existing (cmd, mods) return-tuple contract -- mirrors
|
|
364
|
+
// generate_partial/get_children_positions's own "extra kwarg the Python
|
|
365
|
+
// side may or may not call" shape.
|
|
366
|
+
using SetFastContinueFn = std::function<void(std::optional<std::unordered_map<std::string, std::set<int>>>)>;
|
|
367
|
+
|
|
368
|
+
// Wraps a SetFastContinueFn as a Python callable taking either a
|
|
369
|
+
// dict[str, set[int]]/dict[str, list[int]] (origin -> breakpoint lines) or
|
|
370
|
+
// None. Mirrors generatePartialTrampoline's own shape.
|
|
371
|
+
nb::object setFastContinueTrampoline(const SetFastContinueFn& setFastContinue) {
|
|
372
|
+
return nb::cpp_function(
|
|
373
|
+
[&setFastContinue](nb::object breakpoints) {
|
|
374
|
+
if (breakpoints.is_none()) {
|
|
375
|
+
setFastContinue(std::nullopt);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
std::unordered_map<std::string, std::set<int>> bp;
|
|
379
|
+
for (auto [k, v] : nb::cast<nb::dict>(breakpoints)) {
|
|
380
|
+
std::set<int> lines;
|
|
381
|
+
for (nb::handle line : v) lines.insert(nb::cast<int>(line));
|
|
382
|
+
bp.emplace(nb::cast<std::string>(k), std::move(lines));
|
|
383
|
+
}
|
|
384
|
+
setFastContinue(std::move(bp));
|
|
385
|
+
},
|
|
386
|
+
// Without .none(), nanobind rejects a Python `None` argument for an
|
|
387
|
+
// `nb::object`-typed parameter registered this ad-hoc way (unlike a
|
|
388
|
+
// `.def()`-declared function parameter, which accepts None by
|
|
389
|
+
// default) -- confirmed via a real call: "incompatible function
|
|
390
|
+
// arguments ... Invoked with types: NoneType" the first time this
|
|
391
|
+
// was called with breakpoints=None (DebugSession's own
|
|
392
|
+
// _apply_fast_continue calls it with None whenever fast-continue
|
|
393
|
+
// mode isn't safe right now -- see debugger.py).
|
|
394
|
+
nb::arg("breakpoints").none());
|
|
395
|
+
}
|
|
396
|
+
|
|
352
397
|
// GIL held (reacquired by the caller). `getFrame`/`callStack`/`generatePartial`/
|
|
353
398
|
// `getChildrenPositions` are valid only for this synchronous call, so their
|
|
354
399
|
// closures are only ever invoked from within the Python hook, before it returns.
|
|
@@ -357,7 +402,8 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
|
|
|
357
402
|
const std::vector<oscadeval::CallStackFrame>& callStack,
|
|
358
403
|
const oscadeval::DebugFramesFn& getFrame,
|
|
359
404
|
const GeneratePartialFn& generatePartial,
|
|
360
|
-
const GetChildrenPositionsFn& getChildrenPositions
|
|
405
|
+
const GetChildrenPositionsFn& getChildrenPositions,
|
|
406
|
+
const SetFastContinueFn& setFastContinue) {
|
|
361
407
|
auto getFramesPy = nb::cpp_function([&callStack, &getFrame]() -> nb::object {
|
|
362
408
|
std::vector<oscadeval::DebugFrame> frames = getFrame();
|
|
363
409
|
nb::list allFrameLocals;
|
|
@@ -380,6 +426,7 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
|
|
|
380
426
|
for (const auto& [origin, line] : *positions) out.append(nb::make_tuple(origin, line));
|
|
381
427
|
return out;
|
|
382
428
|
});
|
|
429
|
+
nb::object setFastContinuePy = setFastContinueTrampoline(setFastContinue);
|
|
383
430
|
|
|
384
431
|
// expr_depth stays hardcoded at 0 -- this port deliberately doesn't
|
|
385
432
|
// track the reference's _expr_depth counter (no consumer reads it);
|
|
@@ -387,7 +434,8 @@ oscadeval::DebugAction callPyDebugHook(nb::handle hook, int line, int depth, boo
|
|
|
387
434
|
nb::object ret = hook(line, depth, nb::arg("forced") = forced, nb::arg("expr_level") = exprLevel,
|
|
388
435
|
nb::arg("expr_depth") = 0, nb::arg("origin") = origin, nb::arg("get_frames") = getFramesPy,
|
|
389
436
|
nb::arg("generate_partial") = generatePartialPy,
|
|
390
|
-
nb::arg("get_children_positions") = getChildrenPositionsPy
|
|
437
|
+
nb::arg("get_children_positions") = getChildrenPositionsPy,
|
|
438
|
+
nb::arg("set_fast_continue") = setFastContinuePy);
|
|
391
439
|
nb::tuple t = nb::cast<nb::tuple>(ret);
|
|
392
440
|
oscadeval::DebugAction action;
|
|
393
441
|
action.stop = (nb::cast<std::string>(t[0]) == "stop");
|
|
@@ -452,13 +500,17 @@ nb::object debugEvaluate(const std::string& path, nb::dict viewportParams, nb::c
|
|
|
452
500
|
[&evPtr]() -> const std::optional<std::vector<std::pair<std::string, int>>>& {
|
|
453
501
|
return evPtr->lastChildrenPositions();
|
|
454
502
|
};
|
|
503
|
+
SetFastContinueFn setFastContinue =
|
|
504
|
+
[&evPtr](std::optional<std::unordered_map<std::string, std::set<int>>> breakpoints) {
|
|
505
|
+
evPtr->setFastContinueBreakpoints(std::move(breakpoints));
|
|
506
|
+
};
|
|
455
507
|
oscadeval::DebugHooks hooks;
|
|
456
508
|
hooks.debugHook = [&](int line, int depth, bool forced, bool exprLevel, const std::string& origin,
|
|
457
509
|
const std::vector<oscadeval::CallStackFrame>& cs,
|
|
458
510
|
const oscadeval::DebugFramesFn& gf) -> oscadeval::DebugAction {
|
|
459
511
|
nb::gil_scoped_acquire g;
|
|
460
512
|
return callPyDebugHook(debugHook, line, depth, forced, exprLevel, origin, cs, gf, generatePartial,
|
|
461
|
-
getChildrenPositions);
|
|
513
|
+
getChildrenPositions, setFastContinue);
|
|
462
514
|
};
|
|
463
515
|
hooks.errorBreak = [&](int line, const std::string& header, const std::string& origin,
|
|
464
516
|
const std::vector<oscadeval::CallStackFrame>& cs, const oscadeval::DebugFramesFn& gf) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
#include "openscad_cpp_parser/ast/ast_node.hpp"
|
|
6
6
|
|
|
7
|
+
#include <limits>
|
|
7
8
|
#include <optional>
|
|
8
9
|
#include <string>
|
|
9
10
|
#include <vector>
|
|
@@ -99,16 +100,47 @@ enum class Op {
|
|
|
99
100
|
// a = index into CompiledChunk::upvalues. At runtime, searches the live
|
|
100
101
|
// call stack (Evaluator::findUpvalue) for a still-active call of the
|
|
101
102
|
// upvalue's own target declaration and reads that call's own slot --
|
|
102
|
-
// undef if that call already returned
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
103
|
+
// undef if that call already returned. Never needs a STORE counterpart:
|
|
104
|
+
// a captured variable is always a parameter/let-binding of some
|
|
105
|
+
// ENCLOSING, already-executing call, and nothing this phase compiles
|
|
106
|
+
// ever writes back into an enclosing call's own slot (let/ListCompLet
|
|
107
|
+
// assignments always target a freshly-opened scope of their OWN, never
|
|
108
|
+
// a parent's).
|
|
109
|
+
//
|
|
110
|
+
// NOTE: as of Op::MakeClosure (below), no CompiledChunk that's ever
|
|
111
|
+
// actually registered (chunkCache_/literalChunkCache_) has a non-empty
|
|
112
|
+
// `upvalues` list any more -- a FunctionLiteral with any real capture
|
|
113
|
+
// need (direct or transitively bubbled up from a nested literal, see
|
|
114
|
+
// bytecode_compiler.cpp's FunctionLiteral case) is now handled via
|
|
115
|
+
// Op::MakeClosure instead, and always runs interpreted when invoked
|
|
116
|
+
// (Evaluator::lookupCompiledLiteralChunk finds no entry for it). This
|
|
117
|
+
// opcode and Evaluator::findUpvalue/CallStackFrame::upvalueParent are
|
|
118
|
+
// therefore unreachable dead code today, left in place rather than
|
|
119
|
+
// removed in the same change that made them unreachable -- deleting
|
|
120
|
+
// them is a distinct, lower-risk cleanup, not bundled in here.
|
|
110
121
|
LoadUpvalue,
|
|
111
122
|
|
|
123
|
+
// Escaping-closure support. a = index into CompiledChunk::closureSites.
|
|
124
|
+
// Builds a FRESH captured environment every time this instruction
|
|
125
|
+
// actually runs (never a compile-time constant -- a loop creating one
|
|
126
|
+
// closure per iteration must capture THAT iteration's own values, not
|
|
127
|
+
// share one snapshot) by reading each of the site's own captures
|
|
128
|
+
// straight out of the CURRENTLY EXECUTING frame's own `slots` (see
|
|
129
|
+
// ClosureSite's own doc comment for why every capture, however deeply
|
|
130
|
+
// the literal was originally nested, always resolves against the one
|
|
131
|
+
// chunk actually running Op::MakeClosure), and constructs
|
|
132
|
+
// Closure{node, capturedTrail} -- reusing Closure::capturedLet (value.hpp)
|
|
133
|
+
// and callCtxFor's existing capturedLet-rooting (user_calls.cpp)
|
|
134
|
+
// UNCHANGED: invoking this closure later (always via the interpreter,
|
|
135
|
+
// see ClosureSite's own doc comment for why its body is never itself
|
|
136
|
+
// compiled) works exactly like any interpreter-created escaping closure
|
|
137
|
+
// always has, no new invocation-side code needed at all. Replaces
|
|
138
|
+
// Op::PushConst's frozen `Closure{&n, nullptr}` specifically for a
|
|
139
|
+
// literal whose (transitively merged) capture set is non-empty; a
|
|
140
|
+
// literal that closes over nothing at all -- even transitively -- still
|
|
141
|
+
// takes the cheaper PushConst path, unchanged.
|
|
142
|
+
MakeClosure,
|
|
143
|
+
|
|
112
144
|
// A call whose callee isn't statically resolvable to a builtin or a
|
|
113
145
|
// named FunctionDeclaration (see CallFn) -- the callee expression is
|
|
114
146
|
// compiled to push its own Value first, THEN each argument (so at
|
|
@@ -224,10 +256,47 @@ struct CompiledChunk {
|
|
|
224
256
|
// inside AT COMPILE TIME (matched at runtime via Evaluator::
|
|
225
257
|
// findUpvalue's exact-identity search over the live call stack, the
|
|
226
258
|
// same identity CallStackFrame::declNode is stamped with), `slot` is
|
|
227
|
-
// that declaration's own slot number for the captured variable.
|
|
259
|
+
// that declaration's own slot number for the captured variable. `name`
|
|
260
|
+
// is that same variable's source name -- unused by Op::LoadUpvalue
|
|
261
|
+
// itself, needed by Op::MakeClosure's ClosureSite (below), which builds
|
|
262
|
+
// a NAME-keyed captured environment (Closure::capturedLet is a
|
|
263
|
+
// TrailView<Value>, looked up by name at invocation time, exactly like
|
|
264
|
+
// every other escaping closure) rather than a slot-indexed one.
|
|
228
265
|
struct UpvalueRef {
|
|
229
266
|
const oscad::ASTNode* targetDecl = nullptr;
|
|
230
267
|
int slot = 0;
|
|
268
|
+
std::string name;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// One FunctionLiteral's own capture list for Op::MakeClosure, computed
|
|
272
|
+
// at compile time (bytecode_compiler.cpp's FunctionLiteral case) as the
|
|
273
|
+
// TRANSITIVE union of: every free variable `node` itself references
|
|
274
|
+
// from outside its own body, PLUS -- for each FunctionLiteral nested
|
|
275
|
+
// anywhere within `node` (however deep) -- whatever THAT nested
|
|
276
|
+
// literal's own ClosureSite still needed from beyond `node`'s own scope
|
|
277
|
+
// (i.e. any capture whose resolution target isn't `node` itself). This
|
|
278
|
+
// "bubbling" is what makes a closure nested inside another compiled-
|
|
279
|
+
// creating closure resolve correctly even though the inner one's body
|
|
280
|
+
// is never itself compiled (see this struct's own body-compilation
|
|
281
|
+
// note below): by the time a real, running CompiledChunk executes
|
|
282
|
+
// Op::MakeClosure for `node`, every one of `captures`'s entries is
|
|
283
|
+
// guaranteed to resolve against THAT SAME running chunk's own `slots`
|
|
284
|
+
// array, regardless of how many literal-within-literal levels `node`
|
|
285
|
+
// was originally nested through in the source.
|
|
286
|
+
//
|
|
287
|
+
// `node`'s own BODY is deliberately never itself compiled/registered
|
|
288
|
+
// once it has any capture need (even an empty one bubbled up from
|
|
289
|
+
// something it contains) -- it always runs via the ordinary AST
|
|
290
|
+
// interpreter when invoked (Evaluator::lookupCompiledLiteralChunk finds
|
|
291
|
+
// no cache entry for it), using Closure::capturedLet exactly like any
|
|
292
|
+
// other escaping closure the interpreter creates directly. This keeps
|
|
293
|
+
// the whole feature to "make closure CREATION safe to compile"; a
|
|
294
|
+
// closure's own body additionally getting compiled too (its captures
|
|
295
|
+
// would need to read from this snapshot instead of a live-stack walk)
|
|
296
|
+
// is a natural follow-on, not attempted here.
|
|
297
|
+
struct ClosureSite {
|
|
298
|
+
const oscad::FunctionLiteral* node = nullptr;
|
|
299
|
+
std::vector<UpvalueRef> captures;
|
|
231
300
|
};
|
|
232
301
|
|
|
233
302
|
// One echo() expression form's own argument names, in source order --
|
|
@@ -245,6 +314,7 @@ struct CompiledChunk {
|
|
|
245
314
|
std::vector<std::string> names;
|
|
246
315
|
std::vector<CallSite> callSites;
|
|
247
316
|
std::vector<UpvalueRef> upvalues;
|
|
317
|
+
std::vector<ClosureSite> closureSites;
|
|
248
318
|
std::vector<EchoSite> echoSites;
|
|
249
319
|
int numSlots = 0;
|
|
250
320
|
// Count of distinct ListCompFor-assignment iterLists allocated across
|
|
@@ -252,6 +322,23 @@ struct CompiledChunk {
|
|
|
252
322
|
// sizes its own native iterLists/iterIndex scratch vectors to this.
|
|
253
323
|
int numIterLists = 0;
|
|
254
324
|
|
|
325
|
+
// Source-file/line span this chunk's own body+defaults were compiled
|
|
326
|
+
// from -- the min/max of every AST node's own position().line actually
|
|
327
|
+
// visited during compilation (see Compiler::trackSpan, bytecode_compiler.cpp),
|
|
328
|
+
// not just the declaration's own header line. Used by Evaluator::
|
|
329
|
+
// chunkEligibleNow (debug "fast continue" mode) to decide, per
|
|
330
|
+
// function, whether ANY currently-set breakpoint line could fall
|
|
331
|
+
// inside this chunk's own body -- a plain integer-range check, chosen
|
|
332
|
+
// specifically so that decision never needs the original source text
|
|
333
|
+
// or an offset->line conversion at eligibility-check time (called on
|
|
334
|
+
// every single compiled call, not just once). origin empty / minLine >
|
|
335
|
+
// maxLine (never updated) should not happen for any chunk that
|
|
336
|
+
// actually compiled a real body -- every function has at least one
|
|
337
|
+
// expression to visit.
|
|
338
|
+
std::string origin;
|
|
339
|
+
int minLine = std::numeric_limits<int>::max();
|
|
340
|
+
int maxLine = 0;
|
|
341
|
+
|
|
255
342
|
// FunctionLiteral chunks discovered while compiling THIS chunk (a
|
|
256
343
|
// FunctionLiteral expression appearing anywhere in its body/defaults),
|
|
257
344
|
// recursively containing their own nested literals in turn. The
|
|
@@ -315,6 +315,28 @@ public:
|
|
|
315
315
|
// the env var.
|
|
316
316
|
static void setBytecodeVmEnabledForTesting(std::optional<bool> enabled);
|
|
317
317
|
|
|
318
|
+
// Tells this Evaluator it's safe to speed up function calls with the
|
|
319
|
+
// bytecode VM even though a debugger is attached, PROVIDED the caller
|
|
320
|
+
// has already verified none of the reasons that's normally unsafe
|
|
321
|
+
// apply right now: no step command (`into`/`over`/`out`/`to_child`)
|
|
322
|
+
// pending, no explicit pause requested, and the session's own initial
|
|
323
|
+
// break-on-first stop already consumed. `breakpoints` is exactly the
|
|
324
|
+
// caller's own current breakpoint set (origin file -> line numbers) --
|
|
325
|
+
// see useBytecodeVm()/chunkEligibleNow's own doc comments for how it's
|
|
326
|
+
// used (a per-function span check, not a blanket switch: a function
|
|
327
|
+
// whose own body could contain one of these lines still always runs
|
|
328
|
+
// interpreted). Call with std::nullopt to go back to "no debugger
|
|
329
|
+
// exception at all" (today's original, always-safe behavior) --
|
|
330
|
+
// e.g. the instant a step command starts or a pause is requested.
|
|
331
|
+
// Cheap to call as often as the debugger's own state changes: this
|
|
332
|
+
// just updates one member, no cache invalidation needed (chunkEligibleNow
|
|
333
|
+
// is re-checked on every lookupOrCompileChunk/lookupCompiledLiteralChunk
|
|
334
|
+
// call, not cached itself -- only the compiled bytecode ITSELF, which
|
|
335
|
+
// never depends on debugger state, is cached forever).
|
|
336
|
+
void setFastContinueBreakpoints(std::optional<std::unordered_map<std::string, std::set<int>>> breakpoints) {
|
|
337
|
+
fastContinueBreakpoints_ = std::move(breakpoints);
|
|
338
|
+
}
|
|
339
|
+
|
|
318
340
|
// Checks whether a debug pause should happen at `node` (via the
|
|
319
341
|
// injected DebugHooks::debugHook, if any -- a no-op otherwise),
|
|
320
342
|
// applying any `mods` the hook returns to `ctx.let_` and throwing
|
|
@@ -815,7 +837,46 @@ private:
|
|
|
815
837
|
// therefore always takes the interpreter path, where the port matches
|
|
816
838
|
// the reference's _check_debug placement exactly. Costs nothing when
|
|
817
839
|
// no debugger is attached, which is every non-debug render.
|
|
818
|
-
|
|
840
|
+
//
|
|
841
|
+
// EXCEPT: a caller that knows none of that applies right now -- no
|
|
842
|
+
// step command pending, no explicit pause requested, the session's
|
|
843
|
+
// own initial break-on-first stop already consumed -- can call
|
|
844
|
+
// setFastContinueBreakpoints() with the CURRENT breakpoint set instead
|
|
845
|
+
// of leaving it unset. That alone doesn't turn the VM on for
|
|
846
|
+
// everything; see chunkEligibleNow, which still forces the
|
|
847
|
+
// interpreter for any specific function whose own compiled span could
|
|
848
|
+
// contain one of those lines. This only ever helps a plain "Continue,
|
|
849
|
+
// pause only at a known breakpoint" -- the far more common case in a
|
|
850
|
+
// long debug session than active single-stepping, and exactly the
|
|
851
|
+
// case where "every function pays the interpreter tax because a
|
|
852
|
+
// debugger happens to be attached somewhere" wastes the most time
|
|
853
|
+
// (see docs/debugger.md and this method's own callers for the full
|
|
854
|
+
// story).
|
|
855
|
+
bool useBytecodeVm() const {
|
|
856
|
+
return bytecodeVmEnabled() && (!debugHooks_.debugHook || fastContinueBreakpoints_.has_value());
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// The fine-grained half of the check above: even when useBytecodeVm()
|
|
860
|
+
// says compiling/using bytecode is on the table at all, a SPECIFIC
|
|
861
|
+
// chunk is only actually safe to run compiled if nothing about it
|
|
862
|
+
// could matter to the debugger right now. No debugger attached at
|
|
863
|
+
// all -- always fine, unaffected by any of this. Debugger attached: only
|
|
864
|
+
// safe in fast-continue mode (useBytecodeVm() already checked that),
|
|
865
|
+
// and only if the chunk's own [minLine, maxLine] span (see
|
|
866
|
+
// CompiledChunk's own doc comment, bytecode.hpp) contains none of the
|
|
867
|
+
// breakpoint lines currently set for its origin file -- a checkpoint
|
|
868
|
+
// for one of those lines could fire mid-body, and compiled code has no
|
|
869
|
+
// way to stop there.
|
|
870
|
+
bool chunkEligibleNow(const CompiledChunk& chunk) const {
|
|
871
|
+
if (!debugHooks_.debugHook) return true;
|
|
872
|
+
if (!fastContinueBreakpoints_) return false;
|
|
873
|
+
auto it = fastContinueBreakpoints_->find(chunk.origin);
|
|
874
|
+
if (it == fastContinueBreakpoints_->end()) return true;
|
|
875
|
+
for (int line : it->second) {
|
|
876
|
+
if (line >= chunk.minLine && line <= chunk.maxLine) return false;
|
|
877
|
+
}
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
819
880
|
|
|
820
881
|
// -- Profiling (Phase 9) --------------------------------------------
|
|
821
882
|
|
|
@@ -886,6 +947,11 @@ private:
|
|
|
886
947
|
// -- Debugging (Phase 9) --------------------------------------------
|
|
887
948
|
|
|
888
949
|
DebugHooks debugHooks_;
|
|
950
|
+
// See setFastContinueBreakpoints's own doc comment. nullopt (the
|
|
951
|
+
// default, and what a plain debugger attach without ever calling that
|
|
952
|
+
// setter leaves it at) means "no fast-continue exception" -- exactly
|
|
953
|
+
// today's original, always-safe behavior.
|
|
954
|
+
std::optional<std::unordered_map<std::string, std::set<int>>> fastContinueBreakpoints_;
|
|
889
955
|
// The root EvalContext resolveTree() was called with -- lets
|
|
890
956
|
// checkDebug()'s locals snapshot fall back to top-level script
|
|
891
957
|
// variables when paused inside a nested user call, the same way the
|
|
@@ -40,6 +40,33 @@ bool isListCompClauseKind(oscad::NodeKind kind) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// Merges `site`'s own captures into `out` for every entry that escapes
|
|
44
|
+
// `owner` itself (targetDecl != owner) -- see ClosureSite's own doc comment
|
|
45
|
+
// (bytecode.hpp) for why this "bubbling" is necessary: a literal nested
|
|
46
|
+
// inside `owner` may need something from further out than `owner`'s own
|
|
47
|
+
// scope, and since that inner literal's body is never itself compiled
|
|
48
|
+
// (only ever invoked via the interpreter once it has any capture need),
|
|
49
|
+
// `owner`'s OWN Op::MakeClosure must snapshot that name too -- otherwise
|
|
50
|
+
// the inner literal's interpreter-resolved capturedLet ancestry, rooted at
|
|
51
|
+
// whatever `owner` itself captured, would simply never contain it.
|
|
52
|
+
// Dedupes by (targetDecl, slot): the same enclosing binding reached through
|
|
53
|
+
// two different nested literals (or the same literal referencing it twice)
|
|
54
|
+
// is still one capture to make.
|
|
55
|
+
void bubbleEscapingCaptures(const CompiledChunk::ClosureSite& site, const oscad::ASTNode* owner,
|
|
56
|
+
std::vector<CompiledChunk::UpvalueRef>& out) {
|
|
57
|
+
for (const auto& cap : site.captures) {
|
|
58
|
+
if (cap.targetDecl == owner) continue;
|
|
59
|
+
bool already = false;
|
|
60
|
+
for (const auto& existing : out) {
|
|
61
|
+
if (existing.targetDecl == cap.targetDecl && existing.slot == cap.slot) {
|
|
62
|
+
already = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!already) out.push_back(cap);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
43
70
|
// Compile-time name -> slot resolution, mirroring the LetOp/nested-scope
|
|
44
71
|
// shadowing rules callCtx()/letChildCtx() apply at runtime: one frame per
|
|
45
72
|
// LetOp (pushed/popped around its own assignments+body), innermost frame
|
|
@@ -128,6 +155,20 @@ public:
|
|
|
128
155
|
return slot;
|
|
129
156
|
}
|
|
130
157
|
|
|
158
|
+
// Widens chunk_'s own [minLine, maxLine] span to include `node` --
|
|
159
|
+
// called at the top of every AST-node-visiting entry point
|
|
160
|
+
// (compileExpr/compileListElement/compileListCompBody) so the chunk's
|
|
161
|
+
// final span covers every line actually compiled into it, not just the
|
|
162
|
+
// declaration's own header line. See CompiledChunk::origin/minLine/
|
|
163
|
+
// maxLine's own doc comment (bytecode.hpp) for why this matters --
|
|
164
|
+
// Evaluator::chunkEligibleNow's debug "fast continue" check.
|
|
165
|
+
void trackSpan(const oscad::ASTNode& node) {
|
|
166
|
+
const oscad::Position& pos = node.position();
|
|
167
|
+
chunk_.origin = pos.origin;
|
|
168
|
+
if (pos.line < chunk_.minLine) chunk_.minLine = pos.line;
|
|
169
|
+
if (pos.line > chunk_.maxLine) chunk_.maxLine = pos.line;
|
|
170
|
+
}
|
|
171
|
+
|
|
131
172
|
// Walks `enclosing_` from innermost (back()) to outermost, looking for
|
|
132
173
|
// a level whose own CompileScope resolves `name` -- the first (most
|
|
133
174
|
// deeply nested, i.e. most recently active at runtime) match wins,
|
|
@@ -135,7 +176,7 @@ public:
|
|
|
135
176
|
// naturally resolve to the innermost active binding too.
|
|
136
177
|
std::optional<CompiledChunk::UpvalueRef> resolveEnclosing(const std::string& name) const {
|
|
137
178
|
for (auto it = enclosing_.rbegin(); it != enclosing_.rend(); ++it) {
|
|
138
|
-
if (auto slot = it->scope->resolve(name)) return CompiledChunk::UpvalueRef{it->decl, *slot};
|
|
179
|
+
if (auto slot = it->scope->resolve(name)) return CompiledChunk::UpvalueRef{it->decl, *slot, name};
|
|
139
180
|
}
|
|
140
181
|
return std::nullopt;
|
|
141
182
|
}
|
|
@@ -171,6 +212,7 @@ public:
|
|
|
171
212
|
void compileExpr(const oscad::Expression& node, std::vector<Instruction>& out, CompileScope& scope,
|
|
172
213
|
bool tail = false) {
|
|
173
214
|
using oscad::NodeKind;
|
|
215
|
+
trackSpan(node);
|
|
174
216
|
switch (node.kind()) {
|
|
175
217
|
case NodeKind::NumberLiteral:
|
|
176
218
|
out.push_back({Op::PushConst, internConst(Value{static_cast<const oscad::NumberLiteral&>(node).val}), 0,
|
|
@@ -238,20 +280,14 @@ public:
|
|
|
238
280
|
// other point where its own free-variable references could be
|
|
239
281
|
// resolved against this enclosing chain (AST nodes have no
|
|
240
282
|
// parent pointers to discover it from later, see this file's
|
|
241
|
-
// own module notes). If the literal itself fails to compile
|
|
242
|
-
// (contains
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
// ctx.let_) locals otherwise. On success, the literal's own
|
|
250
|
-
// chunk is stashed in chunk_.nestedLiterals for
|
|
251
|
-
// Evaluator::lookupOrCompileChunk to flatten into
|
|
252
|
-
// literalChunkCache_; the emitted instruction just pushes the
|
|
253
|
-
// AST pointer as a Value, exactly like the interpreter's own
|
|
254
|
-
// `Value{&node}` (evalExpr's own FunctionLiteral case).
|
|
283
|
+
// own module notes). If the literal itself fails to compile as
|
|
284
|
+
// bytecode at all (contains an echo/assert/unsupported
|
|
285
|
+
// construct), the WHOLE containing declaration must also bail
|
|
286
|
+
// -- propagated by simply rethrowing -- since there's no lighter
|
|
287
|
+
// way left to find out what it captures (see
|
|
288
|
+
// bubbleEscapingCaptures' own doc comment for why a literal
|
|
289
|
+
// that DOES compile as bytecode no longer needs this bail just
|
|
290
|
+
// for referencing enclosing state).
|
|
255
291
|
case NodeKind::FunctionLiteral: {
|
|
256
292
|
auto& n = static_cast<const oscad::FunctionLiteral&>(node);
|
|
257
293
|
std::vector<EnclosingLevel> childEnclosing = enclosing_;
|
|
@@ -261,33 +297,44 @@ public:
|
|
|
261
297
|
*n.body)) {
|
|
262
298
|
throw NotCompilable{};
|
|
263
299
|
}
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
300
|
+
// Effective capture set: `n`'s own direct free-variable
|
|
301
|
+
// references, plus -- transitively -- whatever any literal
|
|
302
|
+
// nested inside `n` still needs from beyond `n`'s own scope
|
|
303
|
+
// (see bubbleEscapingCaptures' own doc comment, above, and
|
|
304
|
+
// ClosureSite's, bytecode.hpp).
|
|
305
|
+
std::vector<CompiledChunk::UpvalueRef> effectiveCaptures = literalChunk.upvalues;
|
|
306
|
+
for (const auto& nestedSite : literalChunk.closureSites) {
|
|
307
|
+
bubbleEscapingCaptures(nestedSite, &n, effectiveCaptures);
|
|
308
|
+
}
|
|
309
|
+
if (effectiveCaptures.empty()) {
|
|
310
|
+
// Closes over nothing at all, even transitively -- the
|
|
311
|
+
// existing fast path: a single frozen constant works
|
|
312
|
+
// for every invocation, and this literal's own compiled
|
|
313
|
+
// chunk (possibly containing its OWN MakeClosure sites
|
|
314
|
+
// for anything nested inside IT that closes only over
|
|
315
|
+
// ITS OWN scope) is genuinely usable, registered as
|
|
316
|
+
// before.
|
|
317
|
+
chunk_.nestedLiterals.emplace_back(&n, std::move(literalChunk));
|
|
318
|
+
out.push_back(
|
|
319
|
+
{Op::PushConst, internConst(Value{std::make_shared<const Closure>(Closure{&n, nullptr})}), 0,
|
|
320
|
+
nullptr});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// Escaping-closure support (Op::MakeClosure, bytecode.hpp).
|
|
324
|
+
// `literalChunk`'s own bytecode is discarded here -- its own
|
|
325
|
+
// upvalue reads would resolve via the live-call-stack walk
|
|
326
|
+
// (Op::LoadUpvalue), which is wrong for a closure that
|
|
327
|
+
// outlives its creator -- so this literal always runs
|
|
328
|
+
// interpreted when invoked instead (Evaluator::
|
|
329
|
+
// lookupCompiledLiteralChunk finds no cache entry for it),
|
|
330
|
+
// using the exact same Closure::capturedLet machinery every
|
|
331
|
+
// other escaping closure the interpreter creates directly
|
|
332
|
+
// already relies on. Only `effectiveCaptures` (the names/
|
|
333
|
+
// slots to snapshot right now) survives from the discarded
|
|
334
|
+
// compile attempt.
|
|
335
|
+
int siteIdx = static_cast<int>(chunk_.closureSites.size());
|
|
336
|
+
chunk_.closureSites.push_back(CompiledChunk::ClosureSite{&n, std::move(effectiveCaptures)});
|
|
337
|
+
out.push_back({Op::MakeClosure, siteIdx, 0, nullptr});
|
|
291
338
|
return;
|
|
292
339
|
}
|
|
293
340
|
case NodeKind::RangeLiteral: {
|
|
@@ -609,6 +656,7 @@ public:
|
|
|
609
656
|
// list too, when reached via a clause's own nested body.
|
|
610
657
|
void compileListElement(const oscad::ASTNode& elem, std::vector<Instruction>& out, CompileScope& scope) {
|
|
611
658
|
using oscad::NodeKind;
|
|
659
|
+
trackSpan(elem);
|
|
612
660
|
switch (elem.kind()) {
|
|
613
661
|
case NodeKind::ListCompFor: {
|
|
614
662
|
auto& n = static_cast<const oscad::ListCompFor&>(elem);
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
5
5
|
#include "openscad_cpp_evaluator/function_builtins.hpp"
|
|
6
6
|
#include "openscad_cpp_evaluator/import_builtin.hpp"
|
|
7
|
+
#include "openscad_cpp_evaluator/scope_trail.hpp"
|
|
7
8
|
|
|
8
9
|
#include "openscad_cpp_parser/ast/declarations.hpp"
|
|
9
10
|
|
|
@@ -132,6 +133,33 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
132
133
|
++pc;
|
|
133
134
|
break;
|
|
134
135
|
}
|
|
136
|
+
case Op::MakeClosure: {
|
|
137
|
+
// Builds a FRESH captured environment every time this
|
|
138
|
+
// instruction actually runs (never a compile-time constant
|
|
139
|
+
// -- a loop creating one closure per iteration must capture
|
|
140
|
+
// THAT iteration's own values, not share one snapshot) by
|
|
141
|
+
// reading each of the site's own captures straight out of
|
|
142
|
+
// THIS frame's own `slots` -- see ClosureSite's own doc
|
|
143
|
+
// comment (bytecode.hpp) for why every capture, however
|
|
144
|
+
// deeply the literal was originally nested, is guaranteed
|
|
145
|
+
// to resolve against this SAME currently-running chunk.
|
|
146
|
+
// The resulting Closure::capturedLet is a real, standalone
|
|
147
|
+
// TrailView (isolated root -- nothing else shares it, and
|
|
148
|
+
// it owes nothing to `ctx.let_`, which a compiled call
|
|
149
|
+
// never populates -- see bindCompiledArgs' own doc
|
|
150
|
+
// comment), so invoking this closure later works through
|
|
151
|
+
// the EXACT same callCtxFor/capturedLetTrail machinery
|
|
152
|
+
// every interpreter-created escaping closure already uses,
|
|
153
|
+
// unchanged.
|
|
154
|
+
const CompiledChunk::ClosureSite& site = chunk.closureSites[static_cast<size_t>(ins.a)];
|
|
155
|
+
auto capturedTrail = TrailView<Value>::makeRoot();
|
|
156
|
+
for (const auto& cap : site.captures) {
|
|
157
|
+
capturedTrail->set(cap.name, slots[static_cast<size_t>(cap.slot)]);
|
|
158
|
+
}
|
|
159
|
+
stack.push_back(Value{std::make_shared<const Closure>(Closure{site.node, std::move(capturedTrail)})});
|
|
160
|
+
++pc;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
135
163
|
case Op::Range: {
|
|
136
164
|
Value step = std::move(stack.back());
|
|
137
165
|
stack.pop_back();
|
|
@@ -14,12 +14,20 @@ const CompiledChunk* Evaluator::lookupOrCompileChunk(const oscad::FunctionDeclar
|
|
|
14
14
|
it = chunkCache_.emplace(&decl, tryCompileFunction(decl)).first;
|
|
15
15
|
if (it->second) flattenNestedLiterals(*it->second);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
if (!it->second) return nullptr;
|
|
18
|
+
// Compilation itself is cached forever above (never depends on
|
|
19
|
+
// debugger state); ELIGIBILITY to actually use that compiled chunk
|
|
20
|
+
// right now is re-checked every call instead -- see chunkEligibleNow's
|
|
21
|
+
// own doc comment for why that's still cheap (no debugger: one bool
|
|
22
|
+
// check; fast-continue mode: a handful of integer comparisons against
|
|
23
|
+
// whatever breakpoints happen to be set right now).
|
|
24
|
+
return chunkEligibleNow(*it->second) ? &*it->second : nullptr;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
const CompiledChunk* Evaluator::lookupCompiledLiteralChunk(const oscad::FunctionLiteral& node) const {
|
|
21
28
|
auto it = literalChunkCache_.find(&node);
|
|
22
|
-
|
|
29
|
+
if (it == literalChunkCache_.end()) return nullptr;
|
|
30
|
+
return chunkEligibleNow(it->second) ? &it->second : nullptr;
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
void Evaluator::flattenNestedLiterals(CompiledChunk& chunk) {
|