openscad-cpp-evaluator 0.9.1__tar.gz → 0.10.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.9.1 → openscad_cpp_evaluator-0.10.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bytecode.hpp +5 -1
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/evaluator.hpp +41 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/bytecode_compiler.cpp +11 -11
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/bytecode_vm.cpp +54 -27
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_bytecode_compiler.cpp +46 -23
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_tail_calls.cpp +102 -4
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tools/cli/main.cpp +0 -0
|
@@ -241,7 +241,11 @@ struct Instruction {
|
|
|
241
241
|
struct CompiledChunk {
|
|
242
242
|
struct Param {
|
|
243
243
|
std::string name;
|
|
244
|
-
int slot = 0;
|
|
244
|
+
int slot = 0; // unused when isDyn is true -- $-params are never slot-addressed
|
|
245
|
+
// $-prefixed parameter: binds through ctx.dyn (dynamically scoped)
|
|
246
|
+
// instead of a slot -- see compileFunctionLike's own doc comment,
|
|
247
|
+
// bytecode_compiler.cpp, and bindCompiledArgs', bytecode_vm.cpp.
|
|
248
|
+
bool isDyn = false;
|
|
245
249
|
};
|
|
246
250
|
|
|
247
251
|
// A call site statically resolved at compile time (see
|
|
@@ -633,6 +633,47 @@ private:
|
|
|
633
633
|
Value evalUserFunctionCore(const std::string& name, const oscad::ASTNode& declNode, const oscad::Expression& bodyExpr,
|
|
634
634
|
EvalContext& childCtx, const oscad::Position* callPos, int upvalueParent,
|
|
635
635
|
F&& computeResult) {
|
|
636
|
+
// Guards against a genuinely non-tail-recursive user function --
|
|
637
|
+
// each logical call HERE is a real, unavoidable C++ recursive call
|
|
638
|
+
// (a trampolined tail call never re-enters this function at all,
|
|
639
|
+
// see evalFunctionBodyTrampoline/runCompiledFunctionTrampoline) --
|
|
640
|
+
// silently overflowing the native stack: a hard, unrecoverable
|
|
641
|
+
// process crash with no C++ exception to catch. Confirmed present
|
|
642
|
+
// (before this guard existed) for a plain, closure-free `function
|
|
643
|
+
// f(n) = n<=0 ? 0 : 1+f(n-1);`, compiled OR interpreted.
|
|
644
|
+
//
|
|
645
|
+
// kMaxUserCallDepth is a conservative, heuristic cap, NOT a
|
|
646
|
+
// precisely-calibrated one -- and specifically calibrated against
|
|
647
|
+
// the WORST measured platform, not the best. Two rounds of CI
|
|
648
|
+
// diagnostics (temporary test builds that recursed to increasing
|
|
649
|
+
// depths, printing success after each one so the CI log itself
|
|
650
|
+
// would show exactly where a crash happened) found: (1) this
|
|
651
|
+
// evaluator's own C++ recursion is safe into the THOUSANDS on an
|
|
652
|
+
// ordinary macOS/Linux desktop main-thread stack, but only into
|
|
653
|
+
// the low HUNDREDS on CI's windows-latest runner (smaller default
|
|
654
|
+
// thread stack and/or larger MSVC-compiled call frames than
|
|
655
|
+
// Clang's); (2) actually THROWING from that depth and unwinding
|
|
656
|
+
// back out through that many nested try/catch frames (this guard
|
|
657
|
+
// fires from inside evalUserFunctionCore, which every level of
|
|
658
|
+
// the recursion has its own try/catch in) costs meaningfully MORE
|
|
659
|
+
// native stack than simply being that deep without throwing --
|
|
660
|
+
// 300 survived on Windows without throwing, but throwing+
|
|
661
|
+
// unwinding through as few as 100 nested frames did not (80 was
|
|
662
|
+
// the last depth that survived). 50 leaves real margin below that
|
|
663
|
+
// 80 figure for whatever's already on the stack from this call's
|
|
664
|
+
// own caller, and for a smaller stack still (e.g. BelfrySCAD's own
|
|
665
|
+
// debug-session worker thread, plausibly smaller than any CI
|
|
666
|
+
// runner's main thread) that this evaluator has no way to probe
|
|
667
|
+
// for directly. A real OpenSCAD/BOSL2 recursive function is
|
|
668
|
+
// overwhelmingly tail-recursive in practice anyway (exactly why
|
|
669
|
+
// reduce()/accumulate()/while() and this evaluator's own
|
|
670
|
+
// trampolines exist) -- genuine non-tail recursion even
|
|
671
|
+
// approaching this depth is already the rare, likely-runaway case
|
|
672
|
+
// a controlled error serves far better than a crash.
|
|
673
|
+
static constexpr size_t kMaxUserCallDepth = 50;
|
|
674
|
+
if (callStack_.size() >= kMaxUserCallDepth) {
|
|
675
|
+
error("Recursion too deep while calling function '" + name + "'", declNode);
|
|
676
|
+
}
|
|
636
677
|
std::optional<ProfileHandle> prof = profileEnter("function", name, callPos, &declNode.position());
|
|
637
678
|
callStack_.push_back(CallStackFrame{CallStackFrame::Kind::Function, name, callPos, &declNode.position(), &declNode,
|
|
638
679
|
nullptr, upvalueParent});
|
|
@@ -193,7 +193,7 @@ class Compiler;
|
|
|
193
193
|
// (a non-empty `enclosing` chain, letting the literal's own free-variable
|
|
194
194
|
// references resolve as upvalues into whatever lexically contains it).
|
|
195
195
|
// Returns false (chunk left partially populated, discarded by the caller)
|
|
196
|
-
// for
|
|
196
|
+
// for any NotCompilable thrown while compiling.
|
|
197
197
|
bool compileFunctionLike(CompiledChunk& chunk, const oscad::Scope* staticScope, const oscad::ASTNode* selfDecl,
|
|
198
198
|
std::vector<EnclosingLevel> enclosing,
|
|
199
199
|
const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params,
|
|
@@ -1075,21 +1075,21 @@ bool compileFunctionLike(CompiledChunk& chunk, const oscad::Scope* staticScope,
|
|
|
1075
1075
|
std::vector<EnclosingLevel> enclosing,
|
|
1076
1076
|
const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params,
|
|
1077
1077
|
const oscad::Expression& bodyExpr) {
|
|
1078
|
-
for (const auto& p : params) {
|
|
1079
|
-
// $-prefixed parameters bind through ctx.dyn (dynamically scoped,
|
|
1080
|
-
// never slot-addressed) via the existing applyDefaults/bindArgs
|
|
1081
|
-
// machinery -- out of scope for this design, see this file's
|
|
1082
|
-
// header comment.
|
|
1083
|
-
if (!p->name->name.empty() && p->name->name[0] == '$') return false;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
1078
|
chunk.selfDecl = selfDecl;
|
|
1087
1079
|
Compiler compiler(chunk, staticScope, selfDecl, std::move(enclosing));
|
|
1088
1080
|
CompileScope bodyScope;
|
|
1089
1081
|
bodyScope.push();
|
|
1090
1082
|
for (const auto& p : params) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1083
|
+
// $-prefixed parameters bind through ctx.dyn (dynamically scoped)
|
|
1084
|
+
// rather than a slot -- never declared as a local, so a reference
|
|
1085
|
+
// to one inside the body already compiles to Op::LoadDyn via the
|
|
1086
|
+
// Identifier case's own $-check regardless of scope visibility.
|
|
1087
|
+
// Still occupies its declared POSITION among all parameters (see
|
|
1088
|
+
// bindCompiledArgs, bytecode_vm.cpp) for positional-argument
|
|
1089
|
+
// matching, exactly like the interpreter's own bindArgs.
|
|
1090
|
+
const bool isDyn = !p->name->name.empty() && p->name->name[0] == '$';
|
|
1091
|
+
const int slot = isDyn ? 0 : compiler.declareLocal(bodyScope, p->name->name);
|
|
1092
|
+
chunk.params.push_back(CompiledChunk::Param{p->name->name, slot, isDyn});
|
|
1093
1093
|
}
|
|
1094
1094
|
chunk.defaultCode.resize(params.size());
|
|
1095
1095
|
|
|
@@ -604,17 +604,18 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
|
|
|
604
604
|
// Mirrors Evaluator::bindArgs' own positional/named matching exactly (same
|
|
605
605
|
// "later write for a repeated name wins," same "evaluate every argument
|
|
606
606
|
// expression even if its name doesn't match a declared parameter" side-
|
|
607
|
-
// effect rule), but writes matched
|
|
608
|
-
// `slots` instead of a fresh unordered_map, and routes a matched
|
|
609
|
-
// straight to `childCtx.dyn`
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
//
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
//
|
|
607
|
+
// effect rule), but writes a matched plain-parameter value directly into
|
|
608
|
+
// `slots` instead of a fresh unordered_map, and routes a matched DECLARED
|
|
609
|
+
// $-parameter (chunk.params[i].isDyn) straight to `childCtx.dyn` instead of
|
|
610
|
+
// a slot -- see compileFunctionLike's own doc comment, bytecode_compiler.cpp.
|
|
611
|
+
// A $-named argument that doesn't match any declared parameter is still an
|
|
612
|
+
// undeclared override, exactly like today's interpreter path -- routed to
|
|
613
|
+
// `childCtx.dyn` too. An undeclared plain (non-$) name has no slot and
|
|
614
|
+
// nothing in this chunk can ever reference it by name, so its already-
|
|
615
|
+
// evaluated value is simply discarded -- ponytail: ctx.let_ isn't written
|
|
616
|
+
// for it either (unlike the interpreter path), which changes nothing
|
|
617
|
+
// observable, since nothing downstream of a compiled call ever reads
|
|
618
|
+
// ctx.let_ for a name a compiled body didn't itself resolve to a slot.
|
|
618
619
|
void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
|
|
619
620
|
const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& callerCtx,
|
|
620
621
|
EvalContext& childCtx, std::vector<Value>& slots, std::vector<bool>& bound) {
|
|
@@ -628,7 +629,11 @@ void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
|
|
|
628
629
|
bool matched = false;
|
|
629
630
|
for (size_t i = 0; i < nparams; ++i) {
|
|
630
631
|
if (chunk.params[i].name == name) {
|
|
631
|
-
|
|
632
|
+
if (chunk.params[i].isDyn) {
|
|
633
|
+
childCtx.dyn->set(name, std::move(v));
|
|
634
|
+
} else {
|
|
635
|
+
slots[static_cast<size_t>(chunk.params[i].slot)] = std::move(v);
|
|
636
|
+
}
|
|
632
637
|
bound[i] = true;
|
|
633
638
|
matched = true;
|
|
634
639
|
break;
|
|
@@ -641,7 +646,12 @@ void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
|
|
|
641
646
|
auto& a = static_cast<const oscad::PositionalArgument&>(*argPtr);
|
|
642
647
|
Value v = ev.evalExpr(*a.expr, callerCtx);
|
|
643
648
|
if (positionalIdx < nparams) {
|
|
644
|
-
|
|
649
|
+
const CompiledChunk::Param& p = chunk.params[positionalIdx];
|
|
650
|
+
if (p.isDyn) {
|
|
651
|
+
childCtx.dyn->set(p.name, std::move(v));
|
|
652
|
+
} else {
|
|
653
|
+
slots[static_cast<size_t>(p.slot)] = std::move(v);
|
|
654
|
+
}
|
|
645
655
|
bound[positionalIdx] = true;
|
|
646
656
|
}
|
|
647
657
|
++positionalIdx;
|
|
@@ -649,6 +659,28 @@ void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
|
|
|
649
659
|
}
|
|
650
660
|
}
|
|
651
661
|
|
|
662
|
+
// Shared by runCompiledFunction/runCompiledFunctionFromBound: applies each
|
|
663
|
+
// unbound parameter's default (or, for a $-parameter with none, an explicit
|
|
664
|
+
// undef -- mirrors Evaluator::applyDefaults' own "declaring a parameter
|
|
665
|
+
// always creates a fresh binding" rule, which for a slot-addressed plain
|
|
666
|
+
// parameter is already true for free via frame.slots' own zero-initialized
|
|
667
|
+
// Value{} fill, but for ctx.dyn needs the same explicit write applyDefaults
|
|
668
|
+
// makes, or a stale ancestor-level $-var would incorrectly show through).
|
|
669
|
+
void applyCompiledDefaults(Evaluator& ev, const CompiledChunk& chunk, VmFrame& frame, EvalContext& childCtx) {
|
|
670
|
+
for (size_t i = 0; i < chunk.params.size(); ++i) {
|
|
671
|
+
if (frame.bound[i]) continue;
|
|
672
|
+
const CompiledChunk::Param& p = chunk.params[i];
|
|
673
|
+
if (p.isDyn) {
|
|
674
|
+
childCtx.dyn->set(p.name, chunk.defaultCode[i].empty()
|
|
675
|
+
? Value{}
|
|
676
|
+
: runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack));
|
|
677
|
+
} else if (!chunk.defaultCode[i].empty()) {
|
|
678
|
+
frame.slots[static_cast<size_t>(p.slot)] =
|
|
679
|
+
runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
652
684
|
} // namespace
|
|
653
685
|
|
|
654
686
|
Value runCompiledFunction(Evaluator& ev, const CompiledChunk& chunk,
|
|
@@ -667,12 +699,7 @@ Value runCompiledFunction(Evaluator& ev, const CompiledChunk& chunk,
|
|
|
667
699
|
// started running, never during argument binding itself.
|
|
668
700
|
ev.setCurrentCallVmFrame(&frame);
|
|
669
701
|
bindCompiledArgs(ev, chunk, arguments, callerCtx, childCtx, frame.slots, frame.bound);
|
|
670
|
-
|
|
671
|
-
if (!frame.bound[i] && !chunk.defaultCode[i].empty()) {
|
|
672
|
-
frame.slots[static_cast<size_t>(chunk.params[i].slot)] =
|
|
673
|
-
runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack);
|
|
674
|
-
}
|
|
675
|
-
}
|
|
702
|
+
applyCompiledDefaults(ev, chunk, frame, childCtx);
|
|
676
703
|
return runChunk(ev, chunk, chunk.bodyCode, frame.slots, childCtx, frame.stack, tailOut);
|
|
677
704
|
}
|
|
678
705
|
|
|
@@ -684,8 +711,13 @@ Value runCompiledFunctionFromBound(Evaluator& ev, const CompiledChunk& chunk, co
|
|
|
684
711
|
frame.bound.assign(chunk.params.size(), false);
|
|
685
712
|
ev.setCurrentCallVmFrame(&frame);
|
|
686
713
|
for (size_t i = 0; i < chunk.params.size(); ++i) {
|
|
687
|
-
|
|
688
|
-
|
|
714
|
+
const CompiledChunk::Param& p = chunk.params[i];
|
|
715
|
+
if (const Value* v = bound.find(p.name)) {
|
|
716
|
+
if (p.isDyn) {
|
|
717
|
+
childCtx.dyn->set(p.name, *v);
|
|
718
|
+
} else {
|
|
719
|
+
frame.slots[static_cast<size_t>(p.slot)] = *v;
|
|
720
|
+
}
|
|
689
721
|
frame.bound[i] = true;
|
|
690
722
|
}
|
|
691
723
|
}
|
|
@@ -699,12 +731,7 @@ Value runCompiledFunctionFromBound(Evaluator& ev, const CompiledChunk& chunk, co
|
|
|
699
731
|
}
|
|
700
732
|
if (!matched && !k.empty() && k[0] == '$') childCtx.dyn->set(k, v);
|
|
701
733
|
}
|
|
702
|
-
|
|
703
|
-
if (!frame.bound[i] && !chunk.defaultCode[i].empty()) {
|
|
704
|
-
frame.slots[static_cast<size_t>(chunk.params[i].slot)] =
|
|
705
|
-
runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
734
|
+
applyCompiledDefaults(ev, chunk, frame, childCtx);
|
|
708
735
|
return runChunk(ev, chunk, chunk.bodyCode, frame.slots, childCtx, frame.stack, tailOut);
|
|
709
736
|
}
|
|
710
737
|
|
{openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/tests/test_bytecode_compiler.cpp
RENAMED
|
@@ -175,10 +175,34 @@ TEST(BytecodeCompiler, NonCompilableFunctionContainingACallStillFallsBackCorrect
|
|
|
175
175
|
"ECHO: 8");
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
TEST(BytecodeCompiler,
|
|
179
|
-
ScopedVm vm(true);
|
|
178
|
+
TEST(BytecodeCompiler, DollarPrefixedParameterCompiles) {
|
|
179
|
+
ScopedVm vm(true);
|
|
180
|
+
// $-prefixed parameters used to bail compileFunctionLike outright
|
|
181
|
+
// (bound through ctx.dyn, never slot-addressed) -- now they compile:
|
|
182
|
+
// never declared as a local (skipped in the params/declareLocal loop),
|
|
183
|
+
// so every reference inside the body still reaches Op::LoadDyn via the
|
|
184
|
+
// Identifier case's own $-check, exactly as an undeclared override
|
|
185
|
+
// already did.
|
|
180
186
|
EXPECT_EQ(runCapturingEcho("function withFn($fn = 8) = $fn;\necho(withFn());"), "ECHO: 8");
|
|
181
187
|
EXPECT_EQ(runCapturingEcho("function withFn($fn = 8) = $fn;\necho(withFn($fn = 20));"), "ECHO: 20");
|
|
188
|
+
// Positional binding: a $-param still occupies its declared POSITION
|
|
189
|
+
// among all parameters for a caller's positional arguments, exactly
|
|
190
|
+
// like the interpreter's own bindArgs.
|
|
191
|
+
EXPECT_EQ(runCapturingEcho("function withFn($fn, x) = $fn + x;\necho(withFn(10, 1));"), "ECHO: 11");
|
|
192
|
+
// Declared without a default and not passed: shadows to undef locally
|
|
193
|
+
// rather than inheriting an ancestor's dyn binding -- mirrors
|
|
194
|
+
// Evaluator::applyDefaults' explicit "declaring a parameter always
|
|
195
|
+
// creates a fresh binding" write.
|
|
196
|
+
EXPECT_EQ(runCapturingEcho("function withFn($fn) = $fn;\necho(let($fn = 99) withFn());"), "ECHO: undef");
|
|
197
|
+
// The proof this is actually running compiled, not silently falling
|
|
198
|
+
// back: same ternary-bodied shape and technique as
|
|
199
|
+
// FastContinueWithNoBreakpointInFunctionUsesVm above (3 stops =
|
|
200
|
+
// compiled, 5 = interpreted), with $fn substituted in for the plain
|
|
201
|
+
// parameter both as the declared name and every body reference.
|
|
202
|
+
const int stops = countDebugHookStops("function f($fn) = $fn > 0 ? $fn + 1 : $fn - 1;\n"
|
|
203
|
+
"echo(f(5));",
|
|
204
|
+
std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
|
|
205
|
+
EXPECT_EQ(stops, 3);
|
|
182
206
|
}
|
|
183
207
|
|
|
184
208
|
TEST(BytecodeCompiler, UndeclaredDollarNamedArgumentReachesDynInsideCompiledFunction) {
|
|
@@ -735,27 +759,26 @@ TEST(BytecodeCompiler, TernaryWrappedMutualRecursionResolvesCorrectly) {
|
|
|
735
759
|
EXPECT_EQ(runCapturingEcho(script), "ECHO: true\nECHO: false");
|
|
736
760
|
}
|
|
737
761
|
|
|
738
|
-
TEST(BytecodeCompiler,
|
|
739
|
-
ScopedVm vm(true);
|
|
740
|
-
//
|
|
741
|
-
//
|
|
742
|
-
// (
|
|
743
|
-
//
|
|
744
|
-
//
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
"ECHO: 17");
|
|
762
|
+
TEST(BytecodeCompiler, ClosureWithDollarParameterNowCompilesToo) {
|
|
763
|
+
ScopedVm vm(true);
|
|
764
|
+
// A FunctionLiteral with its OWN dollar-prefixed parameter used to fail
|
|
765
|
+
// compileFunctionLike outright, bailing the WHOLE containing
|
|
766
|
+
// declaration (see DollarPrefixedParameterCompiles above for the plain-
|
|
767
|
+
// function case) -- now compileFunctionLike has no $-specific bail left
|
|
768
|
+
// at all, so this compiles too, container and closure both.
|
|
769
|
+
const std::string script = "function outer(x) = let(g = function(y, $fn) y + x + $fn) g(5, $fn=2);\n"
|
|
770
|
+
"echo(outer(10));";
|
|
771
|
+
EXPECT_EQ(runCapturingEcho(script), "ECHO: 17");
|
|
772
|
+
// Proof via the same fast-continue stop-count technique used
|
|
773
|
+
// throughout this file (nullopt is NOT a reliable signal here -- it
|
|
774
|
+
// forces every function to interpret regardless of eligibility, see
|
|
775
|
+
// DebugAttachedWithoutFastContinueAlwaysInterprets above -- a real
|
|
776
|
+
// breakpoint map on a non-matching line is what actually distinguishes
|
|
777
|
+
// compiled from interpreted). 3 stops: outer's own body-entry, g's
|
|
778
|
+
// call-site, g's own body-entry -- no ternary/sub-expression stops for
|
|
779
|
+
// either, since both now run compiled.
|
|
780
|
+
const int stops = countDebugHookStops(script, std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
|
|
781
|
+
EXPECT_EQ(stops, 3);
|
|
759
782
|
}
|
|
760
783
|
|
|
761
784
|
// -- Tail-call optimization, VM path (Phase B) -----------------------------
|
|
@@ -9,10 +9,15 @@
|
|
|
9
9
|
// Every test here explicitly forces the interpreter path via ScopedVm(false)
|
|
10
10
|
// (not the OSCAD_BYTECODE_VM env var, which is cached forever after its
|
|
11
11
|
// first read within a test binary -- see Evaluator::
|
|
12
|
-
// setBytecodeVmEnabledForTesting's own doc comment)
|
|
13
|
-
// call machinery (Phase B
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// setBytecodeVmEnabledForTesting's own doc comment). Both the interpreter
|
|
13
|
+
// AND the bytecode VM's own call machinery (Phase B) do genuine C++
|
|
14
|
+
// recursion for a NON-tail call (there is no other way to implement one --
|
|
15
|
+
// see NonTailRecursionIsUnaffectedByTheTrampoline, below) -- a sufficiently
|
|
16
|
+
// deep one used to silently overflow the native stack and crash the whole
|
|
17
|
+
// process; evalUserFunctionCore's own depth guard (evaluator.hpp) now turns
|
|
18
|
+
// that into a controlled EvalError well before that point, shared by both
|
|
19
|
+
// paths, exercised by both compiled- and interpreted-path variants of the
|
|
20
|
+
// same test at the bottom of this file.
|
|
16
21
|
|
|
17
22
|
#include "openscad_cpp_evaluator/eval_error.hpp"
|
|
18
23
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
@@ -114,6 +119,21 @@ TEST(TailCalls, DollarVarSetEarlyInATailChainStaysVisibleManyHopsLater) {
|
|
|
114
119
|
EXPECT_EQ(runCapturingEcho(script), "ECHO: 77");
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
TEST(TailCalls, DollarParameterThreadsThroughCompiledTailRecursion) {
|
|
123
|
+
ScopedVm vm(true);
|
|
124
|
+
// $fn declared as its own PARAMETER (not just read as an ambient dyn
|
|
125
|
+
// var, unlike the test above) rebinds via childCtx.dyn at every tail
|
|
126
|
+
// hop -- compileFunctionLike no longer bails on a $-prefixed parameter
|
|
127
|
+
// (see its own doc comment, bytecode_compiler.cpp). No tail-call-
|
|
128
|
+
// specific hazard: every hop's bindCompiledArgs call is identical to
|
|
129
|
+
// what a genuine non-tail call already does, and dyn already threads
|
|
130
|
+
// correctly across hops (proved by the test above).
|
|
131
|
+
const std::string script =
|
|
132
|
+
"function sum_with_fn($fn, n, acc=0) = n == 0 ? $fn + acc : sum_with_fn($fn, n - 1, acc + n);\n"
|
|
133
|
+
"echo(sum_with_fn(1000, 50000));";
|
|
134
|
+
EXPECT_EQ(runCapturingEcho(script), "ECHO: 1.25003e+9");
|
|
135
|
+
}
|
|
136
|
+
|
|
117
137
|
TEST(TailCalls, NonTailRecursionIsUnaffectedByTheTrampoline) {
|
|
118
138
|
ScopedVm vm(false);
|
|
119
139
|
// `n * fact(n-1)` is NOT a tail position -- the multiply happens after
|
|
@@ -267,3 +287,81 @@ TEST(TailCalls, DebugHookFiresPerHopInsideATailChain) {
|
|
|
267
287
|
// single body-entry stop.
|
|
268
288
|
EXPECT_EQ(bodyEntry, 501 + 500 + 1);
|
|
269
289
|
}
|
|
290
|
+
|
|
291
|
+
TEST(TailCalls, DeepNonTailRecursionHitsAControlledErrorInsteadOfCrashingInterpreted) {
|
|
292
|
+
ScopedVm vm(false);
|
|
293
|
+
// `1 + f(n-1)` is NOT a tail position (the addition happens after the
|
|
294
|
+
// recursive call returns) -- there is no trampoline for this, ever
|
|
295
|
+
// (see NonTailRecursionIsUnaffectedByTheTrampoline, above): every
|
|
296
|
+
// logical call is a real, unavoidable C++ recursive call through
|
|
297
|
+
// evalUserFunctionCore. Before its own depth guard existed, a script
|
|
298
|
+
// shaped exactly like this one reliably segfaulted the whole process
|
|
299
|
+
// (confirmed present in the wild -- a plain, closure-free
|
|
300
|
+
// FunctionDeclaration, nothing specific to closures/letrec/tail-call
|
|
301
|
+
// machinery at all). Interpreted-path variant; see the compiled-path
|
|
302
|
+
// one below. f(500) is comfortably past kMaxUserCallDepth (50, see its
|
|
303
|
+
// own doc comment, evaluator.hpp) -- the actual native recursion never
|
|
304
|
+
// gets anywhere near 500, since the guard fires at 50 regardless of
|
|
305
|
+
// what depth the script asks for.
|
|
306
|
+
Evaluator ev;
|
|
307
|
+
auto ast = parseSrc("function f(n) = n <= 0 ? 0 : 1 + f(n - 1);\nresult = f(500);");
|
|
308
|
+
auto scope = oscad::buildScopes(ast);
|
|
309
|
+
EvalContext ctx = EvalContext::makeRoot(scope.get());
|
|
310
|
+
try {
|
|
311
|
+
ev.resolveTree(ast, ctx);
|
|
312
|
+
FAIL() << "expected EvalError";
|
|
313
|
+
} catch (const EvalError& e) {
|
|
314
|
+
EXPECT_NE(std::string(e.what()).find("Recursion too deep"), std::string::npos);
|
|
315
|
+
EXPECT_NE(std::string(e.what()).find("'f'"), std::string::npos);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
TEST(TailCalls, DeepNonTailRecursionHitsAControlledErrorInsteadOfCrashingCompiled) {
|
|
320
|
+
ScopedVm vm(true);
|
|
321
|
+
// Same script, same guard (evalUserFunctionCore is shared by both the
|
|
322
|
+
// interpreter and the bytecode VM's own compiled call path) -- the
|
|
323
|
+
// compiled path's own real per-call native stack usage is actually
|
|
324
|
+
// larger (more C++ frames between one logical call and the next, see
|
|
325
|
+
// runChunk/runCompiledFunctionFromBound/runCompiledFunctionFromBoundTrampoline),
|
|
326
|
+
// so this is the MORE exposed of the two paths, not a lesser check.
|
|
327
|
+
Evaluator ev;
|
|
328
|
+
auto ast = parseSrc("function f(n) = n <= 0 ? 0 : 1 + f(n - 1);\nresult = f(500);");
|
|
329
|
+
auto scope = oscad::buildScopes(ast);
|
|
330
|
+
EvalContext ctx = EvalContext::makeRoot(scope.get());
|
|
331
|
+
EXPECT_THROW(ev.resolveTree(ast, ctx), EvalError);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
TEST(TailCalls, CollatzStepsRecursesFromBothTernaryBranchesInterpreted) {
|
|
335
|
+
ScopedVm vm(false);
|
|
336
|
+
// Real-world example exercising tail calls from BOTH ternary branches at
|
|
337
|
+
// once (the even and odd cases each recurse) -- came up when auditing
|
|
338
|
+
// for un-trampolined tail positions (see this file's header). Scans
|
|
339
|
+
// every n < 10000 and takes the longest chain; the known record holder
|
|
340
|
+
// in that range is n=6171 at 261 steps, calling collatz_steps 10000
|
|
341
|
+
// times over.
|
|
342
|
+
const std::string script =
|
|
343
|
+
"function collatz_steps(n, steps=0) = n == 1 ? steps : "
|
|
344
|
+
"(n % 2 == 0 ? collatz_steps(n / 2, steps + 1) : collatz_steps(3 * n + 1, steps + 1));\n"
|
|
345
|
+
"lengths = [for (i = [1:10000]) collatz_steps(i)];\n"
|
|
346
|
+
"echo(max(lengths));";
|
|
347
|
+
EXPECT_EQ(runCapturingEcho(script), "ECHO: 261");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
TEST(TailCalls, CollatzStepsRecursesFromBothTernaryBranchesCompiled) {
|
|
351
|
+
ScopedVm vm(true);
|
|
352
|
+
const std::string script =
|
|
353
|
+
"function collatz_steps(n, steps=0) = n == 1 ? steps : "
|
|
354
|
+
"(n % 2 == 0 ? collatz_steps(n / 2, steps + 1) : collatz_steps(3 * n + 1, steps + 1));\n"
|
|
355
|
+
"lengths = [for (i = [1:10000]) collatz_steps(i)];\n"
|
|
356
|
+
"echo(max(lengths));";
|
|
357
|
+
EXPECT_EQ(runCapturingEcho(script), "ECHO: 261");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
TEST(TailCalls, ShallowNonTailRecursionStillWorksUnderTheDepthGuard) {
|
|
361
|
+
ScopedVm vm(true);
|
|
362
|
+
// The guard must not fire for perfectly ordinary, shallow non-tail
|
|
363
|
+
// recursion -- the overwhelmingly common real-world shape (a handful
|
|
364
|
+
// to a few dozen levels, e.g. walking a small nested data structure).
|
|
365
|
+
const std::string script = "function fact(n) = n <= 1 ? 1 : n * fact(n - 1);\necho(fact(10));";
|
|
366
|
+
EXPECT_EQ(runCapturingEcho(script), "ECHO: 3.6288e+6");
|
|
367
|
+
}
|
|
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
|
|
File without changes
|
{openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/eval_perf_benchmark.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/manifold_cache_reuse.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.9.1 → openscad_cpp_evaluator-0.10.0}/examples/minimal_debugger.cpp
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|