openscad-cpp-evaluator 0.15.0__tar.gz → 0.17.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.15.0 → openscad_cpp_evaluator-0.17.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/bindings/CMakeLists.txt +1 -0
- openscad_cpp_evaluator-0.17.0/bindings/ast_to_py.cpp +326 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/bindings/module.cpp +14 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/csg_node.hpp +16 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/eval_error.hpp +12 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/evaluator.hpp +18 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/python/openscad_cpp_evaluator/__init__.py +36 -1
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/bytecode_vm.cpp +2 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/csg_generate.cpp +7 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/csg_resolve.cpp +3 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/eval_error.cpp +44 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/expr_eval.cpp +19 -1
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_bytecode_compiler.cpp +11 -3
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_csg_tree.cpp +70 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_python_bindings.py +116 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/uv.lock +0 -0
|
@@ -15,6 +15,7 @@ find_package(nanobind CONFIG REQUIRED)
|
|
|
15
15
|
# from-source Manifold/Boost rebuild per interpreter in CI.
|
|
16
16
|
nanobind_add_module(_openscad_cpp_evaluator NB_STATIC STABLE_ABI
|
|
17
17
|
module.cpp
|
|
18
|
+
ast_to_py.cpp
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
# The static evaluator lib PUBLIC-links parser + manifold + Boost::polygon,
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// Snapshots the parser's AST into plain Python dicts.
|
|
2
|
+
//
|
|
3
|
+
// Deliberately a COPY rather than wrappers around live ASTNode pointers.
|
|
4
|
+
// The tree is owned by a `std::vector<std::unique_ptr<ASTNode>>` local to
|
|
5
|
+
// whichever binding parsed it, and dies when that call returns; handing
|
|
6
|
+
// Python raw pointers into it would be a use-after-free the moment anything
|
|
7
|
+
// outlived the call, and this project has already shipped one dangling
|
|
8
|
+
// pointer of exactly that shape (the bodyCtx bug fixed in v0.13.1). It also
|
|
9
|
+
// sidesteps the threading problem entirely -- a render worker thread can
|
|
10
|
+
// parse, hand the snapshot to the GUI thread, and exit, with nothing shared.
|
|
11
|
+
//
|
|
12
|
+
// The cost is one walk plus allocation per parse. That is affordable
|
|
13
|
+
// because callers ask for this explicitly (parse_ast()), not on the render
|
|
14
|
+
// path, which never needs it.
|
|
15
|
+
//
|
|
16
|
+
// Every node dict carries at least:
|
|
17
|
+
// kind -- str, the NodeKind name ("NumberLiteral", "ModularCall", ...)
|
|
18
|
+
// position -- dict(origin, line, column, start_offset, end_offset)
|
|
19
|
+
// plus that kind's own fields, named in snake_case. Child nodes are nested
|
|
20
|
+
// dicts; child lists are lists of dicts; an absent optional child is None.
|
|
21
|
+
//
|
|
22
|
+
// `position.start_offset`/`end_offset` are the point of the whole exercise:
|
|
23
|
+
// they let a caller recover the ORIGINAL source text of any subexpression
|
|
24
|
+
// and reuse it verbatim, rather than re-serialising a parsed value and
|
|
25
|
+
// losing how the author wrote it (1.500 -> 1.5, 1e3 -> 1000).
|
|
26
|
+
|
|
27
|
+
#include "openscad_cpp_parser/api.hpp"
|
|
28
|
+
#include "openscad_cpp_parser/ast/ast_node.hpp"
|
|
29
|
+
#include "openscad_cpp_parser/ast/comments.hpp"
|
|
30
|
+
#include "openscad_cpp_parser/ast/declarations.hpp"
|
|
31
|
+
#include "openscad_cpp_parser/ast/expression.hpp"
|
|
32
|
+
#include "openscad_cpp_parser/ast/module_instantiation.hpp"
|
|
33
|
+
#include "openscad_cpp_parser/ast/vector_element.hpp"
|
|
34
|
+
|
|
35
|
+
#include <nanobind/nanobind.h>
|
|
36
|
+
#include <nanobind/stl/string.h>
|
|
37
|
+
|
|
38
|
+
#include <memory>
|
|
39
|
+
#include <vector>
|
|
40
|
+
|
|
41
|
+
namespace nb = nanobind;
|
|
42
|
+
|
|
43
|
+
namespace oscadbind {
|
|
44
|
+
|
|
45
|
+
namespace {
|
|
46
|
+
|
|
47
|
+
nb::dict positionToPy(const oscad::Position& p) {
|
|
48
|
+
nb::dict d;
|
|
49
|
+
// origin is "" for the main file (matching how the evaluator reports
|
|
50
|
+
// it); left as-is rather than mapped to None, so a caller can compare
|
|
51
|
+
// it against a path without a null check.
|
|
52
|
+
d["origin"] = p.origin;
|
|
53
|
+
d["line"] = p.line;
|
|
54
|
+
d["column"] = p.column;
|
|
55
|
+
d["start_offset"] = p.start_offset;
|
|
56
|
+
d["end_offset"] = p.end_offset;
|
|
57
|
+
return d;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
nb::object nodeToPy(const oscad::ASTNode* n);
|
|
61
|
+
|
|
62
|
+
// A null unique_ptr child (e.g. a parameter with no default) becomes None
|
|
63
|
+
// rather than being omitted, so a consumer can index the key unconditionally.
|
|
64
|
+
template <typename T>
|
|
65
|
+
nb::object childToPy(const std::unique_ptr<T>& p) {
|
|
66
|
+
return p ? nodeToPy(p.get()) : nb::none();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
template <typename T>
|
|
70
|
+
nb::list listToPy(const std::vector<std::unique_ptr<T>>& v) {
|
|
71
|
+
nb::list out;
|
|
72
|
+
for (const std::unique_ptr<T>& item : v) out.append(nodeToPy(item.get()));
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
nb::dict baseDict(const oscad::ASTNode* n) {
|
|
77
|
+
nb::dict d;
|
|
78
|
+
d["kind"] = oscad::nodeKindName(n->kind());
|
|
79
|
+
d["position"] = positionToPy(n->position());
|
|
80
|
+
return d;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
nb::object nodeToPy(const oscad::ASTNode* n) {
|
|
84
|
+
if (n == nullptr) return nb::none();
|
|
85
|
+
nb::dict d = baseDict(n);
|
|
86
|
+
|
|
87
|
+
// kind() is authoritative about the concrete type (every leaf class sets
|
|
88
|
+
// its own), so static_cast is safe here and avoids a dynamic_cast per
|
|
89
|
+
// node on what is already an allocation-heavy walk.
|
|
90
|
+
#define OSC_NODE(KindName, Type) case oscad::NodeKind::KindName: { const auto* x = static_cast<const oscad::Type*>(n); (void)x;
|
|
91
|
+
#define OSC_END break; }
|
|
92
|
+
|
|
93
|
+
switch (n->kind()) {
|
|
94
|
+
// -- comments ---------------------------------------------------
|
|
95
|
+
OSC_NODE(CommentLine, CommentLine) d["text"] = x->text; OSC_END
|
|
96
|
+
OSC_NODE(BlankLine, BlankLine) OSC_END
|
|
97
|
+
OSC_NODE(CommentSpan, CommentSpan) d["text"] = x->text; OSC_END
|
|
98
|
+
OSC_NODE(CommentedExpr, CommentedExpr)
|
|
99
|
+
d["leading_comments"] = listToPy(x->leadingComments);
|
|
100
|
+
d["trailing_comments"] = listToPy(x->trailingComments);
|
|
101
|
+
d["expr"] = childToPy(x->expr);
|
|
102
|
+
OSC_END
|
|
103
|
+
|
|
104
|
+
// -- primaries / literals ---------------------------------------
|
|
105
|
+
OSC_NODE(Identifier, Identifier) d["name"] = x->name; OSC_END
|
|
106
|
+
// `val` is the DECODED string minus its quotes; escape sequences
|
|
107
|
+
// are kept raw (a source \n stays two characters). Use the span to
|
|
108
|
+
// recover exactly what was written.
|
|
109
|
+
OSC_NODE(StringLiteral, StringLiteral) d["val"] = x->val; OSC_END
|
|
110
|
+
// Parsed to double, so the source spelling (1.500, 1e3, 0x10) is
|
|
111
|
+
// NOT recoverable from here -- that is what the span is for.
|
|
112
|
+
OSC_NODE(NumberLiteral, NumberLiteral) d["val"] = x->val; OSC_END
|
|
113
|
+
OSC_NODE(BooleanLiteral, BooleanLiteral) d["val"] = x->val; OSC_END
|
|
114
|
+
OSC_NODE(UndefinedLiteral, UndefinedLiteral) OSC_END
|
|
115
|
+
OSC_NODE(RangeLiteral, RangeLiteral)
|
|
116
|
+
d["start"] = childToPy(x->start);
|
|
117
|
+
d["end"] = childToPy(x->end);
|
|
118
|
+
// Never null: the parser defaults an omitted step to 1.
|
|
119
|
+
d["step"] = childToPy(x->step);
|
|
120
|
+
OSC_END
|
|
121
|
+
|
|
122
|
+
// -- declarations / arguments -----------------------------------
|
|
123
|
+
OSC_NODE(ParameterDeclaration, ParameterDeclaration)
|
|
124
|
+
d["name"] = childToPy(x->name);
|
|
125
|
+
d["default_value"] = childToPy(x->defaultValue); // nullable
|
|
126
|
+
d["leading_comments"] = listToPy(x->leadingComments);
|
|
127
|
+
d["trailing_comments"] = listToPy(x->trailingComments);
|
|
128
|
+
OSC_END
|
|
129
|
+
OSC_NODE(PositionalArgument, PositionalArgument) d["expr"] = childToPy(x->expr); OSC_END
|
|
130
|
+
OSC_NODE(NamedArgument, NamedArgument)
|
|
131
|
+
d["name"] = childToPy(x->name);
|
|
132
|
+
d["expr"] = childToPy(x->expr);
|
|
133
|
+
OSC_END
|
|
134
|
+
OSC_NODE(Assignment, Assignment)
|
|
135
|
+
d["name"] = childToPy(x->name);
|
|
136
|
+
d["expr"] = childToPy(x->expr);
|
|
137
|
+
OSC_END
|
|
138
|
+
|
|
139
|
+
// -- prefix expression forms ------------------------------------
|
|
140
|
+
OSC_NODE(LetOp, LetOp)
|
|
141
|
+
d["assignments"] = listToPy(x->assignments);
|
|
142
|
+
d["body"] = childToPy(x->body);
|
|
143
|
+
OSC_END
|
|
144
|
+
OSC_NODE(EchoOp, EchoOp)
|
|
145
|
+
d["arguments"] = listToPy(x->arguments);
|
|
146
|
+
d["body"] = childToPy(x->body);
|
|
147
|
+
OSC_END
|
|
148
|
+
OSC_NODE(AssertOp, AssertOp)
|
|
149
|
+
d["arguments"] = listToPy(x->arguments);
|
|
150
|
+
d["body"] = childToPy(x->body);
|
|
151
|
+
OSC_END
|
|
152
|
+
OSC_NODE(FunctionLiteral, FunctionLiteral)
|
|
153
|
+
d["parameters"] = listToPy(x->parameters);
|
|
154
|
+
d["body"] = childToPy(x->body);
|
|
155
|
+
OSC_END
|
|
156
|
+
|
|
157
|
+
// -- unary operators --------------------------------------------
|
|
158
|
+
#define OSC_UNARY(KindName) OSC_NODE(KindName, KindName) d["expr"] = childToPy(x->expr); OSC_END
|
|
159
|
+
OSC_UNARY(UnaryMinusOp)
|
|
160
|
+
OSC_UNARY(LogicalNotOp)
|
|
161
|
+
OSC_UNARY(BitwiseNotOp)
|
|
162
|
+
#undef OSC_UNARY
|
|
163
|
+
|
|
164
|
+
// -- binary operators -------------------------------------------
|
|
165
|
+
#define OSC_BINARY(KindName) \
|
|
166
|
+
OSC_NODE(KindName, KindName) d["left"] = childToPy(x->left); d["right"] = childToPy(x->right); OSC_END
|
|
167
|
+
OSC_BINARY(AdditionOp)
|
|
168
|
+
OSC_BINARY(SubtractionOp)
|
|
169
|
+
OSC_BINARY(MultiplicationOp)
|
|
170
|
+
OSC_BINARY(DivisionOp)
|
|
171
|
+
OSC_BINARY(ModuloOp)
|
|
172
|
+
OSC_BINARY(ExponentOp)
|
|
173
|
+
OSC_BINARY(BitwiseAndOp)
|
|
174
|
+
OSC_BINARY(BitwiseOrOp)
|
|
175
|
+
OSC_BINARY(BitwiseShiftLeftOp)
|
|
176
|
+
OSC_BINARY(BitwiseShiftRightOp)
|
|
177
|
+
OSC_BINARY(LogicalAndOp)
|
|
178
|
+
OSC_BINARY(LogicalOrOp)
|
|
179
|
+
OSC_BINARY(EqualityOp)
|
|
180
|
+
OSC_BINARY(InequalityOp)
|
|
181
|
+
OSC_BINARY(GreaterThanOp)
|
|
182
|
+
OSC_BINARY(GreaterThanOrEqualOp)
|
|
183
|
+
OSC_BINARY(LessThanOp)
|
|
184
|
+
OSC_BINARY(LessThanOrEqualOp)
|
|
185
|
+
#undef OSC_BINARY
|
|
186
|
+
|
|
187
|
+
OSC_NODE(TernaryOp, TernaryOp)
|
|
188
|
+
d["condition"] = childToPy(x->condition);
|
|
189
|
+
d["true_expr"] = childToPy(x->trueExpr);
|
|
190
|
+
d["false_expr"] = childToPy(x->falseExpr);
|
|
191
|
+
OSC_END
|
|
192
|
+
|
|
193
|
+
// -- postfix ----------------------------------------------------
|
|
194
|
+
OSC_NODE(PrimaryCall, PrimaryCall)
|
|
195
|
+
d["left"] = childToPy(x->left);
|
|
196
|
+
d["arguments"] = listToPy(x->arguments);
|
|
197
|
+
OSC_END
|
|
198
|
+
OSC_NODE(PrimaryIndex, PrimaryIndex)
|
|
199
|
+
d["left"] = childToPy(x->left);
|
|
200
|
+
d["index"] = childToPy(x->index);
|
|
201
|
+
OSC_END
|
|
202
|
+
OSC_NODE(PrimaryMember, PrimaryMember)
|
|
203
|
+
d["left"] = childToPy(x->left);
|
|
204
|
+
d["member"] = childToPy(x->member);
|
|
205
|
+
OSC_END
|
|
206
|
+
|
|
207
|
+
// -- list comprehension clauses ---------------------------------
|
|
208
|
+
OSC_NODE(ListCompLet, ListCompLet)
|
|
209
|
+
d["assignments"] = listToPy(x->assignments);
|
|
210
|
+
d["body"] = childToPy(x->body);
|
|
211
|
+
OSC_END
|
|
212
|
+
OSC_NODE(ListCompEach, ListCompEach) d["body"] = childToPy(x->body); OSC_END
|
|
213
|
+
OSC_NODE(ListCompFor, ListCompFor)
|
|
214
|
+
d["assignments"] = listToPy(x->assignments);
|
|
215
|
+
d["body"] = childToPy(x->body);
|
|
216
|
+
OSC_END
|
|
217
|
+
OSC_NODE(ListCompCFor, ListCompCFor)
|
|
218
|
+
d["inits"] = listToPy(x->inits);
|
|
219
|
+
d["condition"] = childToPy(x->condition);
|
|
220
|
+
d["incrs"] = listToPy(x->incrs);
|
|
221
|
+
d["body"] = childToPy(x->body);
|
|
222
|
+
OSC_END
|
|
223
|
+
OSC_NODE(ListCompIf, ListCompIf)
|
|
224
|
+
d["condition"] = childToPy(x->condition);
|
|
225
|
+
d["true_expr"] = childToPy(x->trueExpr);
|
|
226
|
+
OSC_END
|
|
227
|
+
OSC_NODE(ListCompIfElse, ListCompIfElse)
|
|
228
|
+
d["condition"] = childToPy(x->condition);
|
|
229
|
+
d["true_expr"] = childToPy(x->trueExpr);
|
|
230
|
+
d["false_expr"] = childToPy(x->falseExpr);
|
|
231
|
+
OSC_END
|
|
232
|
+
// Also how a plain vector literal ([1,2,3]) is represented --
|
|
233
|
+
// elements mix comprehension clauses and bare expressions.
|
|
234
|
+
OSC_NODE(ListComprehension, ListComprehension) d["elements"] = listToPy(x->elements); OSC_END
|
|
235
|
+
|
|
236
|
+
// -- module instantiation ---------------------------------------
|
|
237
|
+
OSC_NODE(ModularCall, ModularCall)
|
|
238
|
+
d["name"] = childToPy(x->name);
|
|
239
|
+
d["arguments"] = listToPy(x->arguments);
|
|
240
|
+
d["children"] = listToPy(x->children);
|
|
241
|
+
OSC_END
|
|
242
|
+
OSC_NODE(ModularFor, ModularFor)
|
|
243
|
+
d["assignments"] = listToPy(x->assignments);
|
|
244
|
+
d["body"] = listToPy(x->body);
|
|
245
|
+
OSC_END
|
|
246
|
+
OSC_NODE(ModularIntersectionFor, ModularIntersectionFor)
|
|
247
|
+
d["assignments"] = listToPy(x->assignments);
|
|
248
|
+
d["body"] = listToPy(x->body);
|
|
249
|
+
OSC_END
|
|
250
|
+
OSC_NODE(ModularLet, ModularLet)
|
|
251
|
+
d["assignments"] = listToPy(x->assignments);
|
|
252
|
+
d["children"] = listToPy(x->children);
|
|
253
|
+
OSC_END
|
|
254
|
+
OSC_NODE(ModularEcho, ModularEcho)
|
|
255
|
+
d["arguments"] = listToPy(x->arguments);
|
|
256
|
+
d["children"] = listToPy(x->children);
|
|
257
|
+
OSC_END
|
|
258
|
+
OSC_NODE(ModularAssert, ModularAssert)
|
|
259
|
+
d["arguments"] = listToPy(x->arguments);
|
|
260
|
+
d["children"] = listToPy(x->children);
|
|
261
|
+
OSC_END
|
|
262
|
+
OSC_NODE(ModularIf, ModularIf)
|
|
263
|
+
d["condition"] = childToPy(x->condition);
|
|
264
|
+
d["true_branch"] = listToPy(x->trueBranch);
|
|
265
|
+
OSC_END
|
|
266
|
+
OSC_NODE(ModularIfElse, ModularIfElse)
|
|
267
|
+
d["condition"] = childToPy(x->condition);
|
|
268
|
+
d["true_branch"] = listToPy(x->trueBranch);
|
|
269
|
+
d["false_branch"] = listToPy(x->falseBranch);
|
|
270
|
+
OSC_END
|
|
271
|
+
|
|
272
|
+
// -- modifier wrappers (! # % *) --------------------------------
|
|
273
|
+
#define OSC_MODIFIER(KindName) OSC_NODE(KindName, KindName) d["child"] = childToPy(x->child); OSC_END
|
|
274
|
+
OSC_MODIFIER(ModularModifierShowOnly)
|
|
275
|
+
OSC_MODIFIER(ModularModifierHighlight)
|
|
276
|
+
OSC_MODIFIER(ModularModifierBackground)
|
|
277
|
+
OSC_MODIFIER(ModularModifierDisable)
|
|
278
|
+
#undef OSC_MODIFIER
|
|
279
|
+
|
|
280
|
+
// -- top-level declarations -------------------------------------
|
|
281
|
+
OSC_NODE(ModuleDeclaration, ModuleDeclaration)
|
|
282
|
+
d["name"] = childToPy(x->name);
|
|
283
|
+
d["parameters"] = listToPy(x->parameters);
|
|
284
|
+
d["children"] = listToPy(x->children);
|
|
285
|
+
d["pre_name_comments"] = listToPy(x->preNameComments);
|
|
286
|
+
d["post_name_comments"] = listToPy(x->postNameComments);
|
|
287
|
+
d["post_params_comments"] = listToPy(x->postParamsComments);
|
|
288
|
+
OSC_END
|
|
289
|
+
OSC_NODE(FunctionDeclaration, FunctionDeclaration)
|
|
290
|
+
d["name"] = childToPy(x->name);
|
|
291
|
+
d["parameters"] = listToPy(x->parameters);
|
|
292
|
+
d["expr"] = childToPy(x->expr);
|
|
293
|
+
d["pre_name_comments"] = listToPy(x->preNameComments);
|
|
294
|
+
d["post_name_comments"] = listToPy(x->postNameComments);
|
|
295
|
+
d["post_params_comments"] = listToPy(x->postParamsComments);
|
|
296
|
+
OSC_END
|
|
297
|
+
OSC_NODE(UseStatement, UseStatement) d["filepath"] = childToPy(x->filepath); OSC_END
|
|
298
|
+
OSC_NODE(IncludeStatement, IncludeStatement) d["filepath"] = childToPy(x->filepath); OSC_END
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
#undef OSC_NODE
|
|
302
|
+
#undef OSC_END
|
|
303
|
+
|
|
304
|
+
// No default: above -- the switch is exhaustive over NodeKind, so
|
|
305
|
+
// adding a node kind to the parser without handling it here is a
|
|
306
|
+
// compiler warning rather than a silently field-less dict.
|
|
307
|
+
return d;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
} // namespace
|
|
311
|
+
|
|
312
|
+
nb::list astToPy(const std::vector<std::unique_ptr<oscad::ASTNode>>& ast) {
|
|
313
|
+
nb::list out;
|
|
314
|
+
for (const std::unique_ptr<oscad::ASTNode>& n : ast) out.append(nodeToPy(n.get()));
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
nb::list parseAstFromFile(const std::string& path, bool includeComments) {
|
|
319
|
+
return astToPy(oscad::getASTFromFile(path, includeComments));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
nb::list parseAstFromString(const std::string& code, bool includeComments) {
|
|
323
|
+
return astToPy(oscad::getASTFromString(code, includeComments));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
} // namespace oscadbind
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
|
|
41
41
|
namespace nb = nanobind;
|
|
42
42
|
|
|
43
|
+
// Defined in ast_to_py.cpp -- snapshots the parser's AST into plain Python
|
|
44
|
+
// dicts. Declared here rather than in a header since these two entry points
|
|
45
|
+
// are the whole interface and only this file calls them.
|
|
46
|
+
namespace oscadbind {
|
|
47
|
+
nb::list parseAstFromFile(const std::string& path, bool includeComments);
|
|
48
|
+
nb::list parseAstFromString(const std::string& code, bool includeComments);
|
|
49
|
+
} // namespace oscadbind
|
|
50
|
+
|
|
43
51
|
namespace {
|
|
44
52
|
|
|
45
53
|
// Moves a std::vector's storage into a NumPy array that owns it via a
|
|
@@ -649,6 +657,12 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
649
657
|
"Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit).");
|
|
650
658
|
m.def("parse_decls", &parseDecls, nb::arg("path"),
|
|
651
659
|
"Parse a .scad file; return top-level declaration (namespace, name, start, end, line, column, origin) tuples.");
|
|
660
|
+
m.def("parse_ast", &oscadbind::parseAstFromFile, nb::arg("path"), nb::arg("include_comments") = false,
|
|
661
|
+
"Parse a .scad file; return the AST as a list of plain nested dicts "
|
|
662
|
+
"(kind, position, plus that kind's own fields). A snapshot, not a view -- "
|
|
663
|
+
"safe to keep, share across threads, and outlive the parse.");
|
|
664
|
+
m.def("parse_ast_string", &oscadbind::parseAstFromString, nb::arg("code"), nb::arg("include_comments") = false,
|
|
665
|
+
"As parse_ast(), but parses a source string instead of a file.");
|
|
652
666
|
m.def("debug_evaluate", &debugEvaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("debug_hook"),
|
|
653
667
|
nb::arg("error_break"), nb::arg("echo_fn"), nb::arg("manifold_cache") = nullptr,
|
|
654
668
|
nb::arg("return_hook") = nb::none(), nb::arg("fast_continue_signal") = nullptr,
|
|
@@ -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
|
|
@@ -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 --
|
|
@@ -18,7 +18,7 @@ from ._openscad_cpp_evaluator import FastContinueSignal, ManifoldCache
|
|
|
18
18
|
__all__ = [
|
|
19
19
|
"Evaluator", "ColoredBody", "EvalError", "ParseError", "OscObject", "parse", "to_renderable_bodies",
|
|
20
20
|
"ManifoldCache", "CallSiteProfile", "ProfileResult", "format_csg_tree", "bodies_from_dicts",
|
|
21
|
-
"FastContinueSignal",
|
|
21
|
+
"FastContinueSignal", "parse_ast", "parse_ast_string",
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
|
|
@@ -256,6 +256,41 @@ def parse(path: str) -> RootScope:
|
|
|
256
256
|
return RootScope(decls)
|
|
257
257
|
|
|
258
258
|
|
|
259
|
+
def parse_ast(path: str, include_comments: bool = False) -> list:
|
|
260
|
+
"""Parse a .scad file; return its AST as a list of plain nested dicts.
|
|
261
|
+
|
|
262
|
+
Each node is `{"kind": str, "position": {...}, **fields}`, where fields
|
|
263
|
+
are that kind's own (e.g. NumberLiteral has "val"; ModularCall has
|
|
264
|
+
"name"/"arguments"/"children"). Child nodes nest as dicts, child lists
|
|
265
|
+
as lists, and an absent optional child is None.
|
|
266
|
+
|
|
267
|
+
`position` carries `start_offset`/`end_offset`, which is the point of
|
|
268
|
+
this API: they let a caller slice the ORIGINAL source text of any
|
|
269
|
+
subexpression and reuse it verbatim, instead of re-serialising a parsed
|
|
270
|
+
value and losing how it was written (`1.500` -> `1.5`, `1e3` -> `1000`).
|
|
271
|
+
|
|
272
|
+
This is a SNAPSHOT, not a view of live parser memory: it is safe to
|
|
273
|
+
keep, mutate, share across threads, and outlive the parse. That costs
|
|
274
|
+
one walk per call, which is why it is a separate entry point rather
|
|
275
|
+
than something evaluate() always pays for.
|
|
276
|
+
|
|
277
|
+
Raises ParseError on a syntax error.
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
return _ext.parse_ast(path, include_comments)
|
|
281
|
+
except Exception as e:
|
|
282
|
+
raise ParseError(str(e)) from e
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def parse_ast_string(code: str, include_comments: bool = False) -> list:
|
|
286
|
+
"""As `parse_ast`, but parses a source string. `position.origin` is
|
|
287
|
+
"<string>" for every node. Raises ParseError on a syntax error."""
|
|
288
|
+
try:
|
|
289
|
+
return _ext.parse_ast_string(code, include_comments)
|
|
290
|
+
except Exception as e:
|
|
291
|
+
raise ParseError(str(e)) from e
|
|
292
|
+
|
|
293
|
+
|
|
259
294
|
class _MeshShim:
|
|
260
295
|
"""Duck-types a manifold3d mesh: the four arrays the renderer reads."""
|
|
261
296
|
__slots__ = ("vert_properties", "tri_verts", "run_original_id", "run_index", "num_prop")
|
|
@@ -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);
|
|
@@ -78,4 +78,48 @@ std::string formatError(const std::string& msg, const oscad::Position* nodePosit
|
|
|
78
78
|
return full;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
std::string formatWarning(const std::string& msg, const oscad::Position* nodePosition,
|
|
82
|
+
const std::vector<CallStackFrame>& callStack) {
|
|
83
|
+
// The warning keeps its OWN location in the headline: that is where the
|
|
84
|
+
// problem actually is, and it's what you need in order to fix a genuine
|
|
85
|
+
// library bug. But on its own, a warning raised deep inside a library
|
|
86
|
+
// ("in file BOSL2/vnf.scad, line 1624") is close to useless -- the
|
|
87
|
+
// reader's next question is always "which line of MINE caused that?",
|
|
88
|
+
// and nothing in the message answered it.
|
|
89
|
+
//
|
|
90
|
+
// callStack is outermost-first, so front() is the call that entered
|
|
91
|
+
// this chain from the top level -- the user's own line, however deep
|
|
92
|
+
// the warning surfaced. Naming it inline makes the common case a
|
|
93
|
+
// one-line answer; the TRACE lines below carry the full chain for when
|
|
94
|
+
// an intermediate frame is the real culprit.
|
|
95
|
+
//
|
|
96
|
+
// Real OpenSCAD reports only the raising location and no trace at all
|
|
97
|
+
// (verified against 2022.08.22), so this is deliberately more than the
|
|
98
|
+
// reference gives: warnings there routinely point into library
|
|
99
|
+
// internals with no way back to the caller.
|
|
100
|
+
std::string full = "WARNING: " + msg + locSuffix(nodePosition);
|
|
101
|
+
|
|
102
|
+
if (!callStack.empty()) {
|
|
103
|
+
const oscad::Position* entry = callStack.front().callPosition;
|
|
104
|
+
// Skip the clause when it would only repeat the headline (a warning
|
|
105
|
+
// raised directly at the top-level call site), or when the entry
|
|
106
|
+
// frame recorded no call position.
|
|
107
|
+
const bool sameSpot = entry != nullptr && nodePosition != nullptr &&
|
|
108
|
+
entry->origin == nodePosition->origin && entry->line == nodePosition->line;
|
|
109
|
+
if (entry != nullptr && !sameSpot) {
|
|
110
|
+
full += ", from " + entry->origin + ", line " + std::to_string(entry->line);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Same shape and truncation as an error's trace, including the
|
|
115
|
+
// 10-frames-each-end bound for pathologically deep chains. A warning
|
|
116
|
+
// raised outside any call has an empty stack and stays a single line --
|
|
117
|
+
// the overwhelmingly common case, which keeps ordinary warnings from
|
|
118
|
+
// sprouting a trace nobody needs.
|
|
119
|
+
for (const std::string& line : traceLines(nodePosition, callStack, "")) {
|
|
120
|
+
full += "\n" + line;
|
|
121
|
+
}
|
|
122
|
+
return full;
|
|
123
|
+
}
|
|
124
|
+
|
|
81
125
|
} // namespace oscadeval
|
|
@@ -95,7 +95,25 @@ FontProvider& Evaluator::fontProvider() {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
void Evaluator::warn(const std::string& message, const oscad::Position* position) {
|
|
98
|
-
if (echoFn_)
|
|
98
|
+
if (!echoFn_) return;
|
|
99
|
+
// During generate the call stack has already unwound, so fall back to
|
|
100
|
+
// the entry position resolve captured onto the CSGNode being generated
|
|
101
|
+
// (see CSGNode::warnEntry). Passed as a synthetic single-frame stack so
|
|
102
|
+
// formatWarning needs no second code path -- the frame carries no name,
|
|
103
|
+
// which traceLines renders as a bare "called by ''" line, so it is
|
|
104
|
+
// deliberately NOT included there; only the headline's "from" clause
|
|
105
|
+
// uses it.
|
|
106
|
+
if (callStack_.empty() && generateWarnEntry != nullptr) {
|
|
107
|
+
std::string out = formatWarning(message, position, {});
|
|
108
|
+
const bool sameSpot = position != nullptr && generateWarnEntry->origin == position->origin &&
|
|
109
|
+
generateWarnEntry->line == position->line;
|
|
110
|
+
if (!sameSpot) {
|
|
111
|
+
out += ", from " + generateWarnEntry->origin + ", line " + std::to_string(generateWarnEntry->line);
|
|
112
|
+
}
|
|
113
|
+
echoFn_(out);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
echoFn_(formatWarning(message, position, callStack_));
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
Value Evaluator::evalIdentifier(const std::string& name, const oscad::Position* position, EvalContext& ctx,
|
{openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.17.0}/tests/test_bytecode_compiler.cpp
RENAMED
|
@@ -1623,9 +1623,17 @@ TEST(ModuleBodyCompiles, DollarArgPropagatesIntoCompiledHullWrapChildren) {
|
|
|
1623
1623
|
// side effect to preserve).
|
|
1624
1624
|
TEST(ModuleBodyCompiles, CompiledRoofUnknownMethodWarningStaysOrderedAfterChildrensOwnEcho) {
|
|
1625
1625
|
ScopedVm vm(true);
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1626
|
+
const std::string out = runCapturingEcho(
|
|
1627
|
+
"module m() { roof(method=\"bogus\") { echo(\"child\"); square(4, center=true); } }\n"
|
|
1628
|
+
"m();");
|
|
1629
|
+
// Asserted as an ordering rather than one exact string: the warning also
|
|
1630
|
+
// carries call-site attribution (", from ... line N" plus TRACE lines,
|
|
1631
|
+
// see formatWarning), which is orthogonal to what this test pins.
|
|
1632
|
+
const size_t echoAt = out.find("ECHO: \"child\"");
|
|
1633
|
+
const size_t warnAt = out.find("WARNING: Unknown roof method 'bogus'. Using 'voronoi'.");
|
|
1634
|
+
ASSERT_NE(echoAt, std::string::npos) << out;
|
|
1635
|
+
ASSERT_NE(warnAt, std::string::npos) << out;
|
|
1636
|
+
EXPECT_LT(echoAt, warnAt) << out;
|
|
1629
1637
|
}
|
|
1630
1638
|
|
|
1631
1639
|
// -- intersection_for -- the TRUE last native-reentry gap, closed by -------
|