openscad-cpp-evaluator 0.4.9__tar.gz → 0.5.1__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.9 → openscad_cpp_evaluator-0.5.1}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/bindings/module.cpp +55 -3
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/bytecode.hpp +18 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/evaluator.hpp +67 -1
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/bytecode_compiler.cpp +37 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/bytecode_vm.cpp +12 -1
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/user_calls.cpp +10 -2
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_bytecode_compiler.cpp +160 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_python_bindings.py +4 -4
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/README.md +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/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>
|
|
@@ -252,6 +253,23 @@ struct CompiledChunk {
|
|
|
252
253
|
// sizes its own native iterLists/iterIndex scratch vectors to this.
|
|
253
254
|
int numIterLists = 0;
|
|
254
255
|
|
|
256
|
+
// Source-file/line span this chunk's own body+defaults were compiled
|
|
257
|
+
// from -- the min/max of every AST node's own position().line actually
|
|
258
|
+
// visited during compilation (see Compiler::trackSpan, bytecode_compiler.cpp),
|
|
259
|
+
// not just the declaration's own header line. Used by Evaluator::
|
|
260
|
+
// chunkEligibleNow (debug "fast continue" mode) to decide, per
|
|
261
|
+
// function, whether ANY currently-set breakpoint line could fall
|
|
262
|
+
// inside this chunk's own body -- a plain integer-range check, chosen
|
|
263
|
+
// specifically so that decision never needs the original source text
|
|
264
|
+
// or an offset->line conversion at eligibility-check time (called on
|
|
265
|
+
// every single compiled call, not just once). origin empty / minLine >
|
|
266
|
+
// maxLine (never updated) should not happen for any chunk that
|
|
267
|
+
// actually compiled a real body -- every function has at least one
|
|
268
|
+
// expression to visit.
|
|
269
|
+
std::string origin;
|
|
270
|
+
int minLine = std::numeric_limits<int>::max();
|
|
271
|
+
int maxLine = 0;
|
|
272
|
+
|
|
255
273
|
// FunctionLiteral chunks discovered while compiling THIS chunk (a
|
|
256
274
|
// FunctionLiteral expression appearing anywhere in its body/defaults),
|
|
257
275
|
// 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
|
|
@@ -128,6 +128,20 @@ public:
|
|
|
128
128
|
return slot;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// Widens chunk_'s own [minLine, maxLine] span to include `node` --
|
|
132
|
+
// called at the top of every AST-node-visiting entry point
|
|
133
|
+
// (compileExpr/compileListElement/compileListCompBody) so the chunk's
|
|
134
|
+
// final span covers every line actually compiled into it, not just the
|
|
135
|
+
// declaration's own header line. See CompiledChunk::origin/minLine/
|
|
136
|
+
// maxLine's own doc comment (bytecode.hpp) for why this matters --
|
|
137
|
+
// Evaluator::chunkEligibleNow's debug "fast continue" check.
|
|
138
|
+
void trackSpan(const oscad::ASTNode& node) {
|
|
139
|
+
const oscad::Position& pos = node.position();
|
|
140
|
+
chunk_.origin = pos.origin;
|
|
141
|
+
if (pos.line < chunk_.minLine) chunk_.minLine = pos.line;
|
|
142
|
+
if (pos.line > chunk_.maxLine) chunk_.maxLine = pos.line;
|
|
143
|
+
}
|
|
144
|
+
|
|
131
145
|
// Walks `enclosing_` from innermost (back()) to outermost, looking for
|
|
132
146
|
// a level whose own CompileScope resolves `name` -- the first (most
|
|
133
147
|
// deeply nested, i.e. most recently active at runtime) match wins,
|
|
@@ -171,6 +185,7 @@ public:
|
|
|
171
185
|
void compileExpr(const oscad::Expression& node, std::vector<Instruction>& out, CompileScope& scope,
|
|
172
186
|
bool tail = false) {
|
|
173
187
|
using oscad::NodeKind;
|
|
188
|
+
trackSpan(node);
|
|
174
189
|
switch (node.kind()) {
|
|
175
190
|
case NodeKind::NumberLiteral:
|
|
176
191
|
out.push_back({Op::PushConst, internConst(Value{static_cast<const oscad::NumberLiteral&>(node).val}), 0,
|
|
@@ -609,6 +624,7 @@ public:
|
|
|
609
624
|
// list too, when reached via a clause's own nested body.
|
|
610
625
|
void compileListElement(const oscad::ASTNode& elem, std::vector<Instruction>& out, CompileScope& scope) {
|
|
611
626
|
using oscad::NodeKind;
|
|
627
|
+
trackSpan(elem);
|
|
612
628
|
switch (elem.kind()) {
|
|
613
629
|
case NodeKind::ListCompFor: {
|
|
614
630
|
auto& n = static_cast<const oscad::ListCompFor&>(elem);
|
|
@@ -676,6 +692,27 @@ public:
|
|
|
676
692
|
int slot = declareLocal(scope, assign->name->name);
|
|
677
693
|
out.push_back({Op::StoreLocal, slot, 0, &assign->position()});
|
|
678
694
|
}
|
|
695
|
+
// Pre-declare every incr-list name NOW, before compiling the
|
|
696
|
+
// condition/body/incr expressions below -- an incr name
|
|
697
|
+
// introduced fresh (not in the init list, e.g. BOSL2's
|
|
698
|
+
// skin.scad: `best_i = result[0]<bestcost ? i : best_i,`)
|
|
699
|
+
// can be READ, including by its OWN incr assignment's RHS
|
|
700
|
+
// (a self-reference to its prior iteration's value) or by
|
|
701
|
+
// the loop body, before its OWN StoreLocal below would
|
|
702
|
+
// otherwise have declared it. Without this, that read
|
|
703
|
+
// compiles to Op::LoadFree (the "not a known local/upvalue/
|
|
704
|
+
// dyn-var, might be dynamic, warn if truly missing"
|
|
705
|
+
// fallback) -- permanently, since compilation happens once
|
|
706
|
+
// but the resulting instruction runs every iteration: every
|
|
707
|
+
// read silently misses the real value stored into the
|
|
708
|
+
// local slot moments later at runtime, not just an extra
|
|
709
|
+
// warning but a genuinely wrong result (confirmed: BOSL2's
|
|
710
|
+
// own best_i accumulator came back undef on every read).
|
|
711
|
+
// Declaring the slot here first means the SAME reads below
|
|
712
|
+
// resolve as ordinary Op::LoadLocal instead.
|
|
713
|
+
for (const auto& assign : n.incrs) {
|
|
714
|
+
if (!scope.resolve(assign->name->name)) declareLocal(scope, assign->name->name);
|
|
715
|
+
}
|
|
679
716
|
int counterSlot = nextSlot_++;
|
|
680
717
|
out.push_back({Op::PushConst, internConst(Value{0.0}), 0, nullptr});
|
|
681
718
|
out.push_back({Op::StoreLocal, counterSlot, 0, nullptr});
|
|
@@ -40,6 +40,16 @@ private:
|
|
|
40
40
|
struct IterList {
|
|
41
41
|
IterableValues values;
|
|
42
42
|
size_t index = 0;
|
|
43
|
+
// Cached once at IterMaterialize time -- IterableValues::size() is O(1)
|
|
44
|
+
// for a list/owned vector, but walks a RANGE's whole sequence from
|
|
45
|
+
// scratch every call (its own doc comment: "no caller in this codebase
|
|
46
|
+
// actually calls size() on a range today" -- Op::IterNext, below, used
|
|
47
|
+
// to be exactly that stale caller). Recomputing it once per IterNext
|
|
48
|
+
// call turned a single range-based for()/list-comp `for` into an O(n^2)
|
|
49
|
+
// walk (a 100,000-element range: interpreter ~0.03s, VM ~9.5s) -- caught
|
|
50
|
+
// via a BOSL2 corpus sweep (test_math.scadtest's own gaussian_rands(),
|
|
51
|
+
// which builds a 100,000-element list via `count()`).
|
|
52
|
+
size_t total = 0;
|
|
43
53
|
};
|
|
44
54
|
|
|
45
55
|
// Shared by Op::CallFn's isBuiltin (evalBuiltinFunction) and isImport
|
|
@@ -244,6 +254,7 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
244
254
|
ev.warn("Bad range parameter in for statement: too many elements (" + std::to_string(count) + ")", ins.pos);
|
|
245
255
|
});
|
|
246
256
|
il.index = 0;
|
|
257
|
+
il.total = il.values.size(); // once here, never per-iteration -- see IterList's own doc comment
|
|
247
258
|
++pc;
|
|
248
259
|
break;
|
|
249
260
|
}
|
|
@@ -254,7 +265,7 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
254
265
|
}
|
|
255
266
|
case Op::IterNext: {
|
|
256
267
|
IterList& il = iterLists[static_cast<size_t>(ins.b)];
|
|
257
|
-
if (il.index < il.
|
|
268
|
+
if (il.index < il.total) {
|
|
258
269
|
slots[static_cast<size_t>(ins.a)] = il.values[il.index];
|
|
259
270
|
++il.index;
|
|
260
271
|
++pc;
|
|
@@ -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) {
|
{openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/tests/test_bytecode_compiler.cpp
RENAMED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
#include "test_helpers.hpp"
|
|
6
6
|
|
|
7
|
+
#include <chrono>
|
|
7
8
|
#include <filesystem>
|
|
8
9
|
#include <fstream>
|
|
9
10
|
#include <gtest/gtest.h>
|
|
@@ -48,6 +49,33 @@ std::string runCapturingEcho(const std::string& code) {
|
|
|
48
49
|
return captured;
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
// Counts total debugHook invocations (statement- and expr-level combined)
|
|
53
|
+
// running `code` with a debugger attached. A VM-compiled function call
|
|
54
|
+
// gets exactly ONE stop -- evalUserFunctionCore's own unconditional
|
|
55
|
+
// body-entry checkDebug(), the only checkpoint compiled bytecode has left
|
|
56
|
+
// (see useBytecodeVm()/chunkEligibleNow's own doc comments, evaluator.hpp)
|
|
57
|
+
// -- while an interpreted call additionally hits every sub-expression
|
|
58
|
+
// checkpoint inside it (ternary condition + chosen branch, at minimum, for
|
|
59
|
+
// the functions these tests use). That gap is the only observable,
|
|
60
|
+
// black-box signal these tests have for "did this call actually run
|
|
61
|
+
// compiled or interpreted" -- there's no other public introspection point.
|
|
62
|
+
int countDebugHookStops(const std::string& code, std::optional<std::unordered_map<std::string, std::set<int>>> fastContinueBreakpoints) {
|
|
63
|
+
int stops = 0;
|
|
64
|
+
DebugHooks hooks;
|
|
65
|
+
hooks.debugHook = [&](int, int, bool, bool, const std::string&, const std::vector<CallStackFrame>&,
|
|
66
|
+
const DebugFramesFn&) {
|
|
67
|
+
++stops;
|
|
68
|
+
return DebugAction{};
|
|
69
|
+
};
|
|
70
|
+
Evaluator ev(EchoFn{}, nullptr, nullptr, hooks);
|
|
71
|
+
if (fastContinueBreakpoints) ev.setFastContinueBreakpoints(std::move(fastContinueBreakpoints));
|
|
72
|
+
auto ast = parseSrc(code);
|
|
73
|
+
auto scope = oscad::buildScopes(ast);
|
|
74
|
+
EvalContext ctx = EvalContext::makeRoot(scope.get());
|
|
75
|
+
ev.resolveTree(ast, ctx);
|
|
76
|
+
return stops;
|
|
77
|
+
}
|
|
78
|
+
|
|
51
79
|
std::filesystem::path tempPath(const std::string& name) {
|
|
52
80
|
return std::filesystem::temp_directory_path() / ("oscad_bytecode_compiler_test_" + name);
|
|
53
81
|
}
|
|
@@ -721,3 +749,135 @@ TEST(BytecodeCompiler, VmOffAndVmOnAgreeOnEchoAssertImportObjectCases) {
|
|
|
721
749
|
EXPECT_EQ(offResult, onResult);
|
|
722
750
|
std::filesystem::remove(path);
|
|
723
751
|
}
|
|
752
|
+
|
|
753
|
+
TEST(BytecodeCompiler, RangeBasedForListCompDoesNotReMaterializeRangeSizePerIteration) {
|
|
754
|
+
ScopedVm vm(true);
|
|
755
|
+
// Op::IterNext used to bound-check via `il.index < il.values.size()`
|
|
756
|
+
// every single iteration. IterableValues::size() is O(1) for a
|
|
757
|
+
// materialized list, but for a RANGE it walks the whole sequence from
|
|
758
|
+
// scratch (its own doc comment already flagged this as a stale
|
|
759
|
+
// assumption: "no caller in this codebase actually calls size() on a
|
|
760
|
+
// range today" -- Op::IterNext was exactly that caller). Recomputing an
|
|
761
|
+
// O(n) size every one of n iterations is O(n^2) overall -- a
|
|
762
|
+
// 100,000-element range list comprehension took ~9.5s compiled vs
|
|
763
|
+
// ~0.03s interpreted before the fix (caught via a BOSL2 corpus sweep,
|
|
764
|
+
// test_math.scadtest's gaussian_rands()). Fixed by caching the size
|
|
765
|
+
// once at IterMaterialize time (IterList::total). 200,000 elements
|
|
766
|
+
// finishes in well under a second now; the old behavior would have
|
|
767
|
+
// taken tens of seconds, so a generous 5s bound cleanly separates
|
|
768
|
+
// "fixed" from "regressed" without making this test itself slow.
|
|
769
|
+
const auto start = std::chrono::steady_clock::now();
|
|
770
|
+
EXPECT_EQ(runCapturingEcho("function myrange(n) = [for (i=[0:1:n-1]) i];\n"
|
|
771
|
+
"r = myrange(200000);\n"
|
|
772
|
+
"echo(len(r));"),
|
|
773
|
+
"ECHO: 200000");
|
|
774
|
+
const auto elapsed = std::chrono::steady_clock::now() - start;
|
|
775
|
+
EXPECT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(), 5000);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// -- Debug "fast continue" per-function VM gating --------------------------
|
|
779
|
+
//
|
|
780
|
+
// A debugger attached AND actively single-stepping needs every checkDebug()
|
|
781
|
+
// checkpoint the interpreter provides (a compiled function has none for a
|
|
782
|
+
// step to land on) -- that's the pre-existing, always-safe, never-changed
|
|
783
|
+
// behavior when Evaluator::setFastContinueBreakpoints is never called at
|
|
784
|
+
// all (fastContinueBreakpoints_ stays nullopt). But a plain "Continue,
|
|
785
|
+
// pause only at a known breakpoint line" doesn't need that: a function
|
|
786
|
+
// whose own compiled span contains none of the currently-set breakpoint
|
|
787
|
+
// lines can safely run on the VM, since nothing inside it could possibly
|
|
788
|
+
// need a checkpoint right now.
|
|
789
|
+
|
|
790
|
+
// Expected stop counts below are empirically verified (via a temporary
|
|
791
|
+
// direct instrumentation pass through lookupOrCompileChunk/evalUserFunction,
|
|
792
|
+
// not guessed): calling `f(5)` for `function f(x) = x > 0 ? x + 1 : x - 1;`
|
|
793
|
+
// as the lone argument of a top-level `echo(...)` produces 3 checkDebug
|
|
794
|
+
// stops when f runs compiled (the top-level echo statement's own stop,
|
|
795
|
+
// f's call-site stop from evalFunctionCall, and evalUserFunctionCore's
|
|
796
|
+
// unconditional body-entry stop -- no others, since compiled bytecode has
|
|
797
|
+
// no sub-expression checkpoints) versus 5 when f runs interpreted (those
|
|
798
|
+
// same 3, plus the ternary's own condition-check and chosen-branch-entry
|
|
799
|
+
// sub-expression stops). A FunctionDeclaration statement itself never adds
|
|
800
|
+
// its own top-level stop (evalChildren's per-statement checkpoint skips
|
|
801
|
+
// declarations, mirroring $children's own count -- see
|
|
802
|
+
// Evaluator::evalUserModule's childrenCount loop), so adding a second,
|
|
803
|
+
// uncalled function declaration to a script doesn't change either count.
|
|
804
|
+
|
|
805
|
+
TEST(BytecodeCompiler, DebugAttachedWithoutFastContinueAlwaysInterprets) {
|
|
806
|
+
ScopedVm vm(true);
|
|
807
|
+
// No setFastContinueBreakpoints call at all (nullopt, the default):
|
|
808
|
+
// must behave exactly as before this feature existed -- always
|
|
809
|
+
// interpreted -- regardless of the function's own line having no
|
|
810
|
+
// breakpoint anywhere near it.
|
|
811
|
+
const int stops = countDebugHookStops("function f(x) = x > 0 ? x + 1 : x - 1;\n"
|
|
812
|
+
"echo(f(5));",
|
|
813
|
+
std::nullopt);
|
|
814
|
+
EXPECT_EQ(stops, 5);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
TEST(BytecodeCompiler, FastContinueWithNoBreakpointInFunctionUsesVm) {
|
|
818
|
+
ScopedVm vm(true);
|
|
819
|
+
// Breakpoint set, but only on line 2 (the echo statement) -- f's own
|
|
820
|
+
// body is entirely on line 1, outside that set, so it's eligible to
|
|
821
|
+
// run compiled.
|
|
822
|
+
const int stops = countDebugHookStops("function f(x) = x > 0 ? x + 1 : x - 1;\n"
|
|
823
|
+
"echo(f(5));",
|
|
824
|
+
std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
|
|
825
|
+
EXPECT_EQ(stops, 3);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
TEST(BytecodeCompiler, FastContinueWithBreakpointInsideFunctionStillInterprets) {
|
|
829
|
+
ScopedVm vm(true);
|
|
830
|
+
// Breakpoint on line 1 itself -- inside f's own compiled span -- must
|
|
831
|
+
// still force the interpreter for f specifically, even in fast-continue
|
|
832
|
+
// mode: something on that exact line could need a real checkpoint.
|
|
833
|
+
const int stops = countDebugHookStops("function f(x) = x > 0 ? x + 1 : x - 1;\n"
|
|
834
|
+
"echo(f(5));",
|
|
835
|
+
std::unordered_map<std::string, std::set<int>>{{"<string>", {1}}});
|
|
836
|
+
EXPECT_EQ(stops, 5);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
TEST(BytecodeCompiler, FastContinueWithBreakpointInDifferentFunctionUsesVmForThisOne) {
|
|
840
|
+
ScopedVm vm(true);
|
|
841
|
+
// Two ternary-bodied functions (so both would show the same VM-vs-
|
|
842
|
+
// interpreter stop-count gap if called), breakpoint only inside g's
|
|
843
|
+
// body (line 2) -- f (line 1) must still run compiled since its own
|
|
844
|
+
// span doesn't contain line 2, even though SOME breakpoint exists in
|
|
845
|
+
// this same file. Confirms the gating is genuinely per-function, not
|
|
846
|
+
// "any breakpoint anywhere disables the whole file." Only f is ever
|
|
847
|
+
// called, so g's own eligibility is never directly observed here.
|
|
848
|
+
const int stops = countDebugHookStops("function f(x) = x > 0 ? x + 1 : x - 1;\n"
|
|
849
|
+
"function g(x) = x > 0 ? x + 1 : x - 1;\n"
|
|
850
|
+
"echo(f(5));",
|
|
851
|
+
std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
|
|
852
|
+
EXPECT_EQ(stops, 3);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
TEST(BytecodeCompiler, CStyleForIncrNameCanReadItsOwnPriorValueInTheSameAssignment) {
|
|
856
|
+
ScopedVm vm(true);
|
|
857
|
+
// A C-style for's incr list can introduce a name that reads its OWN
|
|
858
|
+
// not-yet-assigned-this-iteration value on the right-hand side (its
|
|
859
|
+
// prior iteration's value) -- e.g. BOSL2's skin.scad: `best_i =
|
|
860
|
+
// result[0]<bestcost ? i : best_i,`. The compiler used to compile each
|
|
861
|
+
// incr assignment's RHS BEFORE declaring that assignment's own local
|
|
862
|
+
// slot (declareLocal ran only after compileExpr), so a self-reference
|
|
863
|
+
// like this -- and the loop BODY's own read, compiled even earlier --
|
|
864
|
+
// resolved via Op::LoadFree (the "not a known local, might be dynamic"
|
|
865
|
+
// fallback) instead of Op::LoadLocal. That's not just an extra
|
|
866
|
+
// "unknown variable" warning: LoadFree runs identically on every
|
|
867
|
+
// iteration once compiled, so it NEVER saw what Op::StoreLocal had
|
|
868
|
+
// written moments earlier at runtime -- a genuinely wrong final
|
|
869
|
+
// result (undef), not merely a cosmetic warning-count mismatch. Real
|
|
870
|
+
// OpenSCAD gives [4] here (one "unknown variable" warning for the
|
|
871
|
+
// first, genuinely-unassigned read, then the accumulator works
|
|
872
|
+
// correctly from there). Fixed by pre-declaring every incr name's slot
|
|
873
|
+
// before compiling the condition/body/incr expressions at all.
|
|
874
|
+
EXPECT_EQ(runCapturingEcho("function myloop(n) =\n"
|
|
875
|
+
" [for (i=0, bestcost=1/0;\n"
|
|
876
|
+
" i<=n;\n"
|
|
877
|
+
" bestcost = (i==2||i==4) ? i : bestcost,\n"
|
|
878
|
+
" best_i = (i==2||i==4) ? i : best_i,\n"
|
|
879
|
+
" i=i+1)\n"
|
|
880
|
+
" if (i==n) best_i];\n"
|
|
881
|
+
"echo(myloop(5));"),
|
|
882
|
+
"ECHO: [4]");
|
|
883
|
+
}
|
|
@@ -97,7 +97,7 @@ def test_return_hook_fires_for_user_function():
|
|
|
97
97
|
returns = []
|
|
98
98
|
|
|
99
99
|
def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
|
|
100
|
-
generate_partial=None, get_children_positions=None):
|
|
100
|
+
generate_partial=None, get_children_positions=None, set_fast_continue=None):
|
|
101
101
|
return ("continue", {})
|
|
102
102
|
|
|
103
103
|
def return_hook(name, value, depth):
|
|
@@ -120,7 +120,7 @@ def test_generate_partial_during_a_live_pause():
|
|
|
120
120
|
seen_partial_counts = []
|
|
121
121
|
|
|
122
122
|
def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
|
|
123
|
-
generate_partial=None, get_children_positions=None):
|
|
123
|
+
generate_partial=None, get_children_positions=None, set_fast_continue=None):
|
|
124
124
|
if forced:
|
|
125
125
|
bodies = generate_partial()
|
|
126
126
|
seen_partial_counts.append(len(bodies))
|
|
@@ -144,7 +144,7 @@ def test_generate_partial_on_an_empty_tree_returns_empty_not_an_error():
|
|
|
144
144
|
seen = []
|
|
145
145
|
|
|
146
146
|
def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
|
|
147
|
-
generate_partial=None, get_children_positions=None):
|
|
147
|
+
generate_partial=None, get_children_positions=None, set_fast_continue=None):
|
|
148
148
|
if forced:
|
|
149
149
|
seen.append(generate_partial())
|
|
150
150
|
return ("continue", {})
|
|
@@ -164,7 +164,7 @@ def test_get_children_positions_reports_module_call_block_children():
|
|
|
164
164
|
seen = []
|
|
165
165
|
|
|
166
166
|
def debug_hook(line, depth, forced=False, expr_level=False, expr_depth=0, origin=None, get_frames=None,
|
|
167
|
-
generate_partial=None, get_children_positions=None):
|
|
167
|
+
generate_partial=None, get_children_positions=None, set_fast_continue=None):
|
|
168
168
|
positions = get_children_positions()
|
|
169
169
|
if positions:
|
|
170
170
|
seen.append((line, positions))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/examples/eval_perf_benchmark.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/examples/manifold_cache_reuse.cpp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{openscad_cpp_evaluator-0.4.9 → openscad_cpp_evaluator-0.5.1}/external/openscad_cpp_parser/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|