openscad-cpp-evaluator 1.37.2__tar.gz → 1.38.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-1.37.2 → openscad_cpp_evaluator-1.38.0}/CLAUDE.md +30 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +14 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/expression.cpp +13 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +5 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/grammar/lexer.l +3 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/grammar/parser.y +12 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +6 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/pretty_print.cpp +3 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +11 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +1 -0
- openscad_cpp_evaluator-1.38.0/external/openscad_cpp_parser/tests/test_profile_time.cpp +86 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/csg_node.hpp +7 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/evaluator.hpp +17 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/CMakeLists.txt +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/builtins.hpp +6 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/function_builtins.cpp +1 -0
- openscad_cpp_evaluator-1.38.0/src/builtins/profile_time.cpp +63 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/registry.cpp +2 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/coverage.cpp +6 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/csg_generate.cpp +11 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/csg_resolve.cpp +19 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/expr_eval.cpp +15 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/format_closure.cpp +4 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/CMakeLists.txt +1 -0
- openscad_cpp_evaluator-1.38.0/tests/test_profile_time.cpp +139 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/uv.lock +1 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/cmake/patch_manifold.cmake +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/import/scene_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/xkcd_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/xkcd_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_discretizer.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_undef_args.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/tools/cli/main.cpp +0 -0
|
@@ -1866,3 +1866,33 @@ A compiled function keeps its parameters in frame slots, which no `EvalContext`
|
|
|
1866
1866
|
can see, and the children are statement opcodes that resolve names through the
|
|
1867
1867
|
context — without this, `function f(w) = render() { cube(w); }.volume;` finds no
|
|
1868
1868
|
`w` and silently measures nothing.
|
|
1869
|
+
|
|
1870
|
+
## `profile_time()`
|
|
1871
|
+
|
|
1872
|
+
`profile_time([label]) { ... }` prints `PROFILE: label: T ms (script S ms, geometry G ms)`;
|
|
1873
|
+
`x = profile_time([label]) expr;` prints `PROFILE: label: T ms` and evaluates to `expr`'s value.
|
|
1874
|
+
`supported_feature("profile-time")` is 1. The label is optional (default `line N of file`), and
|
|
1875
|
+
is evaluated before the clock starts. Times under 1 ms print in microseconds (`fmtMs`), so a fast part never reads `0.00 ms`.
|
|
1876
|
+
|
|
1877
|
+
### Implementation notes
|
|
1878
|
+
|
|
1879
|
+
- **Parser:** `profile_time` is a reserved keyword (`KW_PROFILE_TIME`) for the same reason as
|
|
1880
|
+
`render`. The statement form is a plain `ModularCall` named `profile_time`; the expression form
|
|
1881
|
+
is a `ProfileTimeOp` node shaped like `EchoOp`, with a required body.
|
|
1882
|
+
- **Script time** is taken around the resolve function's call in `Evaluator::evalModularCall`
|
|
1883
|
+
(csg_resolve.cpp), not inside `resolveProfileTime`, so argument evaluation and every child
|
|
1884
|
+
count. It is stored on `CSGNode::profileScriptMs`, which is deliberately not in `cacheKey()` --
|
|
1885
|
+
a timing in the key would make every profiled subtree a permanent miss.
|
|
1886
|
+
- **Geometry time** is taken in `generateTreeImpl`, from *before* the cache lookup, and printed
|
|
1887
|
+
when the node finishes, so a hit reports `geometry cached` rather than vanishing. A hit on an
|
|
1888
|
+
**ancestor** never visits the profile_time() node at all; `reportCachedProfiles` walks the hit
|
|
1889
|
+
subtree for them, but only when the run resolved any (`profileNodeCount_`), so a model without
|
|
1890
|
+
profile_time() pays nothing on its hits.
|
|
1891
|
+
- **Resolve-only runs** (`evaluate(..., generate=false)`) print `script S ms (no geometry built)`
|
|
1892
|
+
at resolve time instead (`generateRequested_`).
|
|
1893
|
+
- **Expression form** is interpreter-only: the bytecode compiler has no case for
|
|
1894
|
+
`ProfileTimeOp`, so its `default:` throws `NotCompilable` and the containing function falls back
|
|
1895
|
+
to the interpreter. It is also deliberately absent from `simplifyTailStep` -- the clock must stop
|
|
1896
|
+
after the body, which a tail step would not wait for.
|
|
1897
|
+
- `PROFILE:` lines go straight to `echoFn_`, not through `emitWarning`, so the ManifoldCache never
|
|
1898
|
+
stores or replays them (a replay would print a stale time as if it were current).
|
|
@@ -217,6 +217,20 @@ public:
|
|
|
217
217
|
void buildScope(Scope& parentScope) override;
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
+
// `profile_time(arguments...) body` -- same shape as EchoOp; times the body.
|
|
221
|
+
// Unlike echo/assert the body is required: timing nothing is meaningless.
|
|
222
|
+
class ProfileTimeOp : public Expression {
|
|
223
|
+
public:
|
|
224
|
+
ProfileTimeOp(Position position, std::vector<std::unique_ptr<Argument>> arguments, std::unique_ptr<Expression> body)
|
|
225
|
+
: Expression(NodeKind::ProfileTimeOp, std::move(position)), arguments(std::move(arguments)), body(std::move(body)) {}
|
|
226
|
+
|
|
227
|
+
std::vector<std::unique_ptr<Argument>> arguments;
|
|
228
|
+
std::unique_ptr<Expression> body;
|
|
229
|
+
|
|
230
|
+
std::string toString() const override;
|
|
231
|
+
void buildScope(Scope& parentScope) override;
|
|
232
|
+
};
|
|
233
|
+
|
|
220
234
|
// `assert(arguments...) body` -- same shape as EchoOp.
|
|
221
235
|
class AssertOp : public Expression {
|
|
222
236
|
public:
|
|
@@ -21,6 +21,7 @@ const char* nodeKindName(NodeKind kind) {
|
|
|
21
21
|
case NodeKind::LetOp: return "LetOp";
|
|
22
22
|
case NodeKind::EchoOp: return "EchoOp";
|
|
23
23
|
case NodeKind::AssertOp: return "AssertOp";
|
|
24
|
+
case NodeKind::ProfileTimeOp: return "ProfileTimeOp";
|
|
24
25
|
case NodeKind::FunctionLiteral: return "FunctionLiteral";
|
|
25
26
|
case NodeKind::UnaryMinusOp: return "UnaryMinusOp";
|
|
26
27
|
case NodeKind::AdditionOp: return "AdditionOp";
|
|
@@ -134,6 +134,18 @@ void EchoOp::buildScope(Scope& parentScope) {
|
|
|
134
134
|
body->buildScope(parentScope);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
std::string ProfileTimeOp::toString() const {
|
|
138
|
+
return "profile_time(" + joinToString(arguments, ", ") + ") " + body->toString();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void ProfileTimeOp::buildScope(Scope& parentScope) {
|
|
142
|
+
setScope(parentScope);
|
|
143
|
+
for (auto& a : arguments) {
|
|
144
|
+
a->buildScope(parentScope);
|
|
145
|
+
}
|
|
146
|
+
body->buildScope(parentScope);
|
|
147
|
+
}
|
|
148
|
+
|
|
137
149
|
std::string AssertOp::toString() const {
|
|
138
150
|
return "assert(" + joinToString(arguments, ", ") + ") " + body->toString();
|
|
139
151
|
}
|
|
@@ -219,6 +231,7 @@ int operatorPrecedence(NodeKind kind) {
|
|
|
219
231
|
case NodeKind::LetOp:
|
|
220
232
|
case NodeKind::EchoOp:
|
|
221
233
|
case NodeKind::AssertOp:
|
|
234
|
+
case NodeKind::ProfileTimeOp:
|
|
222
235
|
case NodeKind::FunctionLiteral:
|
|
223
236
|
return 0;
|
|
224
237
|
case NodeKind::LogicalOrOp:
|
|
@@ -88,6 +88,11 @@ NodePtr makeEchoOp(ParserDriver& driver, const OscadLocation& loc, NodeList argu
|
|
|
88
88
|
nodeCast<Expression>(std::move(body)));
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
NodePtr makeProfileTimeOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body) {
|
|
92
|
+
return std::make_unique<ProfileTimeOp>(driver.toPosition(loc), nodeListCast<Argument>(std::move(arguments)),
|
|
93
|
+
nodeCast<Expression>(std::move(body)));
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
NodePtr makeAssertOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body) {
|
|
92
97
|
return std::make_unique<AssertOp>(driver.toPosition(loc), nodeListCast<Argument>(std::move(arguments)),
|
|
93
98
|
nodeCast<Expression>(std::move(body)));
|
|
@@ -104,6 +104,7 @@ NodePtr makeAssignment(ParserDriver& driver, const OscadLocation& loc, const Osc
|
|
|
104
104
|
|
|
105
105
|
NodePtr makeLetOp(ParserDriver& driver, const OscadLocation& loc, NodeList assignments, NodePtr body);
|
|
106
106
|
NodePtr makeEchoOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body);
|
|
107
|
+
NodePtr makeProfileTimeOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body);
|
|
107
108
|
NodePtr makeAssertOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body);
|
|
108
109
|
NodePtr makeFunctionLiteral(ParserDriver& driver, const OscadLocation& loc, NodeList parameters, NodePtr body);
|
|
109
110
|
|
|
@@ -52,6 +52,9 @@ const std::unordered_map<std::string, KeywordFactory>& keywordTable() {
|
|
|
52
52
|
// apart from a function call. Note `$render` still lexes as NAME --
|
|
53
53
|
// this table is keyed on the full IDENT text, including the `$`.
|
|
54
54
|
{"render", [](const OscadLocation& l) { return yy::parser::make_KW_RENDER(l); }},
|
|
55
|
+
// Reserved for the same reason as `render`: its expression form
|
|
56
|
+
// (`x = profile_time("label") expr;`) starts like a function call.
|
|
57
|
+
{"profile_time", [](const OscadLocation& l) { return yy::parser::make_KW_PROFILE_TIME(l); }},
|
|
55
58
|
{"undef", [](const OscadLocation& l) { return yy::parser::make_KW_UNDEF(l); }},
|
|
56
59
|
{"true", [](const OscadLocation& l) { return yy::parser::make_KW_TRUE(l); }},
|
|
57
60
|
{"false", [](const OscadLocation& l) { return yy::parser::make_KW_FALSE(l); }},
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
KW_INTERSECTION_FOR "intersection_for"
|
|
94
94
|
KW_EACH "each"
|
|
95
95
|
KW_RENDER "render"
|
|
96
|
+
KW_PROFILE_TIME "profile_time"
|
|
96
97
|
KW_UNDEF "undef"
|
|
97
98
|
KW_TRUE "true"
|
|
98
99
|
KW_FALSE "false"
|
|
@@ -158,7 +159,7 @@
|
|
|
158
159
|
%type <NodePtr> modifier_show_only modifier_highlight modifier_background modifier_disable
|
|
159
160
|
%type <NodePtr> if_statement ifelse_statement
|
|
160
161
|
%type <NodePtr> modular_for modular_intersection_for modular_let modular_assert modular_echo modular_call
|
|
161
|
-
%type <NodePtr> render_stmt render_expr
|
|
162
|
+
%type <NodePtr> render_stmt render_expr profile_time_stmt
|
|
162
163
|
%type <NodePtr> expr opchain postfix primary
|
|
163
164
|
%type <NodePtr> range_expr vector_expr vector_element
|
|
164
165
|
%type <NodePtr> listcomp_elements listcomp_paren_expr listcomp_let listcomp_each
|
|
@@ -330,6 +331,7 @@ single_module_instantiation:
|
|
|
330
331
|
| modular_echo { $$ = std::move($1); }
|
|
331
332
|
| modular_call { $$ = std::move($1); }
|
|
332
333
|
| render_stmt { $$ = std::move($1); }
|
|
334
|
+
| profile_time_stmt { $$ = std::move($1); }
|
|
333
335
|
;
|
|
334
336
|
|
|
335
337
|
modular_for:
|
|
@@ -378,6 +380,14 @@ render_stmt:
|
|
|
378
380
|
}
|
|
379
381
|
;
|
|
380
382
|
|
|
383
|
+
// Same arrangement as render_stmt: the keyword exists only for the
|
|
384
|
+
// expression form, and the statement form is an ordinary builtin call.
|
|
385
|
+
profile_time_stmt:
|
|
386
|
+
"profile_time" "(" arguments ")" child_statement {
|
|
387
|
+
$$ = makeModularCall(driver, @$, @1, "profile_time", std::move($3), std::move($5));
|
|
388
|
+
}
|
|
389
|
+
;
|
|
390
|
+
|
|
381
391
|
// -- Expressions ----------------------------------------------------------
|
|
382
392
|
//
|
|
383
393
|
// `expr` covers let/assert/echo/funclit_def/ternary plus the operator
|
|
@@ -393,6 +403,7 @@ expr:
|
|
|
393
403
|
| "assert" "(" arguments ")" { $$ = makeAssertOp(driver, @$, std::move($3), makeUndefinedLiteral(driver, @$)); }
|
|
394
404
|
| "echo" "(" arguments ")" expr { $$ = makeEchoOp(driver, @$, std::move($3), std::move($5)); }
|
|
395
405
|
| "echo" "(" arguments ")" { $$ = makeEchoOp(driver, @$, std::move($3), makeUndefinedLiteral(driver, @$)); }
|
|
406
|
+
| "profile_time" "(" arguments ")" expr { $$ = makeProfileTimeOp(driver, @$, std::move($3), std::move($5)); }
|
|
396
407
|
| "function" "(" parameters ")" expr { $$ = makeFunctionLiteral(driver, @$, std::move($3), std::move($5)); }
|
|
397
408
|
| opchain "?" expr ":" expr { $$ = makeTernaryOp(driver, @$, std::move($1), std::move($3), std::move($5)); }
|
|
398
409
|
| opchain { $$ = std::move($1); }
|
|
@@ -180,6 +180,12 @@ void classifyNode(ASTNode& node, std::vector<ExprSlot>& exprFields, std::vector<
|
|
|
180
180
|
addExpr(n.body, exprFields);
|
|
181
181
|
break;
|
|
182
182
|
}
|
|
183
|
+
case NodeKind::ProfileTimeOp: {
|
|
184
|
+
auto& n = static_cast<ProfileTimeOp&>(node);
|
|
185
|
+
addArgumentExprList(n.arguments, exprFields);
|
|
186
|
+
addExpr(n.body, exprFields);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
183
189
|
case NodeKind::AssertOp: {
|
|
184
190
|
auto& n = static_cast<AssertOp&>(node);
|
|
185
191
|
addArgumentExprList(n.arguments, exprFields);
|
|
@@ -486,6 +486,9 @@ std::string fmtExpr(const ASTNode& exprNode, int indent, int w) {
|
|
|
486
486
|
}
|
|
487
487
|
return "echo(" + args + ")\n" + pad + fmtExpr(*e->body, indent, w);
|
|
488
488
|
}
|
|
489
|
+
if (auto* p = dynamic_cast<const ProfileTimeOp*>(&exprNode)) {
|
|
490
|
+
return "profile_time(" + joinToString(p->arguments, ", ") + ")\n" + pad + fmtExpr(*p->body, indent, w);
|
|
491
|
+
}
|
|
489
492
|
if (auto* l = dynamic_cast<const LetOp*>(&exprNode)) {
|
|
490
493
|
return fmtLetOpExpr(*l, indent, w);
|
|
491
494
|
}
|
|
@@ -150,6 +150,12 @@ json toJsonImpl(const ASTNode& node, bool includePos) {
|
|
|
150
150
|
j["body"] = valueToJson(n.body.get(), includePos);
|
|
151
151
|
break;
|
|
152
152
|
}
|
|
153
|
+
case NodeKind::ProfileTimeOp: {
|
|
154
|
+
auto& n = static_cast<const ProfileTimeOp&>(node);
|
|
155
|
+
j["arguments"] = listToJson(n.arguments, includePos);
|
|
156
|
+
j["body"] = valueToJson(n.body.get(), includePos);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
153
159
|
case NodeKind::AssertOp: {
|
|
154
160
|
auto& n = static_cast<const AssertOp&>(node);
|
|
155
161
|
j["arguments"] = listToJson(n.arguments, includePos);
|
|
@@ -446,6 +452,11 @@ const std::unordered_map<std::string, Builder>& registry() {
|
|
|
446
452
|
return std::make_unique<EchoOp>(std::move(pos), listFromJson<Argument>(j, "arguments"),
|
|
447
453
|
childFromJson<Expression>(j, "body"));
|
|
448
454
|
}},
|
|
455
|
+
{"ProfileTimeOp",
|
|
456
|
+
[](const json& j, Position pos) -> std::unique_ptr<ASTNode> {
|
|
457
|
+
return std::make_unique<ProfileTimeOp>(std::move(pos), listFromJson<Argument>(j, "arguments"),
|
|
458
|
+
childFromJson<Expression>(j, "body"));
|
|
459
|
+
}},
|
|
449
460
|
{"AssertOp",
|
|
450
461
|
[](const json& j, Position pos) -> std::unique_ptr<ASTNode> {
|
|
451
462
|
return std::make_unique<AssertOp>(std::move(pos), listFromJson<Argument>(j, "arguments"),
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// `profile_time()`: the statement form times its children, the expression
|
|
2
|
+
// form (`x = profile_time("label") expr;`) its body. `profile_time` is a
|
|
3
|
+
// reserved keyword only because the expression form would otherwise be
|
|
4
|
+
// indistinguishable from a function call; the statement form is a plain
|
|
5
|
+
// ModularCall, so builtin dispatch downstream sees an ordinary module.
|
|
6
|
+
|
|
7
|
+
#include "openscad_cpp_parser/api.hpp"
|
|
8
|
+
#include "openscad_cpp_parser/pretty_print.hpp"
|
|
9
|
+
#include "openscad_cpp_parser/serialization.hpp"
|
|
10
|
+
#include "test_helpers.hpp"
|
|
11
|
+
|
|
12
|
+
#include <gtest/gtest.h>
|
|
13
|
+
|
|
14
|
+
using namespace oscad;
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
const ProfileTimeOp* profileOf(const std::vector<std::unique_ptr<ASTNode>>& ast) {
|
|
19
|
+
auto* a = dynamic_cast<Assignment*>(ast[0].get());
|
|
20
|
+
return a ? dynamic_cast<const ProfileTimeOp*>(a->expr.get()) : nullptr;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
TEST(ProfileTime, StatementFormIsAPlainModularCall) {
|
|
26
|
+
auto ast = parseSrc("profile_time(\"gears\") { cube(1); sphere(2); }");
|
|
27
|
+
ASSERT_EQ(ast.size(), 1u);
|
|
28
|
+
auto* call = dynamic_cast<ModularCall*>(ast[0].get());
|
|
29
|
+
ASSERT_NE(call, nullptr);
|
|
30
|
+
EXPECT_EQ(call->name->name, "profile_time");
|
|
31
|
+
EXPECT_EQ(call->arguments.size(), 1u);
|
|
32
|
+
EXPECT_EQ(call->children.size(), 2u);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TEST(ProfileTime, ExpressionFormWrapsItsBody) {
|
|
36
|
+
auto ast = parseSrc("x = profile_time(\"sum\") 1 + 2;");
|
|
37
|
+
const ProfileTimeOp* p = profileOf(ast);
|
|
38
|
+
ASSERT_NE(p, nullptr);
|
|
39
|
+
EXPECT_EQ(p->kind(), NodeKind::ProfileTimeOp);
|
|
40
|
+
EXPECT_EQ(p->arguments.size(), 1u);
|
|
41
|
+
EXPECT_EQ(p->body->kind(), NodeKind::AdditionOp);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
TEST(ProfileTime, ExpressionFormAllowsNoLabel) {
|
|
45
|
+
const ProfileTimeOp* p = nullptr;
|
|
46
|
+
auto ast = parseSrc("x = profile_time() f(3);");
|
|
47
|
+
p = profileOf(ast);
|
|
48
|
+
ASSERT_NE(p, nullptr);
|
|
49
|
+
EXPECT_TRUE(p->arguments.empty());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
TEST(ProfileTime, ExpressionFormNeedsABody) {
|
|
53
|
+
EXPECT_THROW(parseSrc("x = profile_time(\"a\");"), ParseError);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
TEST(ProfileTime, IsReserved) {
|
|
57
|
+
EXPECT_THROW(parseSrc("profile_time = 1;"), ParseError);
|
|
58
|
+
EXPECT_THROW(parseSrc("function profile_time() = 1;"), ParseError);
|
|
59
|
+
EXPECT_THROW(parseSrc("module profile_time() {}"), ParseError);
|
|
60
|
+
// `$profile_time` is a different identifier and stays usable.
|
|
61
|
+
EXPECT_NO_THROW(parseSrc("$profile_time = 1;"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
TEST(ProfileTime, ToStringRoundTrips) {
|
|
65
|
+
auto ast = parseSrc("x = profile_time(\"a\") 1 + 2;");
|
|
66
|
+
EXPECT_EQ(ast[0]->toString(), "x = profile_time(\"a\") 1 + 2");
|
|
67
|
+
EXPECT_NE(profileOf(parseSrc(ast[0]->toString() + ";")), nullptr);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
TEST(ProfileTime, JsonRoundTrips) {
|
|
71
|
+
auto ast = parseSrc("x = profile_time(\"a\") 1 + 2;");
|
|
72
|
+
auto back = astFromJsonString(astToJsonString(ast));
|
|
73
|
+
const ProfileTimeOp* p = profileOf(back);
|
|
74
|
+
ASSERT_NE(p, nullptr);
|
|
75
|
+
EXPECT_EQ(p->body->kind(), NodeKind::AdditionOp);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
TEST(ProfileTime, PrettyPrintReparses) {
|
|
79
|
+
for (const char* src : {"x = profile_time(\"a\") 1 + 2;",
|
|
80
|
+
"profile_time(\"g\") { cube(1); }"}) {
|
|
81
|
+
auto ast = parseSrc(src);
|
|
82
|
+
std::string out = toOpenscad(ast);
|
|
83
|
+
EXPECT_NE(out.find("profile_time(\""), std::string::npos) << out;
|
|
84
|
+
EXPECT_NO_THROW(parseSrc(out)) << out;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -71,6 +71,13 @@ struct CSGNode {
|
|
|
71
71
|
// stack risk the moment this node is ever destroyed.
|
|
72
72
|
int treeDepth = 1;
|
|
73
73
|
|
|
74
|
+
// profile_time() only: how long resolving this node (its arguments and
|
|
75
|
+
// its children's script code) took, in ms; < 0 for every other node.
|
|
76
|
+
// Measured fresh on every run, so -- like warnEntry -- deliberately NOT
|
|
77
|
+
// part of cacheKey(): putting a timing in the key would make every
|
|
78
|
+
// profiled subtree a permanent cache miss.
|
|
79
|
+
double profileScriptMs = -1.0;
|
|
80
|
+
|
|
74
81
|
// Memoizes manifold_cache.cpp's cacheKey(*this) -- that function
|
|
75
82
|
// recurses into every descendant to build its own key, so without this
|
|
76
83
|
// memo a generate-time walk that calls cacheKey() at every node (not
|
|
@@ -422,6 +422,22 @@ public:
|
|
|
422
422
|
// subtree, not just direct children, since hull() consumes all of it.
|
|
423
423
|
bool insideHull = false;
|
|
424
424
|
|
|
425
|
+
// profile_time(). generateRequested_ is false only inside an
|
|
426
|
+
// evaluate(generate = false) run, where no geometry pass follows to
|
|
427
|
+
// report a profiled block, so resolve reports the script time alone.
|
|
428
|
+
// profileNodeCount_ counts this run's profile_time() nodes so a cache
|
|
429
|
+
// hit only walks its subtree for them when there are any (see
|
|
430
|
+
// reportCachedProfiles).
|
|
431
|
+
bool generateRequested_ = true;
|
|
432
|
+
std::size_t profileNodeCount_ = 0;
|
|
433
|
+
// Prints "PROFILE: <label>: ..." for a profile_time() node. geometryMs
|
|
434
|
+
// < 0 means no geometry pass; `cached` means its geometry came from the
|
|
435
|
+
// ManifoldCache rather than being built.
|
|
436
|
+
void reportProfile(const CSGNode& node, double geometryMs, bool cached);
|
|
437
|
+
// A cache hit at `node` skips its whole subtree, so a profile_time()
|
|
438
|
+
// inside it would otherwise print nothing at all.
|
|
439
|
+
void reportCachedProfiles(const CSGNode& node);
|
|
440
|
+
|
|
425
441
|
ColoredBody tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue);
|
|
426
442
|
|
|
427
443
|
// tagGenerated()'s counterpart for a mesh Manifold refused to build --
|
|
@@ -837,6 +853,7 @@ private:
|
|
|
837
853
|
Value evalLetExpr(const oscad::LetOp& node, EvalContext& ctx);
|
|
838
854
|
Value evalEchoExpr(const oscad::EchoOp& node, EvalContext& ctx);
|
|
839
855
|
Value evalAssertExpr(const oscad::AssertOp& node, EvalContext& ctx);
|
|
856
|
+
Value evalProfileTimeExpr(const oscad::ProfileTimeOp& node, EvalContext& ctx);
|
|
840
857
|
void bindLetName(EvalContext& ctx, const std::string& name, const Value& v);
|
|
841
858
|
|
|
842
859
|
// Bracketed public in place: Stage 2's Op::NativeStatement
|
|
@@ -146,6 +146,12 @@ std::vector<ColoredBody> generateChildren(Evaluator& ev, const CSGParams& params
|
|
|
146
146
|
// generateTree()'s default child-concatenation, exactly reproducing
|
|
147
147
|
// passthrough).
|
|
148
148
|
CSGParams resolveRender(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
|
|
149
|
+
CSGParams resolveProfileTime(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
|
|
150
|
+
// profile_time()'s message pieces: "12.40 ms" (or "4.7 µs" under 1 ms), and the label it names --
|
|
151
|
+
// the label as written (a non-string formatted like echo() would), or
|
|
152
|
+
// "line N" (with the file when there is one) when none was given.
|
|
153
|
+
std::string fmtMs(double ms);
|
|
154
|
+
std::string profileLabel(const Value& label, const oscad::Position& pos);
|
|
149
155
|
|
|
150
156
|
// breakpoint() -- no-op as of Phase 4 (no debugger until Phase 9); still
|
|
151
157
|
// evaluates/validates its optional `condition` argument for parity.
|
{openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -1018,6 +1018,7 @@ const std::unordered_map<std::string, double>& featureLevels() {
|
|
|
1018
1018
|
{"roof-op", 1.0}, // roof(), method="voronoi" only
|
|
1019
1019
|
{"svg-class", 1.0}, // import(svg, class=); id= is upstream's
|
|
1020
1020
|
{"discretization-by-error", 1.0}, // $fe, always on (upstream: behind --enable)
|
|
1021
|
+
{"profile-time", 1.0}, // profile_time(label) { ... } and profile_time(label) expr
|
|
1021
1022
|
};
|
|
1022
1023
|
return levels;
|
|
1023
1024
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#include "builtins.hpp"
|
|
2
|
+
|
|
3
|
+
#include "openscad_cpp_evaluator/call_args.hpp"
|
|
4
|
+
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
5
|
+
|
|
6
|
+
#include <cstdio>
|
|
7
|
+
|
|
8
|
+
namespace oscadeval {
|
|
9
|
+
|
|
10
|
+
// Microseconds below a millisecond, where "0.00 ms" would hide the answer.
|
|
11
|
+
std::string fmtMs(double ms) {
|
|
12
|
+
char buf[32];
|
|
13
|
+
if (ms < 1.0) std::snprintf(buf, sizeof buf, "%.1f \u00b5s", ms * 1000.0);
|
|
14
|
+
else std::snprintf(buf, sizeof buf, "%.2f ms", ms);
|
|
15
|
+
return buf;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
std::string profileLabel(const Value& label, const oscad::Position& pos) {
|
|
19
|
+
if (const std::string* s = std::get_if<std::string>(&label)) return *s;
|
|
20
|
+
if (!std::holds_alternative<std::monostate>(label)) return fmtValue(label);
|
|
21
|
+
std::string where = "line " + std::to_string(pos.line);
|
|
22
|
+
if (!pos.origin.empty()) where += " of " + pos.origin.substr(pos.origin.find_last_of("/\\") + 1);
|
|
23
|
+
return where;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// profile_time([label]) { ... } -- resolves its children like render() does.
|
|
27
|
+
// The script-time clock is NOT here but around this call in
|
|
28
|
+
// Evaluator::evalModularCall, so it covers argument evaluation and every
|
|
29
|
+
// child alike; the geometry time is taken in generateTreeImpl. The label
|
|
30
|
+
// goes into params (it is constant, so it costs the cache nothing).
|
|
31
|
+
CSGParams resolveProfileTime(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
32
|
+
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
33
|
+
CSGParams params;
|
|
34
|
+
params["label"] = getArg(args, 0, "label");
|
|
35
|
+
EvalContext blockCtx = ev.blockScope(effCtx);
|
|
36
|
+
ev.evalChildren(node.children, blockCtx);
|
|
37
|
+
return params;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void Evaluator::reportProfile(const CSGNode& node, double geometryMs, bool cached) {
|
|
41
|
+
if (!echoFn_ || node.node == nullptr) return;
|
|
42
|
+
auto it = node.params.find("label");
|
|
43
|
+
const std::string label = profileLabel(it == node.params.end() ? Value{} : it->second, node.node->position());
|
|
44
|
+
const double scriptMs = node.profileScriptMs < 0 ? 0.0 : node.profileScriptMs;
|
|
45
|
+
std::string msg = "PROFILE: " + label + ": ";
|
|
46
|
+
if (geometryMs < 0) {
|
|
47
|
+
msg += "script " + fmtMs(scriptMs) + " (no geometry built)";
|
|
48
|
+
} else if (cached) {
|
|
49
|
+
msg += fmtMs(scriptMs) + " (script " + fmtMs(scriptMs) + ", geometry cached)";
|
|
50
|
+
} else {
|
|
51
|
+
msg += fmtMs(scriptMs + geometryMs) + " (script " + fmtMs(scriptMs) + ", geometry " + fmtMs(geometryMs) + ")";
|
|
52
|
+
}
|
|
53
|
+
echoFn_(msg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void Evaluator::reportCachedProfiles(const CSGNode& node) {
|
|
57
|
+
for (const std::unique_ptr<CSGNode>& c : node.children) {
|
|
58
|
+
if (c->isBuiltin && c->kind == "profile_time") reportProfile(*c, 0.0, true);
|
|
59
|
+
reportCachedProfiles(*c);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
} // namespace oscadeval
|
|
@@ -26,6 +26,7 @@ const std::unordered_map<std::string_view, ResolveFn>& resolveDispatch() {
|
|
|
26
26
|
{"intersection", &resolveCsg},
|
|
27
27
|
{"children", &resolveChildren},
|
|
28
28
|
{"render", &resolveRender},
|
|
29
|
+
{"profile_time", &resolveProfileTime},
|
|
29
30
|
{"breakpoint", &resolveBreakpoint},
|
|
30
31
|
{"import", &resolveImport},
|
|
31
32
|
{"hull", &resolveHull},
|
|
@@ -118,6 +119,7 @@ const std::vector<std::string>* builtinParamNames(const std::string& name) {
|
|
|
118
119
|
{"minkowski", {"convexity"}},
|
|
119
120
|
{"children", {"index", "separate"}},
|
|
120
121
|
{"render", {"convexity"}},
|
|
122
|
+
{"profile_time", {"label"}},
|
|
121
123
|
// "repair" and "class" are this port's own additions, not upstream
|
|
122
124
|
// parameters. ("id" IS upstream's, and was declared here long before
|
|
123
125
|
// anything read it -- so a script filtering by id was accepted in
|
|
@@ -263,6 +263,12 @@ private:
|
|
|
263
263
|
expr(*o.body);
|
|
264
264
|
break;
|
|
265
265
|
}
|
|
266
|
+
case NodeKind::ProfileTimeOp: {
|
|
267
|
+
auto& o = static_cast<const oscad::ProfileTimeOp&>(e);
|
|
268
|
+
args(o.arguments);
|
|
269
|
+
expr(*o.body);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
266
272
|
case NodeKind::FunctionLiteral: {
|
|
267
273
|
auto& f = static_cast<const oscad::FunctionLiteral&>(e);
|
|
268
274
|
emit(&e, CoverageKind::Body);
|
|
@@ -36,6 +36,10 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
36
36
|
for (CSGNode* nodePtr : tree) {
|
|
37
37
|
CSGNode& node = *nodePtr;
|
|
38
38
|
checkCancel();
|
|
39
|
+
// profile_time(): timed from before the cache lookup, so a hit
|
|
40
|
+
// still reports (as cached) rather than vanishing.
|
|
41
|
+
const bool profiled = node.isBuiltin && node.kind == "profile_time";
|
|
42
|
+
const auto profileStart = profiled ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
|
|
39
43
|
|
|
40
44
|
// ManifoldCache lookup: node.uncacheable (rands() taint, set
|
|
41
45
|
// during resolve -- see csg_resolve.cpp) always forces a miss,
|
|
@@ -69,6 +73,8 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
69
73
|
// one (BelfrySCAD #521). Goes through emitWarning so an enclosing
|
|
70
74
|
// capture records them too, exactly as a fresh generate would.
|
|
71
75
|
for (const std::string& w : cached->warnings) emitWarning(w);
|
|
76
|
+
// A hit skips the subtree, profile_time() calls and all.
|
|
77
|
+
if (profileNodeCount_ > 0) reportCachedProfiles(node);
|
|
72
78
|
// Skipped entirely while measuring: restampCachedIds writes
|
|
73
79
|
// idToNode/idToColor, and geometry that is about to be discarded
|
|
74
80
|
// has no click-to-source identity worth recording. See
|
|
@@ -171,6 +177,11 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
171
177
|
// capacity, and 35K nodes' worth of 592-byte slots was 230MB.
|
|
172
178
|
for (const std::unique_ptr<CSGNode>& c : node.children) c->bodies = std::vector<ColoredBody>();
|
|
173
179
|
}
|
|
180
|
+
if (profiled) {
|
|
181
|
+
reportProfile(node,
|
|
182
|
+
std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - profileStart).count(),
|
|
183
|
+
cached.has_value());
|
|
184
|
+
}
|
|
174
185
|
for (const ColoredBody& b : node.bodies) topLevelBodies.push_back(b);
|
|
175
186
|
}
|
|
176
187
|
return topLevelBodies;
|
|
@@ -93,10 +93,17 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
|
|
|
93
93
|
treeStack_.emplace_back();
|
|
94
94
|
const std::uint64_t randsBefore = randsCallCount_;
|
|
95
95
|
CSGParams params;
|
|
96
|
+
const bool profiled = hasResolveFn && name == "profile_time";
|
|
97
|
+
double profileScriptMs = -1.0;
|
|
96
98
|
try {
|
|
97
99
|
if (hasResolveFn) {
|
|
98
100
|
warnUnexpectedBuiltinArgs(*this, node);
|
|
101
|
+
const auto profileStart = profiled ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
|
|
99
102
|
params = it->second(*this, node, ctx);
|
|
103
|
+
if (profiled) {
|
|
104
|
+
profileScriptMs =
|
|
105
|
+
std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - profileStart).count();
|
|
106
|
+
}
|
|
100
107
|
} else if (!isBuiltin) {
|
|
101
108
|
evalUserModule(static_cast<const oscad::ModuleDeclaration&>(*userModuleDecl), node, ctx);
|
|
102
109
|
} else {
|
|
@@ -144,7 +151,13 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
|
|
|
144
151
|
treeNode->uncacheable = uncacheable;
|
|
145
152
|
treeNode->children = std::move(children);
|
|
146
153
|
treeNode->params = std::move(params);
|
|
154
|
+
treeNode->profileScriptMs = profileScriptMs;
|
|
147
155
|
setTreeDepthOrThrow(*treeNode, node);
|
|
156
|
+
if (profiled) {
|
|
157
|
+
++profileNodeCount_;
|
|
158
|
+
// No geometry pass will follow to report it, so report it now.
|
|
159
|
+
if (!generateRequested_) reportProfile(*treeNode, -1.0, false);
|
|
160
|
+
}
|
|
148
161
|
treeStack_.back().push_back(std::move(treeNode));
|
|
149
162
|
}
|
|
150
163
|
|
|
@@ -271,6 +284,12 @@ std::vector<ColoredBody> Evaluator::evaluateImpl(const NodeList& nodes, EvalCont
|
|
|
271
284
|
|
|
272
285
|
using Clock = std::chrono::steady_clock;
|
|
273
286
|
const Clock::time_point resolveStart = profiling_ ? Clock::now() : Clock::time_point{};
|
|
287
|
+
profileNodeCount_ = 0;
|
|
288
|
+
generateRequested_ = generate;
|
|
289
|
+
struct GenerateRequestedGuard {
|
|
290
|
+
bool& flag;
|
|
291
|
+
~GenerateRequestedGuard() { flag = true; }
|
|
292
|
+
} generateRequestedGuard{generateRequested_};
|
|
274
293
|
std::vector<std::unique_ptr<CSGNode>> tree = resolveTreeImpl(nodes, ctx);
|
|
275
294
|
rerootAtShowOnly(tree);
|
|
276
295
|
const Clock::time_point resolveEnd = profiling_ ? Clock::now() : Clock::time_point{};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include "openscad_cpp_evaluator/utf8.hpp"
|
|
3
3
|
|
|
4
4
|
#include "openscad_cpp_evaluator/call_args.hpp"
|
|
5
|
+
#include "builtins/builtins.hpp"
|
|
5
6
|
#include "openscad_cpp_evaluator/eval_error.hpp"
|
|
6
7
|
#include "openscad_cpp_evaluator/freetype_font_provider.hpp"
|
|
7
8
|
|
|
@@ -444,6 +445,18 @@ Value Evaluator::evalEchoExpr(const oscad::EchoOp& node, EvalContext& ctx) {
|
|
|
444
445
|
return evalExpr(*node.body, ctx);
|
|
445
446
|
}
|
|
446
447
|
|
|
448
|
+
// `x = profile_time("label") expr;` -- times evaluating `expr` and prints
|
|
449
|
+
// it. The label is evaluated before the clock starts, so it isn't counted.
|
|
450
|
+
Value Evaluator::evalProfileTimeExpr(const oscad::ProfileTimeOp& node, EvalContext& ctx) {
|
|
451
|
+
checkDebug(node, ctx);
|
|
452
|
+
const Value label = getArg(resolveArgs(*this, node.arguments, ctx), 0, "label");
|
|
453
|
+
const auto start = std::chrono::steady_clock::now();
|
|
454
|
+
Value result = evalExpr(*node.body, ctx);
|
|
455
|
+
const double ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
|
|
456
|
+
if (echoFn_) echoFn_("PROFILE: " + profileLabel(label, node.position()) + ": " + fmtMs(ms));
|
|
457
|
+
return result;
|
|
458
|
+
}
|
|
459
|
+
|
|
447
460
|
Value Evaluator::evalAssertExpr(const oscad::AssertOp& node, EvalContext& ctx) {
|
|
448
461
|
// Unlike the statement form (evalAssertStatement), which supports named
|
|
449
462
|
// arguments via getArg()/CallArgs, the expression form indexes raw
|
|
@@ -518,6 +531,8 @@ Value Evaluator::evalExpr(const oscad::Expression& node, EvalContext& ctx) {
|
|
|
518
531
|
return evalEchoExpr(static_cast<const oscad::EchoOp&>(node), ctx);
|
|
519
532
|
case NodeKind::AssertOp:
|
|
520
533
|
return evalAssertExpr(static_cast<const oscad::AssertOp&>(node), ctx);
|
|
534
|
+
case NodeKind::ProfileTimeOp:
|
|
535
|
+
return evalProfileTimeExpr(static_cast<const oscad::ProfileTimeOp&>(node), ctx);
|
|
521
536
|
|
|
522
537
|
case NodeKind::PrimaryIndex: {
|
|
523
538
|
auto& n = static_cast<const oscad::PrimaryIndex&>(node);
|