openscad-cpp-evaluator 1.7.2__tar.gz → 1.9.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.7.2 → openscad_cpp_evaluator-1.9.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/bindings/module.cpp +19 -5
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +38 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/api.cpp +24 -4
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +3 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/grammar/parser.y +24 -2
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +1 -0
- openscad_cpp_evaluator-1.9.0/external/openscad_cpp_parser/tests/test_strict_commas.cpp +117 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/export.hpp +16 -2
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/python/openscad_cpp_evaluator/__init__.py +17 -5
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/export.cpp +9 -5
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_export.cpp +51 -1
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_python_bindings.py +63 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tools/cli/cli_lib.cpp +9 -1
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.7.2 → openscad_cpp_evaluator-1.9.0}/uv.lock +0 -0
|
@@ -357,7 +357,7 @@ struct Geometry {
|
|
|
357
357
|
// exportModel, with the path/format/warnings marshalling. Releases the GIL:
|
|
358
358
|
// a large export is seconds of Manifold work with no Python involved.
|
|
359
359
|
nb::list exportModelPy(const std::string& path, const Geometry& geom, const std::string& format, bool asciiStl,
|
|
360
|
-
bool stripSlivers) {
|
|
360
|
+
bool stripSlivers, bool splitComponents) {
|
|
361
361
|
std::vector<std::string> warnings;
|
|
362
362
|
{
|
|
363
363
|
nb::gil_scoped_release rel;
|
|
@@ -365,6 +365,7 @@ nb::list exportModelPy(const std::string& path, const Geometry& geom, const std:
|
|
|
365
365
|
opts.format = format;
|
|
366
366
|
opts.asciiStl = asciiStl;
|
|
367
367
|
opts.stripSlivers = stripSlivers;
|
|
368
|
+
opts.splitComponents = splitComponents;
|
|
368
369
|
warnings = oscadeval::exportModel(path, geom.bodies, opts);
|
|
369
370
|
}
|
|
370
371
|
nb::list out;
|
|
@@ -374,7 +375,7 @@ nb::list exportModelPy(const std::string& path, const Geometry& geom, const std:
|
|
|
374
375
|
|
|
375
376
|
nb::object evaluate(const std::string& path, nb::dict viewportParams,
|
|
376
377
|
std::shared_ptr<oscadeval::ManifoldCache> manifoldCache, bool profile,
|
|
377
|
-
bool generate) {
|
|
378
|
+
bool generate, bool strictCommas) {
|
|
378
379
|
std::unordered_map<std::string, oscadeval::Value> vp = toViewportParams(viewportParams);
|
|
379
380
|
|
|
380
381
|
std::vector<oscadeval::ColoredBody> bodies;
|
|
@@ -388,6 +389,12 @@ nb::object evaluate(const std::string& path, nb::dict viewportParams,
|
|
|
388
389
|
nb::gil_scoped_release rel;
|
|
389
390
|
auto logFn = [&echoes](const std::string& m) { echoes.push_back(m); };
|
|
390
391
|
try {
|
|
392
|
+
// Constructed on the parsing thread, which is the one whose
|
|
393
|
+
// thread-local mode StrictCommaScope sets -- a flag flipped from
|
|
394
|
+
// Python before the call would sit on the wrong thread the
|
|
395
|
+
// moment a host evaluates off the main one.
|
|
396
|
+
std::optional<oscad::StrictCommaScope> strict;
|
|
397
|
+
if (strictCommas) strict.emplace();
|
|
391
398
|
oscad::ParsedProgram program = oscad::getProgramFromFile(path);
|
|
392
399
|
oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(program.nodes, path, logFn);
|
|
393
400
|
oscadeval::Evaluator ev(logFn, nullptr, manifoldCache, oscadeval::DebugHooks{}, profile);
|
|
@@ -786,21 +793,28 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
786
793
|
|
|
787
794
|
m.def("export_model", &exportModelPy, nb::arg("path"), nb::arg("geometry"), nb::arg("format") = std::string(),
|
|
788
795
|
nb::arg("ascii_stl") = false, nb::arg("strip_slivers") = true,
|
|
796
|
+
nb::arg("split_components") = false,
|
|
789
797
|
"Write `geometry` to `path`, format taken from the extension unless `format` says otherwise. "
|
|
790
798
|
"Returns the warnings to surface (open shells, mesh problems, slivers removed) rather than "
|
|
791
799
|
"logging them. Raises RuntimeError when there is no geometry, the format is unknown, or the "
|
|
792
|
-
"file cannot be opened
|
|
800
|
+
"file cannot be opened.\n\n"
|
|
801
|
+
"`split_components` gives every disconnected piece its own object in the multi-object formats "
|
|
802
|
+
"(3MF, AMF, OBJ, PLY, VRML, X3D). Off by default, matching OpenSCAD, which writes one object "
|
|
803
|
+
"per colour however many pieces it is in.");
|
|
793
804
|
|
|
794
805
|
m.def("export_extensions", []() { return oscadeval::exportExtensions(); },
|
|
795
806
|
"The file extensions export_model understands, dot-prefixed. Read from the C++ writer table "
|
|
796
807
|
"rather than restated here, so the Python side cannot drift from what can actually be written.");
|
|
797
808
|
|
|
798
809
|
m.def("evaluate", &evaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("manifold_cache") = nullptr,
|
|
799
|
-
nb::arg("profile") = false, nb::arg("generate") = true,
|
|
810
|
+
nb::arg("profile") = false, nb::arg("generate") = true, nb::arg("strict_commas") = false,
|
|
800
811
|
"Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit).\n"
|
|
801
812
|
"generate=False stops after the resolve pass: the script runs and reports everything "
|
|
802
813
|
"it normally would, but no Manifold geometry is built, and neither bodies nor csg_tree "
|
|
803
|
-
"are populated
|
|
814
|
+
"are populated.\n\n"
|
|
815
|
+
"strict_commas=True makes a trailing comma in a call argument list or a let/for "
|
|
816
|
+
"assignment list a syntax error, as OpenSCAD 2021.01 did. List literals and parameter "
|
|
817
|
+
"declarations keep theirs, which 2021.01 accepted too.");
|
|
804
818
|
m.def("parse_decls", &parseDecls, nb::arg("path"),
|
|
805
819
|
"Parse a .scad file; return top-level declaration (namespace, name, start, end, line, column, origin) tuples.");
|
|
806
820
|
m.def("strip_slivers", &stripSliversPy, nb::arg("verts"), nb::arg("tris"),
|
|
@@ -60,6 +60,44 @@ std::unique_ptr<Scope> buildScopes(const std::vector<ASTNode*>& ast);
|
|
|
60
60
|
std::unique_ptr<Scope> buildScopesInto(const std::vector<std::unique_ptr<ASTNode>>& ast, ScopeTable& table);
|
|
61
61
|
std::unique_ptr<Scope> buildScopesInto(const std::vector<ASTNode*>& ast, ScopeTable& table);
|
|
62
62
|
|
|
63
|
+
// Makes a trailing comma in a CALL ARGUMENT LIST a syntax error for as long
|
|
64
|
+
// as it is in scope, matching OpenSCAD 2021.01:
|
|
65
|
+
//
|
|
66
|
+
// cube(1,); // error while this is in scope
|
|
67
|
+
// echo(let(x = 1, y = 2,) x) // error -- let() parses as a call
|
|
68
|
+
// a = [2, 4,]; // still fine: 2021.01 accepts it too
|
|
69
|
+
// module m(a, b,) {} // still fine, same reason
|
|
70
|
+
//
|
|
71
|
+
// One production (`arguments`) feeds every call form -- module
|
|
72
|
+
// instantiation, function calls, echo, assert, render and let -- so this is
|
|
73
|
+
// one rule, not a family of them. Parameter lists and list literals are
|
|
74
|
+
// deliberately untouched: 2021.01 accepts a trailing comma in both, and
|
|
75
|
+
// rejecting them would fail files it loads happily.
|
|
76
|
+
//
|
|
77
|
+
// A scope object rather than a parameter on all nine parse entry points:
|
|
78
|
+
// this is a parse-wide mode, the same shape ParseNumberingScope already
|
|
79
|
+
// uses, and threading a bool through getASTFromString/getASTFromFile/
|
|
80
|
+
// getASTFromLibraryFile/getProgramFromFile and their helpers would touch
|
|
81
|
+
// every one of them to say the same thing. Nests and restores on scope
|
|
82
|
+
// exit, exceptions included; thread-local, so one thread parsing strictly
|
|
83
|
+
// cannot change what another thread sees.
|
|
84
|
+
//
|
|
85
|
+
// getProgramFromFile()'s cache keys on this too -- a strict parse must not
|
|
86
|
+
// be handed a tree an earlier lenient parse of the same file left behind.
|
|
87
|
+
class StrictCommaScope {
|
|
88
|
+
public:
|
|
89
|
+
StrictCommaScope();
|
|
90
|
+
~StrictCommaScope();
|
|
91
|
+
StrictCommaScope(const StrictCommaScope&) = delete;
|
|
92
|
+
StrictCommaScope& operator=(const StrictCommaScope&) = delete;
|
|
93
|
+
|
|
94
|
+
private:
|
|
95
|
+
bool previous_;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Whether a StrictCommaScope is currently in effect on this thread.
|
|
99
|
+
bool strictCommasEnabled();
|
|
100
|
+
|
|
63
101
|
// Parses `code`. Throws ParseError (with the full caret diagnostic) on a
|
|
64
102
|
// syntax error.
|
|
65
103
|
//
|
|
@@ -89,6 +89,17 @@ std::string formatSyntaxError(const ParserDriver& driver, const std::string& cod
|
|
|
89
89
|
|
|
90
90
|
} // namespace
|
|
91
91
|
|
|
92
|
+
namespace {
|
|
93
|
+
// See StrictCommaScope (api.hpp). Thread-local so a strict parse on one
|
|
94
|
+
// thread cannot change what another thread is parsing.
|
|
95
|
+
thread_local bool g_strictCommas = false;
|
|
96
|
+
} // namespace
|
|
97
|
+
|
|
98
|
+
StrictCommaScope::StrictCommaScope() : previous_(g_strictCommas) { g_strictCommas = true; }
|
|
99
|
+
StrictCommaScope::~StrictCommaScope() { g_strictCommas = previous_; }
|
|
100
|
+
|
|
101
|
+
bool strictCommasEnabled() { return g_strictCommas; }
|
|
102
|
+
|
|
92
103
|
std::vector<std::unique_ptr<ASTNode>> parseAst(const std::string& code, const std::string& origin, SourceMap* sourceMap) {
|
|
93
104
|
// Stamps every node this parse builds with one treeId and a dense
|
|
94
105
|
// slot, so a ScopeTable can address it without the node carrying a
|
|
@@ -96,6 +107,7 @@ std::vector<std::unique_ptr<ASTNode>> parseAst(const std::string& code, const st
|
|
|
96
107
|
ParseNumberingScope numbering;
|
|
97
108
|
|
|
98
109
|
ParserDriver driver(origin);
|
|
110
|
+
driver.strictCommas = g_strictCommas;
|
|
99
111
|
lexerBeginString(code);
|
|
100
112
|
yy::parser parser(driver);
|
|
101
113
|
int rc = parser.parse();
|
|
@@ -258,7 +270,8 @@ namespace {
|
|
|
258
270
|
using FileAst = std::vector<std::unique_ptr<ASTNode>>;
|
|
259
271
|
using FileAstPtr = std::shared_ptr<const FileAst>;
|
|
260
272
|
|
|
261
|
-
// One entry per (file, comments) -- keyed by PATH, with the
|
|
273
|
+
// One entry per (file, comments, strict-commas) -- keyed by PATH, with the
|
|
274
|
+
// content stamp
|
|
262
275
|
// stored beside the tree rather than in the key. A stale stamp REPLACES the
|
|
263
276
|
// entry instead of adding a second one: an editor re-renders on every save,
|
|
264
277
|
// and a stamp-in-the-key cache would keep a full copy of every version the
|
|
@@ -266,11 +279,18 @@ using FileAstPtr = std::shared_ptr<const FileAst>;
|
|
|
266
279
|
struct CacheKey {
|
|
267
280
|
std::string path;
|
|
268
281
|
bool comments;
|
|
269
|
-
|
|
282
|
+
// Part of the key, not incidental: the same file parses differently
|
|
283
|
+
// under StrictCommaScope, so a strict parse must not be served a tree an
|
|
284
|
+
// earlier lenient parse of it left here (and vice versa).
|
|
285
|
+
bool strictCommas;
|
|
286
|
+
bool operator==(const CacheKey& o) const {
|
|
287
|
+
return comments == o.comments && strictCommas == o.strictCommas && path == o.path;
|
|
288
|
+
}
|
|
270
289
|
};
|
|
271
290
|
struct CacheKeyHash {
|
|
272
291
|
size_t operator()(const CacheKey& k) const {
|
|
273
|
-
return std::hash<std::string>{}(k.path) ^ (k.comments ? 0x5bf03635U : 0U)
|
|
292
|
+
return std::hash<std::string>{}(k.path) ^ (k.comments ? 0x5bf03635U : 0U)
|
|
293
|
+
^ (k.strictCommas ? 0x9e3779b9U : 0U);
|
|
274
294
|
}
|
|
275
295
|
};
|
|
276
296
|
struct CacheEntry {
|
|
@@ -290,7 +310,7 @@ FileAstPtr parseFileShared(const std::string& absPath, bool includeComments) {
|
|
|
290
310
|
const auto written = fs::last_write_time(absPath, ec);
|
|
291
311
|
const std::int64_t stampMtime = ec ? 0 : static_cast<std::int64_t>(written.time_since_epoch().count());
|
|
292
312
|
|
|
293
|
-
const CacheKey key{absPath, includeComments};
|
|
313
|
+
const CacheKey key{absPath, includeComments, g_strictCommas};
|
|
294
314
|
{
|
|
295
315
|
std::lock_guard<std::mutex> lock(g_astCacheMutex);
|
|
296
316
|
auto it = g_astCache.find(key);
|
|
@@ -39,6 +39,9 @@ public:
|
|
|
39
39
|
std::string origin_;
|
|
40
40
|
NodeList result;
|
|
41
41
|
bool hadError = false;
|
|
42
|
+
// Copied from the ambient StrictCommaScope at parse start (api.cpp), so
|
|
43
|
+
// one parse's mode cannot change under it mid-run.
|
|
44
|
+
bool strictCommas = false;
|
|
42
45
|
int errorLine = 0;
|
|
43
46
|
int errorColumn = 0;
|
|
44
47
|
int errorOffset = 0;
|
|
@@ -247,7 +247,18 @@ parameter:
|
|
|
247
247
|
arguments:
|
|
248
248
|
%empty { $$ = NodeList{}; }
|
|
249
249
|
| argument_seq { $$ = std::move($1); }
|
|
250
|
-
|
|
250
|
+
// The trailing comma OpenSCAD 2021.01 rejected. Still parsed, then
|
|
251
|
+
// reported -- @2 is the comma itself, so the caret lands on it rather
|
|
252
|
+
// than on the whole argument list, which a %expect-style grammar split
|
|
253
|
+
// could not manage. This one production feeds every call form (module
|
|
254
|
+
// instantiation, function calls, echo, assert, render, and let), which
|
|
255
|
+
// is why the whole feature is one rule. See StrictCommaScope (api.hpp).
|
|
256
|
+
| argument_seq "," {
|
|
257
|
+
if (driver.strictCommas) {
|
|
258
|
+
driver.reportError(@2, "trailing comma in argument list");
|
|
259
|
+
}
|
|
260
|
+
$$ = std::move($1);
|
|
261
|
+
}
|
|
251
262
|
;
|
|
252
263
|
|
|
253
264
|
argument_seq:
|
|
@@ -263,7 +274,18 @@ argument:
|
|
|
263
274
|
assignments_expr:
|
|
264
275
|
%empty { $$ = NodeList{}; }
|
|
265
276
|
| assignment_expr_seq { $$ = std::move($1); }
|
|
266
|
-
|
|
277
|
+
// 2021.01 rejected this one too. `let` does NOT go through `arguments`
|
|
278
|
+
// in this grammar -- it has its own assignment list, shared with `for`
|
|
279
|
+
// and `intersection_for` -- so the feature is two productions, not the
|
|
280
|
+
// one it looks like. 2021.01 rejects a trailing comma in all four:
|
|
281
|
+
// let(x=1,), for(i=[0:2],), intersection_for(i=[0:1],) and the list
|
|
282
|
+
// comprehension forms of for. See StrictCommaScope (api.hpp).
|
|
283
|
+
| assignment_expr_seq "," {
|
|
284
|
+
if (driver.strictCommas) {
|
|
285
|
+
driver.reportError(@2, "trailing comma in assignment list");
|
|
286
|
+
}
|
|
287
|
+
$$ = std::move($1);
|
|
288
|
+
}
|
|
267
289
|
;
|
|
268
290
|
|
|
269
291
|
assignment_expr_seq:
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// StrictCommaScope: a trailing comma in a call argument list is an error,
|
|
2
|
+
// matching OpenSCAD 2021.01 (BelfrySCAD issue #362).
|
|
3
|
+
//
|
|
4
|
+
// The scope is deliberately narrow. Measured against 2021.01 itself, it
|
|
5
|
+
// rejected a trailing comma in CALL ARGUMENTS and accepted one everywhere
|
|
6
|
+
// else -- list literals, list comprehensions, and module/function parameter
|
|
7
|
+
// declarations all take one, in 2021.01 as in every version since. Rejecting
|
|
8
|
+
// those would fail files 2021.01 loads happily, so they are left alone and
|
|
9
|
+
// this file says so case by case.
|
|
10
|
+
#include "test_helpers.hpp"
|
|
11
|
+
|
|
12
|
+
#include <gtest/gtest.h>
|
|
13
|
+
|
|
14
|
+
using namespace oscad;
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
// Two productions carry it: `arguments` (every call form) and
|
|
19
|
+
// `assignments_expr` (let/for/intersection_for). `let` is NOT a call in this
|
|
20
|
+
// grammar -- it has its own assignment list -- which the first draft of this
|
|
21
|
+
// feature got wrong and this test caught.
|
|
22
|
+
const char* const kRejected[] = {
|
|
23
|
+
"cube(1,);", // module instantiation
|
|
24
|
+
"translate([0,0,0],) cube(1);", // ...with a child
|
|
25
|
+
"function g(x) = x; y = g(1,);", // user function call
|
|
26
|
+
"y = max(1, 2,);", // builtin function call
|
|
27
|
+
"y = str(\"a\", \"b\",);",
|
|
28
|
+
"echo(1, 2,);",
|
|
29
|
+
"assert(true, \"m\",);",
|
|
30
|
+
// assignments_expr, not arguments
|
|
31
|
+
"y = let(x = 1, y = 2,) x + y;",
|
|
32
|
+
"let(x = 1,) cube(x);",
|
|
33
|
+
"for (i = [0:2],) cube(i);",
|
|
34
|
+
"intersection_for (i = [0:1],) cube(1);",
|
|
35
|
+
"a = [for (i = [0:1],) i];",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Accepted by 2021.01, so accepted here even under the scope.
|
|
39
|
+
const char* const kAccepted[] = {
|
|
40
|
+
"a = [2, 4,];", // list literal
|
|
41
|
+
"a = [for (i = [0:2]) i,];", // list comprehension
|
|
42
|
+
"a = [each [1, 2],];",
|
|
43
|
+
"module m(a, b,) {} m(1, 2);", // module parameter declaration
|
|
44
|
+
"function f(a, b,) = a + b;", // function parameter declaration
|
|
45
|
+
"cube(1);", // no trailing comma at all
|
|
46
|
+
"y = max(1, 2);",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
} // namespace
|
|
50
|
+
|
|
51
|
+
TEST(StrictCommas, CallArgumentTrailingCommaIsAnErrorInScope) {
|
|
52
|
+
StrictCommaScope strict;
|
|
53
|
+
for (const char* src : kRejected) {
|
|
54
|
+
EXPECT_THROW(parseSrc(src), ParseError) << src;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
TEST(StrictCommas, EverythingElseKeepsItsTrailingComma) {
|
|
59
|
+
StrictCommaScope strict;
|
|
60
|
+
for (const char* src : kAccepted) {
|
|
61
|
+
EXPECT_NO_THROW(parseSrc(src)) << src;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
TEST(StrictCommas, OffByDefault) {
|
|
66
|
+
// The whole set parses without the scope -- this is opt-in, and every
|
|
67
|
+
// existing caller must be unaffected.
|
|
68
|
+
for (const char* src : kRejected) {
|
|
69
|
+
EXPECT_NO_THROW(parseSrc(src)) << src;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
TEST(StrictCommas, ScopeNestsAndRestores) {
|
|
74
|
+
EXPECT_FALSE(strictCommasEnabled());
|
|
75
|
+
{
|
|
76
|
+
StrictCommaScope outer;
|
|
77
|
+
EXPECT_TRUE(strictCommasEnabled());
|
|
78
|
+
{
|
|
79
|
+
StrictCommaScope inner;
|
|
80
|
+
EXPECT_TRUE(strictCommasEnabled());
|
|
81
|
+
}
|
|
82
|
+
EXPECT_TRUE(strictCommasEnabled()) << "an inner scope must not switch it off";
|
|
83
|
+
}
|
|
84
|
+
EXPECT_FALSE(strictCommasEnabled());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
TEST(StrictCommas, RestoresWhenAParseThrows) {
|
|
88
|
+
try {
|
|
89
|
+
StrictCommaScope strict;
|
|
90
|
+
parseSrc("cube(1,);");
|
|
91
|
+
FAIL() << "expected ParseError";
|
|
92
|
+
} catch (const ParseError&) {
|
|
93
|
+
}
|
|
94
|
+
EXPECT_FALSE(strictCommasEnabled()) << "the scope must unwind with the exception";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
TEST(StrictCommas, TheErrorPointsAtTheComma) {
|
|
98
|
+
StrictCommaScope strict;
|
|
99
|
+
try {
|
|
100
|
+
parseSrc("cube(1,);");
|
|
101
|
+
FAIL() << "expected ParseError";
|
|
102
|
+
} catch (const ParseError& e) {
|
|
103
|
+
const std::string what = e.what();
|
|
104
|
+
EXPECT_NE(what.find("trailing comma in argument list"), std::string::npos) << what;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
TEST(StrictCommas, AnAssignmentListSaysSo) {
|
|
109
|
+
StrictCommaScope strict;
|
|
110
|
+
try {
|
|
111
|
+
parseSrc("y = let(x = 1,) x;");
|
|
112
|
+
FAIL() << "expected ParseError";
|
|
113
|
+
} catch (const ParseError& e) {
|
|
114
|
+
const std::string what = e.what();
|
|
115
|
+
EXPECT_NE(what.find("trailing comma in assignment list"), std::string::npos) << what;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -39,13 +39,21 @@ struct ExportObject {
|
|
|
39
39
|
// `color("red") body(); color("blue") detail();` leaves the detail
|
|
40
40
|
// whole. Only the invisible interior is affected: the visible surface
|
|
41
41
|
// is identical either way.
|
|
42
|
-
// 3. One object per connected component.
|
|
42
|
+
// 3. One object per connected component -- OPTIONAL, and off by default.
|
|
43
|
+
// Rules 1 and 2 are about correctness; this one is presentation, and
|
|
44
|
+
// it is the one that diverges from OpenSCAD, which writes a single
|
|
45
|
+
// object however many disjoint pieces the model has. Splitting turned
|
|
46
|
+
// `cube(10); translate([20,0,0]) cube(10);` into two 3MF objects and a
|
|
47
|
+
// multiboard tile into hundreds, which slicers list individually and
|
|
48
|
+
// Prusa Slicer complains about (issue #319). Ask for it when separate
|
|
49
|
+
// pieces are what you want; leave it off to match OpenSCAD.
|
|
43
50
|
//
|
|
44
51
|
// A body Manifold rejected (an open shell is not a solid) can take part in
|
|
45
52
|
// none of that: it keeps its own triangles and its own object, and its
|
|
46
53
|
// 1-based index is appended to `openParts` for the caller to warn about.
|
|
47
54
|
std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& bodies,
|
|
48
|
-
std::vector<int>* openParts = nullptr
|
|
55
|
+
std::vector<int>* openParts = nullptr,
|
|
56
|
+
bool splitComponents = false);
|
|
49
57
|
|
|
50
58
|
// Writes a binary STL: composes every body's mesh into one solid (bodies
|
|
51
59
|
// with no `.body` -- 2D-only sections -- or an empty Manifold are
|
|
@@ -130,6 +138,12 @@ struct ExportOptions {
|
|
|
130
138
|
// break no topology, but slicers commonly discard them and are then
|
|
131
139
|
// left with the holes their removal opens.
|
|
132
140
|
bool stripSlivers = true;
|
|
141
|
+
// Rule 3 of splitBodiesForExport: give every disconnected piece its own
|
|
142
|
+
// object in the multi-object formats (3MF, AMF, OBJ, PLY, VRML, X3D).
|
|
143
|
+
// Off matches OpenSCAD, which writes one object per colour however many
|
|
144
|
+
// pieces it is in; see that function's own comment for why the default
|
|
145
|
+
// moved here.
|
|
146
|
+
bool splitComponents = false;
|
|
133
147
|
};
|
|
134
148
|
|
|
135
149
|
// Writes `bodies` to `path`, choosing the writer from the extension (or
|
|
@@ -292,13 +292,13 @@ def strip_slivers(verts, tris):
|
|
|
292
292
|
|
|
293
293
|
|
|
294
294
|
def export_model(path: str, geometry, format: str = "", ascii_stl: bool = False,
|
|
295
|
-
strip_slivers: bool = True) -> list:
|
|
295
|
+
strip_slivers: bool = True, split_components: bool = False) -> list:
|
|
296
296
|
"""Write `geometry` to `path`. Returns the warnings to surface.
|
|
297
297
|
|
|
298
298
|
`geometry` is the handle an Evaluator stashes on itself as
|
|
299
299
|
`.geometry` -- the evaluated bodies, still on the C++ side. Export has
|
|
300
300
|
to do real CSG (the implicit top-level union, the per-colour volume
|
|
301
|
-
claim
|
|
301
|
+
claim), and going through the flattened
|
|
302
302
|
arrays the renderer gets would mean rebuilding every Manifold first:
|
|
303
303
|
~146ms on a 224k-triangle model, and Manifold's own provenance lost
|
|
304
304
|
along the way.
|
|
@@ -309,9 +309,13 @@ def export_model(path: str, geometry, format: str = "", ascii_stl: bool = False,
|
|
|
309
309
|
here refuses to write: a deliberately open surface is a legitimate
|
|
310
310
|
export. Raises EvalError when there is no geometry, the format is
|
|
311
311
|
unknown, or the file cannot be opened.
|
|
312
|
+
|
|
313
|
+
`split_components` gives every disconnected piece its own object in the
|
|
314
|
+
multi-object formats. Off by default, which is what OpenSCAD writes.
|
|
312
315
|
"""
|
|
313
316
|
try:
|
|
314
|
-
return list(_ext.export_model(path, geometry, format, ascii_stl, strip_slivers
|
|
317
|
+
return list(_ext.export_model(path, geometry, format, ascii_stl, strip_slivers,
|
|
318
|
+
split_components))
|
|
315
319
|
except Exception as e:
|
|
316
320
|
raise EvalError(str(e)) from e
|
|
317
321
|
|
|
@@ -493,7 +497,7 @@ class Evaluator:
|
|
|
493
497
|
self.dyn_explicit = set()
|
|
494
498
|
|
|
495
499
|
def evaluate(self, source_path: str, viewport_params: Optional[dict] = None,
|
|
496
|
-
generate: bool = True):
|
|
500
|
+
generate: bool = True, strict_commas: bool = False):
|
|
497
501
|
"""Run `source_path` and return (bodies, id_to_node).
|
|
498
502
|
|
|
499
503
|
`generate=False` stops after the resolve pass. The script still runs
|
|
@@ -504,6 +508,13 @@ class Evaluator:
|
|
|
504
508
|
the geometry without skipping the language.
|
|
505
509
|
|
|
506
510
|
Ignored on the debugger path, which has no geometry handle anyway.
|
|
511
|
+
|
|
512
|
+
`strict_commas=True` makes a trailing comma in a call argument list
|
|
513
|
+
or a let/for assignment list a syntax error, as OpenSCAD 2021.01 did
|
|
514
|
+
-- `cube(1,)` and `let(x = 1,)`, but NOT `[2, 4,]` or
|
|
515
|
+
`module m(a, b,)`, which 2021.01 accepted. For checking a script
|
|
516
|
+
against that version; off, and no divergence from current OpenSCAD,
|
|
517
|
+
otherwise. Also ignored on the debugger path.
|
|
507
518
|
"""
|
|
508
519
|
vp = viewport_params or {}
|
|
509
520
|
try:
|
|
@@ -524,7 +535,8 @@ class Evaluator:
|
|
|
524
535
|
else:
|
|
525
536
|
(body_dicts, echoes, id_spans, csg_tree, profile_result, dyn,
|
|
526
537
|
dyn_explicit, geometry) = _ext.evaluate(
|
|
527
|
-
source_path, vp, self._manifold_cache, self._profile, generate
|
|
538
|
+
source_path, vp, self._manifold_cache, self._profile, generate,
|
|
539
|
+
strict_commas)
|
|
528
540
|
# The evaluated bodies, still on the C++ side. Stashed like
|
|
529
541
|
# csg_tree/profile_result rather than returned, so
|
|
530
542
|
# evaluate()'s own 2-tuple result is unchanged -- callers
|
|
@@ -418,7 +418,8 @@ std::vector<std::array<float, 4>> carryTriColors(const std::vector<std::array<fl
|
|
|
418
418
|
|
|
419
419
|
} // namespace
|
|
420
420
|
|
|
421
|
-
std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& bodies, std::vector<int>* openParts
|
|
421
|
+
std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& bodies, std::vector<int>* openParts,
|
|
422
|
+
bool splitComponents) {
|
|
422
423
|
struct Solid {
|
|
423
424
|
manifold::Manifold man;
|
|
424
425
|
std::optional<std::array<float, 4>> color;
|
|
@@ -545,9 +546,12 @@ std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& b
|
|
|
545
546
|
|
|
546
547
|
std::vector<ExportObject> out;
|
|
547
548
|
for (const Claimed& g : claimedGroups) {
|
|
548
|
-
// Decompose() is the rule-3 split
|
|
549
|
-
//
|
|
550
|
-
|
|
549
|
+
// Decompose() is the rule-3 split, and it is opt-in: without it this
|
|
550
|
+
// colour group stays one object however many disjoint pieces it is
|
|
551
|
+
// in, which is what OpenSCAD writes. A single-component solid comes
|
|
552
|
+
// back as a one-element list either way, so there is no special case.
|
|
553
|
+
std::vector<manifold::Manifold> parts;
|
|
554
|
+
if (splitComponents) parts = g.man.Decompose();
|
|
551
555
|
if (parts.empty()) parts.push_back(g.man);
|
|
552
556
|
for (const manifold::Manifold& part : parts) {
|
|
553
557
|
if (part.IsEmpty()) continue;
|
|
@@ -1022,7 +1026,7 @@ std::vector<std::string> exportModel(const std::string& path, const std::vector<
|
|
|
1022
1026
|
// its own -- that is what the file contains.
|
|
1023
1027
|
for (std::string& w : checkExportBodies(bodies)) warnings.push_back(std::move(w));
|
|
1024
1028
|
std::vector<int> openParts;
|
|
1025
|
-
const std::vector<ExportObject> objects = splitBodiesForExport(bodies, &openParts);
|
|
1029
|
+
const std::vector<ExportObject> objects = splitBodiesForExport(bodies, &openParts, opts.splitComponents);
|
|
1026
1030
|
reportOpen(openParts);
|
|
1027
1031
|
if (objects.empty()) throw std::runtime_error("No geometry to export");
|
|
1028
1032
|
if (ext == ".3mf") {
|
|
@@ -163,10 +163,16 @@ TEST(ExportAmf, WritesOneObjectAndOneMaterialPerColour) {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
TEST(ExportAmf, TwoObjectsSharingAColourShareOneMaterial) {
|
|
166
|
+
// splitComponents, because two objects is the premise: same-coloured
|
|
167
|
+
// disjoint pieces are ONE object by default (see the DisjointPieces
|
|
168
|
+
// tests below). What is under test here is that the two of them share a
|
|
169
|
+
// single material entry rather than each declaring their own.
|
|
166
170
|
std::vector<ColoredBody> bodies =
|
|
167
171
|
evalToBodies("color(\"red\") cube(2); translate([10,0,0]) color(\"red\") cube(2);");
|
|
168
172
|
const std::string path = tempPath("shared.amf").string();
|
|
169
|
-
|
|
173
|
+
ExportOptions opts;
|
|
174
|
+
opts.splitComponents = true;
|
|
175
|
+
exportModel(path, bodies, opts);
|
|
170
176
|
|
|
171
177
|
const std::string xml = readFile(path);
|
|
172
178
|
EXPECT_EQ(countOf(xml, "<object id="), 2u);
|
|
@@ -174,6 +180,50 @@ TEST(ExportAmf, TwoObjectsSharingAColourShareOneMaterial) {
|
|
|
174
180
|
std::remove(path.c_str());
|
|
175
181
|
}
|
|
176
182
|
|
|
183
|
+
// Issue #319: a model in several disconnected pieces was written as one
|
|
184
|
+
// object PER PIECE, so a slicer listed them individually -- hundreds of
|
|
185
|
+
// entries for a multiboard tile, and Prusa Slicer objecting. OpenSCAD writes
|
|
186
|
+
// one object, and so does this by default now; the split is still available
|
|
187
|
+
// for callers that want the pieces apart.
|
|
188
|
+
TEST(ExportSplit, DisjointPiecesAreOneObjectByDefault) {
|
|
189
|
+
std::vector<ColoredBody> bodies =
|
|
190
|
+
evalToBodies("cube(10); translate([20,0,0]) cube(10); translate([40,0,0]) cube(10);");
|
|
191
|
+
const std::string path = tempPath("joined.amf").string();
|
|
192
|
+
exportModel(path, bodies, ExportOptions{});
|
|
193
|
+
|
|
194
|
+
const std::string xml = readFile(path);
|
|
195
|
+
EXPECT_EQ(countOf(xml, "<object id="), 1u);
|
|
196
|
+
EXPECT_EQ(countOf(xml, "<volume"), 1u);
|
|
197
|
+
std::remove(path.c_str());
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
TEST(ExportSplit, DisjointPiecesSplitWhenAsked) {
|
|
201
|
+
std::vector<ColoredBody> bodies =
|
|
202
|
+
evalToBodies("cube(10); translate([20,0,0]) cube(10); translate([40,0,0]) cube(10);");
|
|
203
|
+
const std::string path = tempPath("split.amf").string();
|
|
204
|
+
ExportOptions opts;
|
|
205
|
+
opts.splitComponents = true;
|
|
206
|
+
exportModel(path, bodies, opts);
|
|
207
|
+
|
|
208
|
+
const std::string xml = readFile(path);
|
|
209
|
+
EXPECT_EQ(countOf(xml, "<object id="), 3u);
|
|
210
|
+
std::remove(path.c_str());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Rule 2 is not what became optional: differently-coloured solids must stay
|
|
214
|
+
// separate objects either way, or a multi-material export loses its colours.
|
|
215
|
+
TEST(ExportSplit, ColoursStayApartWithTheSplitOff) {
|
|
216
|
+
std::vector<ColoredBody> bodies =
|
|
217
|
+
evalToBodies("color(\"red\") cube(10); translate([20,0,0]) color(\"blue\") cube(10);");
|
|
218
|
+
const std::string path = tempPath("colours.amf").string();
|
|
219
|
+
exportModel(path, bodies, ExportOptions{});
|
|
220
|
+
|
|
221
|
+
const std::string xml = readFile(path);
|
|
222
|
+
EXPECT_EQ(countOf(xml, "<object id="), 2u);
|
|
223
|
+
EXPECT_EQ(countOf(xml, "<material id="), 2u);
|
|
224
|
+
std::remove(path.c_str());
|
|
225
|
+
}
|
|
226
|
+
|
|
177
227
|
TEST(ExportAmf, MaterialAndObjectIdsStartAtOne) {
|
|
178
228
|
// id 0 is reserved by the spec; a reader may drop anything using it.
|
|
179
229
|
std::vector<ColoredBody> bodies = evalToBodies("cube(2);");
|
|
@@ -633,3 +633,66 @@ def test_degenerate_ranges_do_not_overflow_the_element_count(tmp_path):
|
|
|
633
633
|
# Silent for NaN; the unbounded pair warn, with the reference's number.
|
|
634
634
|
assert len(warnings) == 2, warnings
|
|
635
635
|
assert all("too many elements (4294967295)" in w for w in warnings), warnings
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def test_export_model_facade_accepts_split_components(tmp_path):
|
|
639
|
+
"""The Python facade's signature must track the nanobind binding's.
|
|
640
|
+
|
|
641
|
+
They are two hand-written parameter lists for one call, and adding
|
|
642
|
+
`split_components` to the binding alone left the facade dropping it --
|
|
643
|
+
a TypeError the C++ suite cannot see, because it never goes through
|
|
644
|
+
Python. Caught in practice, hence this.
|
|
645
|
+
"""
|
|
646
|
+
from openscad_cpp_evaluator import Evaluator, export_model
|
|
647
|
+
|
|
648
|
+
src = tmp_path / "pieces.scad"
|
|
649
|
+
src.write_text("cube(10); translate([20,0,0]) cube(10); translate([40,0,0]) cube(10);")
|
|
650
|
+
ev = Evaluator()
|
|
651
|
+
ev.evaluate(str(src), {})
|
|
652
|
+
|
|
653
|
+
def objects_in(path, **kwargs):
|
|
654
|
+
export_model(str(path), ev.geometry, **kwargs)
|
|
655
|
+
import zipfile
|
|
656
|
+
with zipfile.ZipFile(path) as z:
|
|
657
|
+
return z.read("3D/3dmodel.model").decode().count("<object ")
|
|
658
|
+
|
|
659
|
+
# Default matches OpenSCAD: one object however many pieces.
|
|
660
|
+
assert objects_in(tmp_path / "joined.3mf") == 1
|
|
661
|
+
assert objects_in(tmp_path / "split.3mf", split_components=True) == 3
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def test_strict_commas_reaches_the_parser(tmp_path):
|
|
665
|
+
"""Evaluator.evaluate(strict_commas=True) must survive both hand-written
|
|
666
|
+
parameter lists -- the facade's and the nanobind binding's. The last
|
|
667
|
+
argument added this way was dropped by the facade and no C++ test could
|
|
668
|
+
see it (see test_export_model_facade_accepts_split_components)."""
|
|
669
|
+
import pytest
|
|
670
|
+
from openscad_cpp_evaluator import Evaluator, EvalError
|
|
671
|
+
|
|
672
|
+
rejected = tmp_path / "call.scad"
|
|
673
|
+
rejected.write_text("cube(1,);\n")
|
|
674
|
+
accepted = tmp_path / "list.scad"
|
|
675
|
+
accepted.write_text("a = [2, 4,];\ncube(a[0]);\n")
|
|
676
|
+
|
|
677
|
+
# Off by default: both parse, as they do in current OpenSCAD.
|
|
678
|
+
Evaluator(echo_fn=lambda _m: None).evaluate(str(rejected), {})
|
|
679
|
+
Evaluator(echo_fn=lambda _m: None).evaluate(str(accepted), {})
|
|
680
|
+
|
|
681
|
+
# On: a call's trailing comma is a syntax error, a list literal's is not
|
|
682
|
+
# -- 2021.01 accepted the list.
|
|
683
|
+
with pytest.raises(EvalError):
|
|
684
|
+
Evaluator(echo_fn=lambda _m: None).evaluate(str(rejected), {}, strict_commas=True)
|
|
685
|
+
Evaluator(echo_fn=lambda _m: None).evaluate(str(accepted), {}, strict_commas=True)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def test_strict_commas_does_not_leak_between_evaluations(tmp_path):
|
|
689
|
+
"""The parser mode is a scope object; a strict parse must not leave it on
|
|
690
|
+
for the next caller."""
|
|
691
|
+
import pytest
|
|
692
|
+
from openscad_cpp_evaluator import Evaluator, EvalError
|
|
693
|
+
|
|
694
|
+
src = tmp_path / "call.scad"
|
|
695
|
+
src.write_text("cube(1,);\n")
|
|
696
|
+
with pytest.raises(EvalError):
|
|
697
|
+
Evaluator(echo_fn=lambda _m: None).evaluate(str(src), {}, strict_commas=True)
|
|
698
|
+
Evaluator(echo_fn=lambda _m: None).evaluate(str(src), {}) # must not raise
|