openscad-cpp-evaluator 1.8.0__tar.gz → 1.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/CLAUDE.md +17 -6
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/bindings/module.cpp +26 -6
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +38 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/api.cpp +24 -4
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +3 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/grammar/parser.y +24 -2
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +1 -0
- openscad_cpp_evaluator-1.10.0/external/openscad_cpp_parser/tests/test_strict_commas.cpp +117 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/colored_body.hpp +4 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/export.hpp +32 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/python/openscad_cpp_evaluator/__init__.py +22 -4
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/colored_body.cpp +7 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/export.cpp +118 -1
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_export.cpp +182 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_python_bindings.py +77 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tools/cli/cli_lib.cpp +9 -1
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.8.0 → openscad_cpp_evaluator-1.10.0}/uv.lock +0 -0
|
@@ -916,8 +916,8 @@ grep for `ponytail:`.
|
|
|
916
916
|
— skip it and the mesh comes out non-manifold at the seam). Self-contained 2D geometry math here
|
|
917
917
|
(`dot2`/`len2`/`norm2`, the parabola-canonical-frame rotation in `discretizeArc`) exists nowhere
|
|
918
918
|
else in this codebase.
|
|
919
|
-
- `include/openscad_cpp_evaluator/export.hpp`, `src/export.cpp` — the writers for all
|
|
920
|
-
formats `exportExtensions()` lists: 3MF, AMF, STL, OBJ, OFF, PLY, VRML, X3D (3MF via
|
|
919
|
+
- `include/openscad_cpp_evaluator/export.hpp`, `src/export.cpp` — the writers for all nine
|
|
920
|
+
formats `exportExtensions()` lists: 3MF, AMF, STL, OBJ, OFF, PLY, SVG, VRML, X3D (3MF via
|
|
921
921
|
`zip_stored.hpp`'s `writeDeflateZip`). **`exportExtensions()` is the one source of truth for
|
|
922
922
|
which formats exist** — the Python facade and BelfrySCAD's dialog/CLI all ask it rather than
|
|
923
923
|
restating the list, because three hand-written copies had already drifted far enough that `.off`
|
|
@@ -927,11 +927,22 @@ grep for `ponytail:`.
|
|
|
927
927
|
than the face, so an object whose triangles are not all one colour is written as one volume per
|
|
928
928
|
colour — per-triangle `<color>` is legal in the spec but poorly supported by the slicers that are
|
|
929
929
|
the audience for it.
|
|
930
|
+
**`writeSvg` is the one 2D writer**, and the only one that reads `ColoredBody::section` rather
|
|
931
|
+
than `.body`: SVG 1.1, millimetres at 1:1, every contour of a body (holes included) a
|
|
932
|
+
`M .. L .. z` subpath of that body's single `<path>`. Model space is Y-up and SVG's is Y-down, so
|
|
933
|
+
the whole point transform is a Y negation; the page is the bounding box padded by **half the
|
|
934
|
+
stroke width** and rounded outward to whole millimetres, which is why the margin is not a
|
|
935
|
+
constant and why `stroke=false` shrinks the document. Both rules are OpenSCAD's, read from its
|
|
936
|
+
`export_svg.cc` — `ExportSvg.MatchesRealOpenscadByteForByte` pins the output against the real
|
|
937
|
+
binary's, vertex order and 6-vertex line wrap included. A 3D body in the list throws
|
|
938
|
+
("Current top level object is not a 2D object"), as OpenSCAD refuses too: there is no projection
|
|
939
|
+
to fall back on. `ExportOptions::svg` carries OpenSCAD's five `-O export-svg/...` settings.
|
|
930
940
|
`colored_body.hpp`/`.cpp`'s `toRenderableBodies()` (thin-extrudes any top-level 2D-only result to
|
|
931
|
-
a
|
|
932
|
-
writers — none of them look at `ColoredBody::section` at all, only `.body`.
|
|
933
|
-
|
|
934
|
-
|
|
941
|
+
a 1-unit-tall `flatPreview` Manifold) must run on a body list before handing it to any of the
|
|
942
|
+
MESH writers — none of them look at `ColoredBody::section` at all, only `.body`. Not before
|
|
943
|
+
`writeSvg`, which needs the sections that step throws away, and which says so rather than writing
|
|
944
|
+
the slab's outline. `tools/cli/cli_lib.cpp` is the only current caller; a future GUI/renderer
|
|
945
|
+
caller needs the same call before passing results to its own mesh consumer.
|
|
935
946
|
- `include/openscad_cpp_evaluator/function_builtins.hpp`, `src/builtins/function_builtins.cpp` —
|
|
936
947
|
the math/string/list/type-check builtin *function* dispatch (`isBuiltinFunctionName`/
|
|
937
948
|
`evalBuiltinFunction`), a completely separate namespace/table from `dispatch.hpp`'s builtin
|
|
@@ -357,7 +357,8 @@ 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, bool splitComponents
|
|
360
|
+
bool stripSlivers, bool splitComponents, bool svgFill, const std::string& svgFillColor,
|
|
361
|
+
bool svgStroke, const std::string& svgStrokeColor, double svgStrokeWidth) {
|
|
361
362
|
std::vector<std::string> warnings;
|
|
362
363
|
{
|
|
363
364
|
nb::gil_scoped_release rel;
|
|
@@ -366,6 +367,11 @@ nb::list exportModelPy(const std::string& path, const Geometry& geom, const std:
|
|
|
366
367
|
opts.asciiStl = asciiStl;
|
|
367
368
|
opts.stripSlivers = stripSlivers;
|
|
368
369
|
opts.splitComponents = splitComponents;
|
|
370
|
+
opts.svg.fill = svgFill;
|
|
371
|
+
opts.svg.fillColor = svgFillColor;
|
|
372
|
+
opts.svg.stroke = svgStroke;
|
|
373
|
+
opts.svg.strokeColor = svgStrokeColor;
|
|
374
|
+
opts.svg.strokeWidth = svgStrokeWidth;
|
|
369
375
|
warnings = oscadeval::exportModel(path, geom.bodies, opts);
|
|
370
376
|
}
|
|
371
377
|
nb::list out;
|
|
@@ -375,7 +381,7 @@ nb::list exportModelPy(const std::string& path, const Geometry& geom, const std:
|
|
|
375
381
|
|
|
376
382
|
nb::object evaluate(const std::string& path, nb::dict viewportParams,
|
|
377
383
|
std::shared_ptr<oscadeval::ManifoldCache> manifoldCache, bool profile,
|
|
378
|
-
bool generate) {
|
|
384
|
+
bool generate, bool strictCommas) {
|
|
379
385
|
std::unordered_map<std::string, oscadeval::Value> vp = toViewportParams(viewportParams);
|
|
380
386
|
|
|
381
387
|
std::vector<oscadeval::ColoredBody> bodies;
|
|
@@ -389,6 +395,12 @@ nb::object evaluate(const std::string& path, nb::dict viewportParams,
|
|
|
389
395
|
nb::gil_scoped_release rel;
|
|
390
396
|
auto logFn = [&echoes](const std::string& m) { echoes.push_back(m); };
|
|
391
397
|
try {
|
|
398
|
+
// Constructed on the parsing thread, which is the one whose
|
|
399
|
+
// thread-local mode StrictCommaScope sets -- a flag flipped from
|
|
400
|
+
// Python before the call would sit on the wrong thread the
|
|
401
|
+
// moment a host evaluates off the main one.
|
|
402
|
+
std::optional<oscad::StrictCommaScope> strict;
|
|
403
|
+
if (strictCommas) strict.emplace();
|
|
392
404
|
oscad::ParsedProgram program = oscad::getProgramFromFile(path);
|
|
393
405
|
oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(program.nodes, path, logFn);
|
|
394
406
|
oscadeval::Evaluator ev(logFn, nullptr, manifoldCache, oscadeval::DebugHooks{}, profile);
|
|
@@ -787,25 +799,33 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
787
799
|
|
|
788
800
|
m.def("export_model", &exportModelPy, nb::arg("path"), nb::arg("geometry"), nb::arg("format") = std::string(),
|
|
789
801
|
nb::arg("ascii_stl") = false, nb::arg("strip_slivers") = true,
|
|
790
|
-
nb::arg("split_components") = false,
|
|
802
|
+
nb::arg("split_components") = false, nb::arg("svg_fill") = false,
|
|
803
|
+
nb::arg("svg_fill_color") = std::string("white"), nb::arg("svg_stroke") = true,
|
|
804
|
+
nb::arg("svg_stroke_color") = std::string("black"), nb::arg("svg_stroke_width") = 0.35,
|
|
791
805
|
"Write `geometry` to `path`, format taken from the extension unless `format` says otherwise. "
|
|
792
806
|
"Returns the warnings to surface (open shells, mesh problems, slivers removed) rather than "
|
|
793
807
|
"logging them. Raises RuntimeError when there is no geometry, the format is unknown, or the "
|
|
794
808
|
"file cannot be opened.\n\n"
|
|
795
809
|
"`split_components` gives every disconnected piece its own object in the multi-object formats "
|
|
796
810
|
"(3MF, AMF, OBJ, PLY, VRML, X3D). Off by default, matching OpenSCAD, which writes one object "
|
|
797
|
-
"per colour however many pieces it is in
|
|
811
|
+
"per colour however many pieces it is in.\n\n"
|
|
812
|
+
"The svg_* arguments are OpenSCAD's -O export-svg/... set and apply to .svg only. "
|
|
813
|
+
"svg_stroke/svg_stroke_width also pad the page, so they are not purely cosmetic. An .svg "
|
|
814
|
+
"export needs an all-2D model and raises otherwise, as OpenSCAD does.");
|
|
798
815
|
|
|
799
816
|
m.def("export_extensions", []() { return oscadeval::exportExtensions(); },
|
|
800
817
|
"The file extensions export_model understands, dot-prefixed. Read from the C++ writer table "
|
|
801
818
|
"rather than restated here, so the Python side cannot drift from what can actually be written.");
|
|
802
819
|
|
|
803
820
|
m.def("evaluate", &evaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("manifold_cache") = nullptr,
|
|
804
|
-
nb::arg("profile") = false, nb::arg("generate") = true,
|
|
821
|
+
nb::arg("profile") = false, nb::arg("generate") = true, nb::arg("strict_commas") = false,
|
|
805
822
|
"Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit).\n"
|
|
806
823
|
"generate=False stops after the resolve pass: the script runs and reports everything "
|
|
807
824
|
"it normally would, but no Manifold geometry is built, and neither bodies nor csg_tree "
|
|
808
|
-
"are populated
|
|
825
|
+
"are populated.\n\n"
|
|
826
|
+
"strict_commas=True makes a trailing comma in a call argument list or a let/for "
|
|
827
|
+
"assignment list a syntax error, as OpenSCAD 2021.01 did. List literals and parameter "
|
|
828
|
+
"declarations keep theirs, which 2021.01 accepted too.");
|
|
809
829
|
m.def("parse_decls", &parseDecls, nb::arg("path"),
|
|
810
830
|
"Parse a .scad file; return top-level declaration (namespace, name, start, end, line, column, origin) tuples.");
|
|
811
831
|
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
|
+
}
|
|
@@ -16,6 +16,10 @@ enum class BodyRole { Normal, Highlight, Background, ShowOnly };
|
|
|
16
16
|
// A single piece of evaluated geometry: exactly one of `body` (3D) or
|
|
17
17
|
// `section` (2D) is set, never both -- mirrors the Python reference's
|
|
18
18
|
// ColoredBody (body: Optional[Manifold] XOR section: Optional[CrossSection]).
|
|
19
|
+
// ONE exception, and it is deliberate: toRenderableBodies() leaves the
|
|
20
|
+
// `section` in place on the `flatPreview` body it extrudes, so a 2D writer
|
|
21
|
+
// downstream still has the contours. `flatPreview` is the flag that says
|
|
22
|
+
// so; nothing else sets both.
|
|
19
23
|
// `role`/`triColors` are later-phase fields (modifier tagging -- Phase 3;
|
|
20
24
|
// multi-color CSG merges -- Phase 3/8) included now so the struct shape
|
|
21
25
|
// doesn't need reworking when those land.
|
|
@@ -127,6 +127,36 @@ void writeOff(const std::string& path, const std::vector<ColoredBody>& bodies);
|
|
|
127
127
|
// no geometry to export.
|
|
128
128
|
void writeThreeMf(const std::string& path, const std::vector<ExportObject>& objects);
|
|
129
129
|
|
|
130
|
+
// -- 2D ------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
// Options for the SVG writer, named and defaulted after OpenSCAD's own
|
|
133
|
+
// `-O export-svg/...` set. `stroke`/`strokeWidth` are not purely cosmetic:
|
|
134
|
+
// half the stroke width pads the page, so turning the stroke off shrinks
|
|
135
|
+
// the document.
|
|
136
|
+
struct ExportSvgOptions {
|
|
137
|
+
bool fill = false;
|
|
138
|
+
std::string fillColor = "white";
|
|
139
|
+
bool stroke = true;
|
|
140
|
+
std::string strokeColor = "black";
|
|
141
|
+
double strokeWidth = 0.35;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// SVG 1.1 of a 2D-only model, in millimetres at 1:1 -- so a print of it
|
|
145
|
+
// measures what the script says. Every contour of a body, holes included,
|
|
146
|
+
// becomes a `M .. L .. z` subpath of that body's single `<path>`.
|
|
147
|
+
//
|
|
148
|
+
// Model space is Y-up and SVG's is Y-down, so the whole point transform is
|
|
149
|
+
// a Y negation. The page is the bounding box padded by half the stroke
|
|
150
|
+
// width and rounded OUTWARD to whole millimetres, which is why the margin
|
|
151
|
+
// is not a constant (0 to just under 1mm per side, depending where the
|
|
152
|
+
// bounds fall). Both rules are OpenSCAD's, read from its export_svg.cc and
|
|
153
|
+
// checked against its binary.
|
|
154
|
+
//
|
|
155
|
+
// Throws std::runtime_error if any top-level body is 3D -- OpenSCAD's rule,
|
|
156
|
+
// and the only honest answer, since there is no projection to fall back on.
|
|
157
|
+
void writeSvg(const std::string& path, const std::vector<ColoredBody>& bodies,
|
|
158
|
+
const ExportSvgOptions& opts = {});
|
|
159
|
+
|
|
130
160
|
// -- one entry point ------------------------------------------------------
|
|
131
161
|
|
|
132
162
|
struct ExportOptions {
|
|
@@ -144,6 +174,8 @@ struct ExportOptions {
|
|
|
144
174
|
// pieces it is in; see that function's own comment for why the default
|
|
145
175
|
// moved here.
|
|
146
176
|
bool splitComponents = false;
|
|
177
|
+
// .svg only.
|
|
178
|
+
ExportSvgOptions svg;
|
|
147
179
|
};
|
|
148
180
|
|
|
149
181
|
// Writes `bodies` to `path`, choosing the writer from the extension (or
|
|
@@ -292,7 +292,10 @@ 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, split_components: bool = False
|
|
295
|
+
strip_slivers: bool = True, split_components: bool = False,
|
|
296
|
+
svg_fill: bool = False, svg_fill_color: str = "white",
|
|
297
|
+
svg_stroke: bool = True, svg_stroke_color: str = "black",
|
|
298
|
+
svg_stroke_width: float = 0.35) -> list:
|
|
296
299
|
"""Write `geometry` to `path`. Returns the warnings to surface.
|
|
297
300
|
|
|
298
301
|
`geometry` is the handle an Evaluator stashes on itself as
|
|
@@ -312,10 +315,17 @@ def export_model(path: str, geometry, format: str = "", ascii_stl: bool = False,
|
|
|
312
315
|
|
|
313
316
|
`split_components` gives every disconnected piece its own object in the
|
|
314
317
|
multi-object formats. Off by default, which is what OpenSCAD writes.
|
|
318
|
+
|
|
319
|
+
The `svg_*` arguments are OpenSCAD's `-O export-svg/...` set and apply
|
|
320
|
+
to `.svg` only, which writes a 2D model at 1:1 in millimetres.
|
|
321
|
+
`svg_stroke`/`svg_stroke_width` also pad the page, so they are not
|
|
322
|
+
purely cosmetic. An `.svg` export of anything 3D raises, as OpenSCAD's
|
|
323
|
+
does -- there is no projection to fall back on.
|
|
315
324
|
"""
|
|
316
325
|
try:
|
|
317
326
|
return list(_ext.export_model(path, geometry, format, ascii_stl, strip_slivers,
|
|
318
|
-
split_components
|
|
327
|
+
split_components, svg_fill, svg_fill_color,
|
|
328
|
+
svg_stroke, svg_stroke_color, svg_stroke_width))
|
|
319
329
|
except Exception as e:
|
|
320
330
|
raise EvalError(str(e)) from e
|
|
321
331
|
|
|
@@ -497,7 +507,7 @@ class Evaluator:
|
|
|
497
507
|
self.dyn_explicit = set()
|
|
498
508
|
|
|
499
509
|
def evaluate(self, source_path: str, viewport_params: Optional[dict] = None,
|
|
500
|
-
generate: bool = True):
|
|
510
|
+
generate: bool = True, strict_commas: bool = False):
|
|
501
511
|
"""Run `source_path` and return (bodies, id_to_node).
|
|
502
512
|
|
|
503
513
|
`generate=False` stops after the resolve pass. The script still runs
|
|
@@ -508,6 +518,13 @@ class Evaluator:
|
|
|
508
518
|
the geometry without skipping the language.
|
|
509
519
|
|
|
510
520
|
Ignored on the debugger path, which has no geometry handle anyway.
|
|
521
|
+
|
|
522
|
+
`strict_commas=True` makes a trailing comma in a call argument list
|
|
523
|
+
or a let/for assignment list a syntax error, as OpenSCAD 2021.01 did
|
|
524
|
+
-- `cube(1,)` and `let(x = 1,)`, but NOT `[2, 4,]` or
|
|
525
|
+
`module m(a, b,)`, which 2021.01 accepted. For checking a script
|
|
526
|
+
against that version; off, and no divergence from current OpenSCAD,
|
|
527
|
+
otherwise. Also ignored on the debugger path.
|
|
511
528
|
"""
|
|
512
529
|
vp = viewport_params or {}
|
|
513
530
|
try:
|
|
@@ -528,7 +545,8 @@ class Evaluator:
|
|
|
528
545
|
else:
|
|
529
546
|
(body_dicts, echoes, id_spans, csg_tree, profile_result, dyn,
|
|
530
547
|
dyn_explicit, geometry) = _ext.evaluate(
|
|
531
|
-
source_path, vp, self._manifold_cache, self._profile, generate
|
|
548
|
+
source_path, vp, self._manifold_cache, self._profile, generate,
|
|
549
|
+
strict_commas)
|
|
532
550
|
# The evaluated bodies, still on the C++ side. Stashed like
|
|
533
551
|
# csg_tree/profile_result rather than returned, so
|
|
534
552
|
# evaluate()'s own 2-tuple result is unchanged -- callers
|
|
@@ -43,6 +43,13 @@ std::vector<ColoredBody> toRenderableBodies(const std::vector<ColoredBody>& bodi
|
|
|
43
43
|
flat.body = flat.body->Transform(cb.sectionXform);
|
|
44
44
|
flat.color = cb.color;
|
|
45
45
|
flat.flatPreview = true;
|
|
46
|
+
// Carried, not consumed: the 2D writers (writeSvg) need the
|
|
47
|
+
// contours, and every caller that exports goes through this
|
|
48
|
+
// conversion first -- the Python binding hands ONE body list to
|
|
49
|
+
// both the renderer and export. Dropping it here made an SVG
|
|
50
|
+
// export of any 2D script impossible from Python.
|
|
51
|
+
flat.section = cb.section;
|
|
52
|
+
flat.sectionXform = cb.sectionXform;
|
|
46
53
|
flat.role = cb.role;
|
|
47
54
|
out.push_back(std::move(flat));
|
|
48
55
|
} else {
|