openscad-cpp-evaluator 0.16.0__tar.gz → 0.21.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.16.0 → openscad_cpp_evaluator-0.21.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/bindings/module.cpp +27 -3
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +21 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/lexer.l +5 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +73 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/csg_node.hpp +16 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/eval_context.hpp +15 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/eval_error.hpp +12 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/evaluator.hpp +59 -2
- openscad_cpp_evaluator-0.21.0/include/openscad_cpp_evaluator/profile.hpp +81 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/python/openscad_cpp_evaluator/__init__.py +20 -2
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/bytecode_vm.cpp +2 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/csg_generate.cpp +7 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/csg_resolve.cpp +6 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/debug_profile.cpp +103 -3
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/eval_context.cpp +1 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/eval_error.cpp +44 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/expr_eval.cpp +19 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/user_calls.cpp +14 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_bytecode_compiler.cpp +11 -3
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_cli.cpp +1 -1
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_csg_tree.cpp +70 -0
- openscad_cpp_evaluator-0.21.0/tests/test_profiling.cpp +344 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/cli_lib.cpp +8 -5
- openscad_cpp_evaluator-0.16.0/include/openscad_cpp_evaluator/profile.hpp +0 -43
- openscad_cpp_evaluator-0.16.0/tests/test_profiling.cpp +0 -127
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/uv.lock +0 -0
|
@@ -246,10 +246,34 @@ nb::object profileResultToPy(const std::optional<oscadeval::ProfileResult>& pr)
|
|
|
246
246
|
if (!pr) return nb::none();
|
|
247
247
|
nb::list sites;
|
|
248
248
|
for (const oscadeval::CallSiteProfile& s : pr->callSites) {
|
|
249
|
-
sites.append(facadeAttr("CallSiteProfile")(s.kind, s.name, s.callerName, s.callOrigin, s.callLine, s.
|
|
250
|
-
s.declLine, s.callCount, s.selfTime, s.cumulativeTime));
|
|
249
|
+
sites.append(facadeAttr("CallSiteProfile")(s.kind, s.name, s.callerName, s.callOrigin, s.callLine, s.callColumn,
|
|
250
|
+
s.declOrigin, s.declLine, s.callCount, s.selfTime, s.cumulativeTime));
|
|
251
251
|
}
|
|
252
|
-
|
|
252
|
+
// The calling-context tree, as plain dicts. Parent/child INDICES, not
|
|
253
|
+
// nested objects: the C++ side is already a flat vector keyed that way,
|
|
254
|
+
// and a self-referential nesting would have to be rebuilt here for no
|
|
255
|
+
// gain -- a consumer walks it by index just as C++ does.
|
|
256
|
+
nb::list paths;
|
|
257
|
+
for (const oscadeval::ProfilePathNode& n : pr->paths) {
|
|
258
|
+
nb::dict d;
|
|
259
|
+
d["parent"] = n.parent;
|
|
260
|
+
nb::list kids;
|
|
261
|
+
for (int c : n.children) kids.append(c);
|
|
262
|
+
d["children"] = kids;
|
|
263
|
+
d["kind"] = n.kind;
|
|
264
|
+
d["name"] = n.name;
|
|
265
|
+
d["call_origin"] = n.callOrigin;
|
|
266
|
+
d["call_line"] = n.callLine;
|
|
267
|
+
d["call_column"] = n.callColumn;
|
|
268
|
+
d["decl_origin"] = n.declOrigin;
|
|
269
|
+
d["decl_line"] = n.declLine;
|
|
270
|
+
d["call_count"] = n.callCount;
|
|
271
|
+
d["self_time"] = n.selfTime;
|
|
272
|
+
d["cumulative_time"] = n.cumulativeTime;
|
|
273
|
+
paths.append(d);
|
|
274
|
+
}
|
|
275
|
+
return facadeAttr("ProfileResult")(sites, pr->resolveTime, pr->generateTime, pr->totalTime, pr->unattributedTime,
|
|
276
|
+
paths);
|
|
253
277
|
}
|
|
254
278
|
|
|
255
279
|
// ctx.dyn / ctx.dynExplicit -> (dict[str, Any], set[str]) -- every currently-
|
|
@@ -49,6 +49,18 @@ public:
|
|
|
49
49
|
LexPos tokenEnd;
|
|
50
50
|
std::string stringBuffer; // accumulates a STRING token's content while in the %x STR lexer state
|
|
51
51
|
|
|
52
|
+
// Where the opening quote of the STRING currently being lexed began.
|
|
53
|
+
//
|
|
54
|
+
// Needed because YY_USER_ACTION resets tokenStart on EVERY rule match,
|
|
55
|
+
// and a string is matched by several rules (opening quote, content
|
|
56
|
+
// runs, escapes, closing quote). currentTokenLoc() at the closing
|
|
57
|
+
// quote therefore describes just that one character, which made every
|
|
58
|
+
// StringLiteral's source span a single `"` -- and dragged the span of
|
|
59
|
+
// anything wrapping it (a PositionalArgument, a vector element) along
|
|
60
|
+
// with it. Recorded when the opening quote is matched and used by
|
|
61
|
+
// stringTokenLoc() below.
|
|
62
|
+
LexPos stringStart;
|
|
63
|
+
|
|
52
64
|
Position toPosition(const OscadLocation& loc) const {
|
|
53
65
|
return Position{origin_, loc.first_line, loc.first_column, loc.first_offset, loc.last_offset};
|
|
54
66
|
}
|
|
@@ -58,6 +70,15 @@ public:
|
|
|
58
70
|
tokenEnd.column, tokenStart.offset, tokenEnd.offset};
|
|
59
71
|
}
|
|
60
72
|
|
|
73
|
+
// As currentTokenLoc(), but spanning from the string's OPENING quote
|
|
74
|
+
// (stringStart) to the current token's end -- i.e. the whole literal
|
|
75
|
+
// including both quotes, which is what a caller slicing the source by
|
|
76
|
+
// this span expects to get back.
|
|
77
|
+
OscadLocation stringTokenLoc() const {
|
|
78
|
+
return OscadLocation{stringStart.line, stringStart.column, tokenEnd.line,
|
|
79
|
+
tokenEnd.column, stringStart.offset, tokenEnd.offset};
|
|
80
|
+
}
|
|
81
|
+
|
|
61
82
|
void reportError(const OscadLocation& loc, const std::string& message);
|
|
62
83
|
};
|
|
63
84
|
|
|
@@ -93,13 +93,17 @@ NUMLIT 0[xX][0-9A-Fa-f]+|[0-9]+([.][0-9]*)?([eE][+-]?[0-9]+)?|[.][0-9]+([eE][+-
|
|
|
93
93
|
|
|
94
94
|
\" {
|
|
95
95
|
driver.stringBuffer.clear();
|
|
96
|
+
// YY_USER_ACTION has just set tokenStart to this opening quote; keep
|
|
97
|
+
// it, because the rules that follow will overwrite it (see
|
|
98
|
+
// ParserDriver::stringStart).
|
|
99
|
+
driver.stringStart = driver.tokenStart;
|
|
96
100
|
BEGIN(STR);
|
|
97
101
|
}
|
|
98
102
|
<STR>\\. { driver.stringBuffer.append(yytext, yyleng); }
|
|
99
103
|
<STR>[^"\\]+ { driver.stringBuffer.append(yytext, yyleng); }
|
|
100
104
|
<STR>\" {
|
|
101
105
|
BEGIN(INITIAL);
|
|
102
|
-
return yy::parser::make_STRING(driver.stringBuffer, driver.
|
|
106
|
+
return yy::parser::make_STRING(driver.stringBuffer, driver.stringTokenLoc());
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
<INCFILE>[ \t\r\n]+ { /* skip whitespace before < */ }
|
|
@@ -363,3 +363,76 @@ TEST(LexicalIdentifiers, IdentifierStr) {
|
|
|
363
363
|
std::vector<std::unique_ptr<ASTNode>> ast;
|
|
364
364
|
EXPECT_EQ(exprSrc("foo", ast)->toString(), "foo");
|
|
365
365
|
}
|
|
366
|
+
|
|
367
|
+
// -- Source spans ---------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
namespace {
|
|
370
|
+
// The source text a node's own position spans -- what a consumer slicing
|
|
371
|
+
// by start/end_offset actually gets back.
|
|
372
|
+
std::string spanned(const std::string& src, const oscad::ASTNode& n) {
|
|
373
|
+
const auto& p = n.position();
|
|
374
|
+
return src.substr(static_cast<size_t>(p.start_offset),
|
|
375
|
+
static_cast<size_t>(p.end_offset - p.start_offset));
|
|
376
|
+
}
|
|
377
|
+
} // namespace
|
|
378
|
+
|
|
379
|
+
// A string is matched by SEVERAL lexer rules (opening quote, content runs,
|
|
380
|
+
// escapes, closing quote), and YY_USER_ACTION resets tokenStart on every
|
|
381
|
+
// one of them -- so building the token's location from currentTokenLoc() at
|
|
382
|
+
// the closing quote described just that one character. Every StringLiteral
|
|
383
|
+
// spanned a bare `"`, and so did anything wrapping it, which silently
|
|
384
|
+
// corrupted a downstream consumer that sliced source by those offsets.
|
|
385
|
+
TEST(SourceSpans, StringLiteralSpansTheWholeLiteral) {
|
|
386
|
+
struct Case { std::string src; std::string want; };
|
|
387
|
+
const Case cases[] = {
|
|
388
|
+
{"a = \"txt\";", "\"txt\""},
|
|
389
|
+
{"a = \"\";", "\"\""}, // empty
|
|
390
|
+
{"a = \"with \\\"quote\\\" in it\";", "\"with \\\"quote\\\" in it\""},
|
|
391
|
+
{"a = \"esc \\n seq\";", "\"esc \\n seq\""},
|
|
392
|
+
{"a = \"comma,inside\";", "\"comma,inside\""},
|
|
393
|
+
};
|
|
394
|
+
for (const Case& c : cases) {
|
|
395
|
+
auto ast = oscad::parseSrc(c.src);
|
|
396
|
+
ASSERT_EQ(ast.size(), 1u) << c.src;
|
|
397
|
+
auto* assign = dynamic_cast<oscad::Assignment*>(ast[0].get());
|
|
398
|
+
ASSERT_NE(assign, nullptr) << c.src;
|
|
399
|
+
EXPECT_EQ(spanned(c.src, *assign->expr), c.want) << c.src;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// The enclosing node's span has to be right too -- that is how the bug
|
|
404
|
+
// actually did damage, by dragging an argument's end_offset into the
|
|
405
|
+
// middle of the string.
|
|
406
|
+
TEST(SourceSpans, NodesWrappingAStringSpanCorrectly) {
|
|
407
|
+
const std::string src = "f(\"x,y\", b);";
|
|
408
|
+
auto ast = oscad::parseSrc(src);
|
|
409
|
+
auto* call = dynamic_cast<oscad::ModularCall*>(ast[0].get());
|
|
410
|
+
ASSERT_NE(call, nullptr);
|
|
411
|
+
ASSERT_EQ(call->arguments.size(), 2u);
|
|
412
|
+
EXPECT_EQ(spanned(src, *call->arguments[0]), "\"x,y\"");
|
|
413
|
+
EXPECT_EQ(spanned(src, *call->arguments[1]), "b");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
TEST(SourceSpans, StringsInAVectorSpanCorrectly) {
|
|
417
|
+
const std::string src = "x = [\"a,b\",\"c\"];";
|
|
418
|
+
auto ast = oscad::parseSrc(src);
|
|
419
|
+
auto* assign = dynamic_cast<oscad::Assignment*>(ast[0].get());
|
|
420
|
+
ASSERT_NE(assign, nullptr);
|
|
421
|
+
auto* vec = dynamic_cast<oscad::ListComprehension*>(assign->expr.get());
|
|
422
|
+
ASSERT_NE(vec, nullptr);
|
|
423
|
+
ASSERT_EQ(vec->elements.size(), 2u);
|
|
424
|
+
EXPECT_EQ(spanned(src, *vec->elements[0]), "\"a,b\"");
|
|
425
|
+
EXPECT_EQ(spanned(src, *vec->elements[1]), "\"c\"");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Multi-line strings must still report the OPENING quote's line/column, not
|
|
429
|
+
// the closing one's.
|
|
430
|
+
TEST(SourceSpans, MultiLineStringReportsItsStartingLine) {
|
|
431
|
+
const std::string src = "a = 1;\nb = \"one\ntwo\";\n";
|
|
432
|
+
auto ast = oscad::parseSrc(src);
|
|
433
|
+
ASSERT_EQ(ast.size(), 2u);
|
|
434
|
+
auto* assign = dynamic_cast<oscad::Assignment*>(ast[1].get());
|
|
435
|
+
ASSERT_NE(assign, nullptr);
|
|
436
|
+
EXPECT_EQ(assign->expr->position().line, 2);
|
|
437
|
+
EXPECT_EQ(spanned(src, *assign->expr), "\"one\ntwo\"");
|
|
438
|
+
}
|
|
@@ -34,6 +34,22 @@ struct CSGNode {
|
|
|
34
34
|
CSGParams params; // resolve step's plain-data output
|
|
35
35
|
bool uncacheable = false; // set by a later phase (ManifoldCache, Phase 8)
|
|
36
36
|
|
|
37
|
+
// The call site that entered this node's call chain from the top
|
|
38
|
+
// level, captured at RESOLVE time -- non-owning, AST-lifetime-bound
|
|
39
|
+
// like `node`. nullptr for a node resolved at top level.
|
|
40
|
+
//
|
|
41
|
+
// Needed because a GenerateFn runs long after resolve has unwound the
|
|
42
|
+
// call stack, so a warning raised there (an open polyhedron(), a
|
|
43
|
+
// non-manifold import()) has no stack left to walk and could only ever
|
|
44
|
+
// name the library's own line. One pointer per node, deliberately, so
|
|
45
|
+
// this stays affordable on a BOSL2-scale tree; the full frame list a
|
|
46
|
+
// TRACE would need is not worth the per-node cost.
|
|
47
|
+
//
|
|
48
|
+
// Not part of cacheKey() (an explicit allowlist of kind/isBuiltin/
|
|
49
|
+
// params/children), so two identical subtrees reached from different
|
|
50
|
+
// call sites still share a cache entry.
|
|
51
|
+
const oscad::Position* warnEntry = nullptr;
|
|
52
|
+
|
|
37
53
|
// 1 + the deepest child's own treeDepth (1 for a leaf) -- set once,
|
|
38
54
|
// at construction, by whichever csg_resolve.cpp site finalizes this
|
|
39
55
|
// node's own `children` (buildTreeNode, evalModularCall's non-splice
|
|
@@ -59,6 +59,21 @@ struct EvalContext {
|
|
|
59
59
|
std::shared_ptr<const ChildrenNodeList> childrenNodes;
|
|
60
60
|
const EvalContext* childrenCallerCtx = nullptr;
|
|
61
61
|
|
|
62
|
+
// True while evaluating statements that a children() forwarded, so a
|
|
63
|
+
// module call reached this way profiles as kind "child" rather than
|
|
64
|
+
// "module". `foo() foo();` and `module foo() { foo(); }` both produce a
|
|
65
|
+
// foo->foo edge, but only the second is recursion, and a profile that
|
|
66
|
+
// cannot tell them apart is misleading.
|
|
67
|
+
//
|
|
68
|
+
// Propagates through withScope/childCtx (a forwarded `translate()
|
|
69
|
+
// foo()` is still child-passing) but NOT through callCtx, which resets
|
|
70
|
+
// it by omission -- entering a body ends the forwarding. The one
|
|
71
|
+
// deliberate exception is buildModuleChildCtx, which copies the
|
|
72
|
+
// caller's flag onto the body ctx so enterUserCall can read it there;
|
|
73
|
+
// enterUserCall then clears it, so the call it describes is the only
|
|
74
|
+
// one that sees it.
|
|
75
|
+
bool viaChildren = false;
|
|
76
|
+
|
|
62
77
|
// The one genuinely fresh construction: seeds `dyn` with OpenSCAD's
|
|
63
78
|
// built-in $-variable defaults ($fn=0, $fa=12, $fs=2, $t=0,
|
|
64
79
|
// $parent_modules=0). Every other EvalContext in a run is derived
|
|
@@ -117,4 +117,16 @@ std::string formatError(const std::string& msg, const oscad::Position* nodePosit
|
|
|
117
117
|
const std::vector<CallStackFrame>& callStack,
|
|
118
118
|
const std::string& innermostFrame = "");
|
|
119
119
|
|
|
120
|
+
// "WARNING: {msg}{locSuffix(nodePosition)}, from {entry file}, line {N}"
|
|
121
|
+
// followed by traceLines(). The "from" clause names the OUTERMOST frame's
|
|
122
|
+
// call site -- the user's own line that entered the call chain -- because
|
|
123
|
+
// a warning raised inside a library otherwise reports only the library's
|
|
124
|
+
// own line, which the reader can neither act on nor trace back.
|
|
125
|
+
//
|
|
126
|
+
// Omitted when the stack is empty (a top-level warning, the common case,
|
|
127
|
+
// which stays a single line) or when it would merely repeat the headline.
|
|
128
|
+
// Deliberately richer than real OpenSCAD, which prints neither.
|
|
129
|
+
std::string formatWarning(const std::string& msg, const oscad::Position* nodePosition,
|
|
130
|
+
const std::vector<CallStackFrame>& callStack);
|
|
131
|
+
|
|
120
132
|
} // namespace oscadeval
|
|
@@ -234,6 +234,24 @@ public:
|
|
|
234
234
|
// GenerateFn is a free function, not a method -- it needs to call this
|
|
235
235
|
// on the Evaluator& it's handed (see dispatch.hpp's GenerateFn doc
|
|
236
236
|
// comment for why that reference exists at all).
|
|
237
|
+
// The call site that entered the current call chain from the top
|
|
238
|
+
// level (callStack_ is outermost-first), or nullptr at top level.
|
|
239
|
+
// Captured into each CSGNode at resolve time so a warning raised
|
|
240
|
+
// during generate -- after the stack has unwound -- can still name the
|
|
241
|
+
// user's own line. See CSGNode::warnEntry.
|
|
242
|
+
const oscad::Position* currentWarnEntry() const {
|
|
243
|
+
return callStack_.empty() ? nullptr : callStack_.front().callPosition;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Set by generateTreeImpl() to the CSGNode currently being generated,
|
|
247
|
+
// so warn() can name the user's own call site during a phase where
|
|
248
|
+
// callStack_ is necessarily empty. Public for the same reason
|
|
249
|
+
// tagGenerated() is: a GenerateFn is a free function holding only an
|
|
250
|
+
// Evaluator&. Read only by warn(); a plain scoped save/restore rather
|
|
251
|
+
// than anything reentrancy-aware, since generateTreeImpl recurses
|
|
252
|
+
// depth-first on one thread.
|
|
253
|
+
const oscad::Position* generateWarnEntry = nullptr;
|
|
254
|
+
|
|
237
255
|
ColoredBody tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue);
|
|
238
256
|
|
|
239
257
|
// tagGenerated()'s counterpart for a mesh Manifold refused to build --
|
|
@@ -1216,12 +1234,16 @@ private:
|
|
|
1216
1234
|
|
|
1217
1235
|
// -- Profiling (Phase 9) --------------------------------------------
|
|
1218
1236
|
|
|
1219
|
-
// (kind, name, callOrigin, callLine) -- the exact call-site
|
|
1237
|
+
// (kind, name, callOrigin, callLine, callColumn) -- the exact call-site
|
|
1238
|
+
// identity a
|
|
1220
1239
|
// CallSiteProfile aggregates by. std::map (not unordered_map): a plain
|
|
1221
1240
|
// tuple of comparable fields already gets a free operator< from the
|
|
1222
1241
|
// standard library, so this needs no custom hash function for a table
|
|
1223
1242
|
// that's never more than a few thousand entries even on a large script.
|
|
1224
|
-
|
|
1243
|
+
// Column is part of the key, not decoration: `foo() foo();` and
|
|
1244
|
+
// `f(g(x))` put two distinct call sites on one line, and without it
|
|
1245
|
+
// they aggregate into one entry whose times belong to neither.
|
|
1246
|
+
using ProfileSiteKey = std::tuple<std::string, std::string, std::string, int, int>;
|
|
1225
1247
|
|
|
1226
1248
|
// Pushes profiling state for a user module/function call about to
|
|
1227
1249
|
// start -- shared by evalUserModule/evalUserFunction/
|
|
@@ -1234,6 +1256,13 @@ private:
|
|
|
1234
1256
|
ProfileSiteKey key;
|
|
1235
1257
|
bool recursiveReentry;
|
|
1236
1258
|
std::chrono::steady_clock::time_point start;
|
|
1259
|
+
// Node this call occupies in the calling-context tree, and the one
|
|
1260
|
+
// to restore on exit. `pathRecursive` is the per-PATH equivalent of
|
|
1261
|
+
// recursiveReentry: true when this entry folded back onto a node
|
|
1262
|
+
// already on the path, so only the outermost entry adds cumulative
|
|
1263
|
+
// time -- the same rule callSites uses, applied per node.
|
|
1264
|
+
int pathNode = -1;
|
|
1265
|
+
int pathPrev = -1;
|
|
1237
1266
|
};
|
|
1238
1267
|
std::optional<ProfileHandle> profileEnter(const std::string& kind, const std::string& name,
|
|
1239
1268
|
const oscad::Position* callPos, const oscad::Position* declPos);
|
|
@@ -1694,6 +1723,34 @@ private:
|
|
|
1694
1723
|
std::map<ProfileSiteKey, CallSiteProfile> profileSites_;
|
|
1695
1724
|
std::set<ProfileSiteKey> profileActive_; // site keys with a call currently on callStack_
|
|
1696
1725
|
std::vector<double> profileChildTime_; // parallel aux stack to callStack_
|
|
1726
|
+
|
|
1727
|
+
// Calling-context tree (see ProfilePathNode). profilePaths_[0] is the
|
|
1728
|
+
// <toplevel> root, created lazily on the first profiled call;
|
|
1729
|
+
// profileCurrentPath_ is the node the running call belongs to, so a
|
|
1730
|
+
// profileEnter knows which parent to hang its node off.
|
|
1731
|
+
std::vector<ProfilePathNode> profilePaths_;
|
|
1732
|
+
int profileCurrentPath_ = -1;
|
|
1733
|
+
|
|
1734
|
+
// Safety valve. The tree is bounded by distinct ACYCLIC paths (see
|
|
1735
|
+
// ProfilePathNode on recursion folding), which is finite but can still
|
|
1736
|
+
// be large for a deeply-layered library. Past this, no new nodes are
|
|
1737
|
+
// created and further calls fold onto their parent -- the report stays
|
|
1738
|
+
// truthful about totals and simply stops subdividing, rather than the
|
|
1739
|
+
// profiler becoming the thing that runs out of memory.
|
|
1740
|
+
static constexpr size_t kMaxProfilePathNodes = 200000;
|
|
1741
|
+
|
|
1742
|
+
// Finds-or-creates the child of profileCurrentPath_ for this call site
|
|
1743
|
+
// and returns its index. Sets `folded` when the returned node was
|
|
1744
|
+
// already on the current path (recursion, or the node cap) rather than
|
|
1745
|
+
// a fresh or sibling child -- the caller must then skip cumulative
|
|
1746
|
+
// time for this entry, exactly as recursiveReentry does for callSites.
|
|
1747
|
+
int profilePathEnter(const std::string& kind, const std::string& name, const std::string& callOrigin,
|
|
1748
|
+
int callLine, int callColumn, const oscad::Position* declPos, bool& folded);
|
|
1749
|
+
|
|
1750
|
+
// Fills in every node's cumulativeTime as selfTime + the cumulative of
|
|
1751
|
+
// its children, bottom-up. See profileExit for why this is derived
|
|
1752
|
+
// rather than measured.
|
|
1753
|
+
void finalizeProfilePaths();
|
|
1697
1754
|
};
|
|
1698
1755
|
|
|
1699
1756
|
} // namespace oscadeval
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <vector>
|
|
5
|
+
|
|
6
|
+
namespace oscadeval {
|
|
7
|
+
|
|
8
|
+
// Aggregated profiling data for one *call site* -- a specific source
|
|
9
|
+
// location that calls a specific user module/function, not one
|
|
10
|
+
// declaration. Two different calls to the same function get separate
|
|
11
|
+
// entries; the same call expression re-executed many times (a loop body,
|
|
12
|
+
// recursion) aggregates into one entry with callCount > 1, since the AST
|
|
13
|
+
// node (and thus its position) is identical across those invocations.
|
|
14
|
+
// Mirrors the reference's CallSiteProfile.
|
|
15
|
+
struct CallSiteProfile {
|
|
16
|
+
std::string kind; // "module" | "child" (forwarded via children()) | "function"
|
|
17
|
+
std::string name;
|
|
18
|
+
std::string callerName; // enclosing module/function's own name, or "<toplevel>"
|
|
19
|
+
std::string callOrigin;
|
|
20
|
+
int callLine = 0;
|
|
21
|
+
int callColumn = 0; // distinguishes two calls sharing one line
|
|
22
|
+
std::string declOrigin;
|
|
23
|
+
int declLine = 0;
|
|
24
|
+
int callCount = 0;
|
|
25
|
+
double selfTime = 0.0; // seconds, own code only, never double-counted
|
|
26
|
+
double cumulativeTime = 0.0; // seconds, includes children; recursion-guarded
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// One node of the calling-context tree: a call site reached by ONE
|
|
30
|
+
// specific path from <toplevel>. Where CallSiteProfile aggregates a site
|
|
31
|
+
// over every path that reached it, this keeps them separate -- so
|
|
32
|
+
// `cuboid` called from `bracket` and `cuboid` called from `rail` are two
|
|
33
|
+
// nodes with their own times, and a consumer can show what a particular
|
|
34
|
+
// path actually cost rather than a total across all callers.
|
|
35
|
+
//
|
|
36
|
+
// Flat vector with parent/child indices rather than pointers: it survives
|
|
37
|
+
// the vector reallocating as nodes are appended, and crosses the Python
|
|
38
|
+
// binding as plain data with no ownership question.
|
|
39
|
+
//
|
|
40
|
+
// Recursion is folded rather than unrolled. Re-entering a call site
|
|
41
|
+
// already on the current path reuses that node instead of appending a new
|
|
42
|
+
// child, so `fib` calling itself 400 deep is one node with callCount 400,
|
|
43
|
+
// not a 400-node chain. That bounds the tree by distinct ACYCLIC paths and
|
|
44
|
+
// keeps the recursion-guarded cumulative-time rule (only the outermost
|
|
45
|
+
// entry contributes) meaningful per node.
|
|
46
|
+
struct ProfilePathNode {
|
|
47
|
+
int parent = -1; // index into ProfileResult::paths; -1 for the root
|
|
48
|
+
std::vector<int> children; // indices, in first-call order
|
|
49
|
+
std::string kind; // "module" | "child" | "function"; empty for the root
|
|
50
|
+
std::string name; // callee; "<toplevel>" for the root
|
|
51
|
+
std::string callOrigin;
|
|
52
|
+
int callLine = 0;
|
|
53
|
+
int callColumn = 0; // distinguishes two calls sharing one line
|
|
54
|
+
std::string declOrigin;
|
|
55
|
+
int declLine = 0;
|
|
56
|
+
int callCount = 0;
|
|
57
|
+
double selfTime = 0.0; // seconds, this node's own code on this path
|
|
58
|
+
double cumulativeTime = 0.0; // seconds, this node and everything under it
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Whole-evaluate() profiling summary, built when Evaluator is constructed
|
|
62
|
+
// with profiling=true. unattributedTime covers top-level script code and
|
|
63
|
+
// anything else not inside a user module/function call (native builtins'
|
|
64
|
+
// own resolve work, mostly) -- resolveTime always equals
|
|
65
|
+
// sum(s.selfTime for callSites) + unattributedTime, so a UI can show
|
|
66
|
+
// percentages that honestly add to 100%. Mirrors the reference's
|
|
67
|
+
// ProfileResult.
|
|
68
|
+
struct ProfileResult {
|
|
69
|
+
std::vector<CallSiteProfile> callSites;
|
|
70
|
+
// The calling-context tree; paths[0] is always the <toplevel> root.
|
|
71
|
+
// Empty only if profiling was off. callSites stays exactly as it was --
|
|
72
|
+
// the flat per-site view is still the right answer to "what is
|
|
73
|
+
// expensive overall", and is cheaper to scan than walking this.
|
|
74
|
+
std::vector<ProfilePathNode> paths;
|
|
75
|
+
double resolveTime = 0.0;
|
|
76
|
+
double generateTime = 0.0;
|
|
77
|
+
double totalTime = 0.0;
|
|
78
|
+
double unattributedTime = 0.0;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
} // namespace oscadeval
|
|
@@ -9,7 +9,7 @@ like a manifold3d body (`.is_empty()` / `.to_mesh()`), so the renderer reads
|
|
|
9
9
|
`cb.body.to_mesh().vert_properties/tri_verts/run_original_id/run_index`
|
|
10
10
|
exactly as before -- no cross-module Manifold objects are ever exchanged.
|
|
11
11
|
"""
|
|
12
|
-
from dataclasses import dataclass
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
13
|
from typing import Optional
|
|
14
14
|
|
|
15
15
|
from . import _openscad_cpp_evaluator as _ext
|
|
@@ -142,11 +142,12 @@ class _CSGNode:
|
|
|
142
142
|
class CallSiteProfile:
|
|
143
143
|
"""Aggregated profiling data for one call site. Mirrors the reference's
|
|
144
144
|
CallSiteProfile -- see ProfileResult's own docstring."""
|
|
145
|
-
kind: str # "module" | "function"
|
|
145
|
+
kind: str # "module" | "child" | "function"
|
|
146
146
|
name: str
|
|
147
147
|
caller_name: str
|
|
148
148
|
call_origin: str
|
|
149
149
|
call_line: int
|
|
150
|
+
call_column: int
|
|
150
151
|
decl_origin: str
|
|
151
152
|
decl_line: int
|
|
152
153
|
call_count: int = 0
|
|
@@ -165,6 +166,23 @@ class ProfileResult:
|
|
|
165
166
|
generate_time: float
|
|
166
167
|
total_time: float
|
|
167
168
|
unattributed_time: float
|
|
169
|
+
# Calling-context tree: a flat list of dicts, paths[0] the <toplevel>
|
|
170
|
+
# root, linked by `parent`/`children` INDICES into this same list.
|
|
171
|
+
#
|
|
172
|
+
# Where call_sites aggregates a site over every path that reached it,
|
|
173
|
+
# each node here is one call site on ONE path -- so `cuboid` called
|
|
174
|
+
# from `bracket` and from `rail` are separate nodes with their own
|
|
175
|
+
# times, and a report can say what a particular path cost rather than
|
|
176
|
+
# only what a name cost in total.
|
|
177
|
+
#
|
|
178
|
+
# Keys: parent, children, kind, name, call_origin, call_line,
|
|
179
|
+
# decl_origin, decl_line, call_count, self_time, cumulative_time.
|
|
180
|
+
#
|
|
181
|
+
# Recursion is folded: re-entering a site already on the path reuses
|
|
182
|
+
# that node (call_count rises) instead of unrolling a node per level.
|
|
183
|
+
# Defaults to empty so a ProfileResult built by older code still
|
|
184
|
+
# constructs.
|
|
185
|
+
paths: list = field(default_factory=list)
|
|
168
186
|
|
|
169
187
|
|
|
170
188
|
def _summarize_param(value, max_items: int = 6, max_len: int = 40) -> str:
|
|
@@ -1168,6 +1168,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
|
|
|
1168
1168
|
treeNode->kind = site.tagName;
|
|
1169
1169
|
treeNode->node = site.node;
|
|
1170
1170
|
treeNode->isBuiltin = true;
|
|
1171
|
+
treeNode->warnEntry = ev.currentWarnEntry();
|
|
1171
1172
|
treeNode->children = std::move(children);
|
|
1172
1173
|
treeNode->params = std::move(pending.params);
|
|
1173
1174
|
treeNode->uncacheable = uncacheable;
|
|
@@ -1244,6 +1245,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
|
|
|
1244
1245
|
treeNode->kind = site.op;
|
|
1245
1246
|
treeNode->node = site.node;
|
|
1246
1247
|
treeNode->isBuiltin = true;
|
|
1248
|
+
treeNode->warnEntry = ev.currentWarnEntry();
|
|
1247
1249
|
treeNode->children = std::move(children);
|
|
1248
1250
|
treeNode->params = std::move(params);
|
|
1249
1251
|
treeNode->uncacheable = uncacheable;
|
|
@@ -59,7 +59,14 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
59
59
|
|
|
60
60
|
auto it = dispatch.find(node.kind);
|
|
61
61
|
if (node.isBuiltin && it != dispatch.end()) {
|
|
62
|
+
// Resolve captured which user-level call reached this node;
|
|
63
|
+
// republish it so a warning raised inside the GenerateFn
|
|
64
|
+
// can name that line. Restored after (rather than left set)
|
|
65
|
+
// so a sibling generated at top level doesn't inherit it.
|
|
66
|
+
const oscad::Position* savedWarnEntry = generateWarnEntry;
|
|
67
|
+
generateWarnEntry = node.warnEntry;
|
|
62
68
|
node.bodies = it->second(*this, node.params, node.children, *node.node);
|
|
69
|
+
generateWarnEntry = savedWarnEntry;
|
|
63
70
|
} else {
|
|
64
71
|
// No registered GenerateFn for this kind (a builtin with
|
|
65
72
|
// display-only semantics like render(), or a non-builtin
|
|
@@ -66,6 +66,7 @@ void Evaluator::buildTreeNode(const std::string& kind, const oscad::ASTNode& nod
|
|
|
66
66
|
treeNode->kind = kind;
|
|
67
67
|
treeNode->node = &node;
|
|
68
68
|
treeNode->isBuiltin = true;
|
|
69
|
+
treeNode->warnEntry = currentWarnEntry();
|
|
69
70
|
treeNode->children = std::move(children);
|
|
70
71
|
treeNode->params = std::move(params);
|
|
71
72
|
treeNode->uncacheable = uncacheable;
|
|
@@ -135,6 +136,7 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
|
|
|
135
136
|
treeNode->kind = name;
|
|
136
137
|
treeNode->node = &node;
|
|
137
138
|
treeNode->isBuiltin = true;
|
|
139
|
+
treeNode->warnEntry = currentWarnEntry();
|
|
138
140
|
treeNode->uncacheable = uncacheable;
|
|
139
141
|
treeNode->children = std::move(children);
|
|
140
142
|
treeNode->params = std::move(params);
|
|
@@ -159,6 +161,7 @@ void Evaluator::spliceModuleChildren(std::vector<std::unique_ptr<CSGNode>> child
|
|
|
159
161
|
unionNode->kind = "union";
|
|
160
162
|
unionNode->node = &callNode;
|
|
161
163
|
unionNode->isBuiltin = false;
|
|
164
|
+
unionNode->warnEntry = currentWarnEntry();
|
|
162
165
|
unionNode->uncacheable = std::any_of(children.begin(), children.end(), [](const auto& c) { return c->uncacheable; });
|
|
163
166
|
unionNode->children = std::move(children);
|
|
164
167
|
setTreeDepthOrThrow(*unionNode, callNode);
|
|
@@ -266,8 +269,10 @@ std::vector<ColoredBody> Evaluator::evaluateImpl(const NodeList& nodes, EvalCont
|
|
|
266
269
|
selfSum += site.selfTime;
|
|
267
270
|
sites.push_back(site);
|
|
268
271
|
}
|
|
272
|
+
finalizeProfilePaths();
|
|
269
273
|
profileResult = ProfileResult{
|
|
270
|
-
std::move(sites), resolveTime, generateTime, resolveTime + generateTime,
|
|
274
|
+
std::move(sites), profilePaths_, resolveTime, generateTime, resolveTime + generateTime,
|
|
275
|
+
std::max(0.0, resolveTime - selfSum),
|
|
271
276
|
};
|
|
272
277
|
}
|
|
273
278
|
|