openscad-cpp-evaluator 1.1.0__tar.gz → 1.3.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.1.0 → openscad_cpp_evaluator-1.3.0}/PKG-INFO +1 -1
- openscad_cpp_evaluator-1.3.0/include/openscad_cpp_evaluator/format_closure.hpp +20 -0
- openscad_cpp_evaluator-1.3.0/include/openscad_cpp_evaluator/osc_range.hpp +44 -0
- openscad_cpp_evaluator-1.3.0/include/openscad_cpp_evaluator/utf8.hpp +35 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/CMakeLists.txt +2 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/booleans.cpp +30 -16
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/function_builtins.cpp +38 -10
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/csg_generate.cpp +25 -2
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/expr_eval.cpp +8 -2
- openscad_cpp_evaluator-1.3.0/src/format_closure.cpp +217 -0
- openscad_cpp_evaluator-1.3.0/src/utf8.cpp +61 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/value.cpp +28 -4
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_booleans.cpp +48 -35
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_control_flow.cpp +6 -3
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_python_bindings.py +151 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/uv.lock +1 -1
- openscad_cpp_evaluator-1.1.0/include/openscad_cpp_evaluator/osc_range.hpp +0 -19
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/tools/cli/main.cpp +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
namespace oscad {
|
|
6
|
+
class FunctionLiteral;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
namespace oscadeval {
|
|
10
|
+
|
|
11
|
+
// A function literal spelled the way the reference spells it when a script
|
|
12
|
+
// does `str(f)` or `echo(f)`: its own source, re-printed from the AST with
|
|
13
|
+
// every binary operator and ternary parenthesised.
|
|
14
|
+
//
|
|
15
|
+
// Deliberately not the parser's ASTNode::toString(), which is
|
|
16
|
+
// precedence-minimal and which 94 places inside the parser depend on. See
|
|
17
|
+
// format_closure.cpp for where each rule came from.
|
|
18
|
+
std::string formatFunctionLiteral(const oscad::FunctionLiteral& fn);
|
|
19
|
+
|
|
20
|
+
} // namespace oscadeval
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cmath>
|
|
4
|
+
|
|
5
|
+
namespace oscadeval {
|
|
6
|
+
|
|
7
|
+
// `[start:step:end]` -- a plain 3-slot struct, not an expanded list. Mirrors
|
|
8
|
+
// the Python reference's OscRange object exactly: echoed as
|
|
9
|
+
// "[start : step : end]", only expanded to a list when iterated (for/list
|
|
10
|
+
// comprehension/intersection_for).
|
|
11
|
+
struct OscRange {
|
|
12
|
+
double start = 0.0;
|
|
13
|
+
double step = 1.0;
|
|
14
|
+
double end = 0.0;
|
|
15
|
+
|
|
16
|
+
// Yields nothing: a zero step, a direction that disagrees with the
|
|
17
|
+
// bounds, or a NaN anywhere in it.
|
|
18
|
+
bool isEmpty() const {
|
|
19
|
+
if (std::isnan(start) || std::isnan(step) || std::isnan(end)) return true;
|
|
20
|
+
if (step == 0.0) return true;
|
|
21
|
+
return (end - start) / step < -1e-10;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Two EMPTY ranges are equal whatever their bounds -- the reference
|
|
25
|
+
// compares element counts first, so `[5:1:0] == [10:1:0]` is true, and
|
|
26
|
+
// so is `[0:nan:inf] == [5:1:0]`. Only if both yield something does it
|
|
27
|
+
// compare start/step/end.
|
|
28
|
+
//
|
|
29
|
+
// This is not a NaN rule, though a NaN range is caught by it: checked
|
|
30
|
+
// against the reference, `nan == nan` is false and `[nan] == [nan]` is
|
|
31
|
+
// false, so nothing else treats NaN as equal to itself. Ranges are
|
|
32
|
+
// special because emptiness is.
|
|
33
|
+
//
|
|
34
|
+
// It matters well beyond range comparison: BOSL2 defines
|
|
35
|
+
// `is_nan(x) = (x != x)`, so a self-unequal range made typeof() answer
|
|
36
|
+
// "nan" for one, where the reference says "invalid".
|
|
37
|
+
friend bool operator==(const OscRange& a, const OscRange& b) {
|
|
38
|
+
const bool ae = a.isEmpty(), be = b.isEmpty();
|
|
39
|
+
if (ae || be) return ae && be;
|
|
40
|
+
return a.start == b.start && a.step == b.step && a.end == b.end;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
} // namespace oscadeval
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <vector>
|
|
5
|
+
|
|
6
|
+
namespace oscadeval {
|
|
7
|
+
|
|
8
|
+
// A string is a sequence of CHARACTERS to a script, not of bytes. The
|
|
9
|
+
// reference reports len("aé—z") as 4, indexes s[1] to "é", and iterates the
|
|
10
|
+
// same four characters -- while a std::string holds 8 bytes for that. These
|
|
11
|
+
// three are what keeps the two views apart.
|
|
12
|
+
//
|
|
13
|
+
// Storage stays UTF-8. Indexing therefore scans, which is O(n) per index and
|
|
14
|
+
// so O(n^2) over a loop -- BOSL2's _str_count_leading is exactly that loop.
|
|
15
|
+
// The saving grace is that a pure-ASCII string needs no scan at all: byte i
|
|
16
|
+
// IS character i, and every function here takes that path first. Only a
|
|
17
|
+
// string that actually holds non-ASCII pays, and those are short in
|
|
18
|
+
// practice.
|
|
19
|
+
|
|
20
|
+
// True when every byte is < 0x80, so byte offsets and character offsets are
|
|
21
|
+
// the same thing.
|
|
22
|
+
bool utf8IsAscii(const std::string& s);
|
|
23
|
+
|
|
24
|
+
// Characters, not bytes. Counts lead bytes, so a malformed sequence counts
|
|
25
|
+
// the same way the reference's own decoder would rather than throwing.
|
|
26
|
+
size_t utf8Length(const std::string& s);
|
|
27
|
+
|
|
28
|
+
// The i-th character as its own string; empty if i is past the end. Empty is
|
|
29
|
+
// distinguishable from a real result because a character is never zero-length.
|
|
30
|
+
std::string utf8CharAt(const std::string& s, size_t i);
|
|
31
|
+
|
|
32
|
+
// Every character as its own string.
|
|
33
|
+
std::vector<std::string> utf8Chars(const std::string& s);
|
|
34
|
+
|
|
35
|
+
} // namespace oscadeval
|
|
@@ -214,7 +214,15 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
214
214
|
: &std::get<ListPtr>(emptyIsAGroupIt->second)->items;
|
|
215
215
|
|
|
216
216
|
std::vector<ColoredBody> allBg, allHi, allSo, allDo;
|
|
217
|
-
|
|
217
|
+
// Two accumulators, one per dimension. union() may legitimately carry
|
|
218
|
+
// both -- `translate(...) { part(); text("label"); }` is how a docs
|
|
219
|
+
// figure annotates a 3D part, and the reference draws both -- and a
|
|
220
|
+
// single optional that switched between body and section is exactly
|
|
221
|
+
// what used to be dereferenced empty when a group changed dimension
|
|
222
|
+
// part-way. difference and intersection still see one dimension only:
|
|
223
|
+
// applyDimensionRules drops the mismatch before it gets here, so for
|
|
224
|
+
// them just one of these is ever populated.
|
|
225
|
+
std::optional<ColoredBody> res3d, res2d;
|
|
218
226
|
size_t idx = 0;
|
|
219
227
|
|
|
220
228
|
size_t stmtIndex = 0;
|
|
@@ -273,50 +281,56 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
273
281
|
// discards while no positive operand has been established yet;
|
|
274
282
|
// union just skips the empty contributor and keeps going.
|
|
275
283
|
if (op == "intersection") {
|
|
276
|
-
|
|
284
|
+
res3d.reset();
|
|
285
|
+
res2d.reset();
|
|
277
286
|
break;
|
|
278
287
|
}
|
|
279
|
-
if (op == "difference" && !
|
|
288
|
+
if (op == "difference" && !res3d && !res2d) break;
|
|
280
289
|
continue;
|
|
281
290
|
}
|
|
282
291
|
|
|
292
|
+
// Each dimension accumulates on its own, so a statement holding
|
|
293
|
+
// both contributes to both rather than the 2D half falling off the
|
|
294
|
+
// end of an if/else.
|
|
283
295
|
if (!bodies3d.empty()) {
|
|
284
296
|
manifold::Manifold grp = *bodies3d.front().body;
|
|
285
297
|
for (size_t i = 1; i < bodies3d.size(); ++i) grp = grp + *bodies3d[i].body;
|
|
286
|
-
if (!
|
|
298
|
+
if (!res3d) {
|
|
287
299
|
ColoredBody cb;
|
|
288
300
|
cb.body = std::move(grp);
|
|
289
301
|
cb.color = bodies3d.front().color;
|
|
290
|
-
|
|
302
|
+
res3d = std::move(cb);
|
|
291
303
|
} else if (op == "union") {
|
|
292
|
-
|
|
304
|
+
res3d->body = *res3d->body + grp;
|
|
293
305
|
} else if (op == "difference") {
|
|
294
|
-
|
|
306
|
+
res3d->body = *res3d->body - grp;
|
|
295
307
|
} else if (op == "intersection") {
|
|
296
|
-
|
|
308
|
+
res3d->body = *res3d->body ^ grp;
|
|
297
309
|
}
|
|
298
|
-
}
|
|
310
|
+
}
|
|
311
|
+
if (!sections2d.empty()) {
|
|
299
312
|
manifold::CrossSection grp = *sections2d.front().section;
|
|
300
313
|
for (size_t i = 1; i < sections2d.size(); ++i) grp = grp + *sections2d[i].section;
|
|
301
|
-
if (!
|
|
314
|
+
if (!res2d) {
|
|
302
315
|
ColoredBody cb;
|
|
303
316
|
cb.section = std::move(grp);
|
|
304
317
|
cb.color = sections2d.front().color;
|
|
305
|
-
|
|
318
|
+
res2d = std::move(cb);
|
|
306
319
|
} else if (op == "union") {
|
|
307
|
-
|
|
320
|
+
res2d->section = *res2d->section + grp;
|
|
308
321
|
} else if (op == "difference") {
|
|
309
|
-
|
|
322
|
+
res2d->section = *res2d->section - grp;
|
|
310
323
|
} else if (op == "intersection") {
|
|
311
|
-
|
|
324
|
+
res2d->section = *res2d->section ^ grp;
|
|
312
325
|
}
|
|
313
326
|
}
|
|
314
327
|
}
|
|
315
328
|
|
|
316
|
-
if (
|
|
329
|
+
if (res3d && res3d->body) attachTriColors(ev, *res3d);
|
|
317
330
|
|
|
318
331
|
std::vector<ColoredBody> result;
|
|
319
|
-
if (
|
|
332
|
+
if (res3d) result.push_back(std::move(*res3d));
|
|
333
|
+
if (res2d) result.push_back(std::move(*res2d));
|
|
320
334
|
result.insert(result.end(), allBg.begin(), allBg.end());
|
|
321
335
|
result.insert(result.end(), allHi.begin(), allHi.end());
|
|
322
336
|
result.insert(result.end(), allSo.begin(), allSo.end());
|
{openscad_cpp_evaluator-1.1.0 → openscad_cpp_evaluator-1.3.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
7
7
|
#include "openscad_cpp_evaluator/segments.hpp"
|
|
8
8
|
#include "openscad_cpp_evaluator/text_metrics.hpp"
|
|
9
|
+
#include "openscad_cpp_evaluator/utf8.hpp"
|
|
9
10
|
|
|
10
11
|
#include "openscad_cpp_parser/ast/ast_node.hpp"
|
|
11
12
|
|
|
@@ -613,10 +614,24 @@ Value builtinRands(double minv, double maxv, double nArg, const Value& seedArg)
|
|
|
613
614
|
// a sequence of characters -- a `search(["a"], "abc")` vector needle sees
|
|
614
615
|
// that same string as an empty table, not as characters.
|
|
615
616
|
Value builtinSearch(const CallArgs& args, Evaluator& ev, const oscad::Position* pos) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
617
|
+
// The reference's own documented parameter names. These used to be
|
|
618
|
+
// "match"/"vector"/"num_returns"/"index_col", which are not names
|
|
619
|
+
// OpenSCAD ever documented, so a script written against the manual --
|
|
620
|
+
// BOSL2's in_list(), which passes num_returns_per_match and
|
|
621
|
+
// index_col_num -- bound neither and silently got the defaults.
|
|
622
|
+
//
|
|
623
|
+
// Worth knowing, though deliberately NOT copied: the reference ignores
|
|
624
|
+
// these names altogether and binds search() purely by position.
|
|
625
|
+
// `search(zzz=m, qqq=t, www=1, eee=1)` returns the right answer there,
|
|
626
|
+
// while the correct names in the wrong order return the wrong one.
|
|
627
|
+
// CallArgs keeps positional and named arguments apart and does not
|
|
628
|
+
// record where a named one was written, so matching that would mean
|
|
629
|
+
// changing how every call collects its arguments -- for a quirk no
|
|
630
|
+
// script relies on deliberately.
|
|
631
|
+
const Value matchArg = getArg(args, 0, "match_value", Value{});
|
|
632
|
+
const Value tableArg = getArg(args, 1, "string_or_vector", Value{});
|
|
633
|
+
const double nrRaw = toDoubleLenient(getArg(args, 2, "num_returns_per_match", Value{1.0}));
|
|
634
|
+
const double icRaw = toDoubleLenient(getArg(args, 3, "index_col_num", Value{0.0}));
|
|
620
635
|
// Both are `unsigned int` in the reference, so a negative argument wraps
|
|
621
636
|
// to a huge positive rather than meaning "unlimited"/"column 0".
|
|
622
637
|
const unsigned numReturns = static_cast<unsigned>(static_cast<long long>(nrRaw));
|
|
@@ -654,11 +669,17 @@ Value builtinSearch(const CallArgs& args, Evaluator& ev, const oscad::Position*
|
|
|
654
669
|
if (const std::string* needle = std::get_if<std::string>(&matchArg)) {
|
|
655
670
|
std::vector<Value> out;
|
|
656
671
|
if (const std::string* hay = std::get_if<std::string>(&tableArg)) {
|
|
657
|
-
|
|
672
|
+
// Both sides walked as CHARACTERS, and the indices reported are
|
|
673
|
+
// character indices. Comparing bytes made a multi-byte needle
|
|
674
|
+
// search once per byte -- search("é", "aé—z") answered [1, 2]
|
|
675
|
+
// where the reference answers [1].
|
|
676
|
+
const std::vector<std::string> needleChars = utf8Chars(*needle);
|
|
677
|
+
const std::vector<std::string> hayChars = utf8Chars(*hay);
|
|
678
|
+
for (size_t i = 0; i < needleChars.size(); ++i) {
|
|
658
679
|
unsigned matchCount = 0;
|
|
659
680
|
std::vector<Value> resultvec;
|
|
660
|
-
for (size_t j = 0; j <
|
|
661
|
-
if (
|
|
681
|
+
for (size_t j = 0; j < hayChars.size(); ++j) {
|
|
682
|
+
if (needleChars[i] != hayChars[j]) continue;
|
|
662
683
|
++matchCount;
|
|
663
684
|
if (numReturns == 1) {
|
|
664
685
|
out.push_back(num(j));
|
|
@@ -675,7 +696,10 @@ Value builtinSearch(const CallArgs& args, Evaluator& ev, const oscad::Position*
|
|
|
675
696
|
// with more than index_col elements. A single bad entry aborts the
|
|
676
697
|
// WHOLE call with an empty result, not just that row -- which is
|
|
677
698
|
// why `search("a", ["a","b"])` is [] and not [0].
|
|
678
|
-
|
|
699
|
+
// Character-wise here too: a needle character is what gets looked
|
|
700
|
+
// up in each row, so a multi-byte one must not be split.
|
|
701
|
+
const std::vector<std::string> needleChars = utf8Chars(*needle);
|
|
702
|
+
for (size_t i = 0; i < needleChars.size(); ++i) {
|
|
679
703
|
unsigned matchCount = 0;
|
|
680
704
|
std::vector<Value> resultvec;
|
|
681
705
|
for (size_t j = 0; j < table.size(); ++j) {
|
|
@@ -689,7 +713,10 @@ Value builtinSearch(const CallArgs& args, Evaluator& ev, const oscad::Position*
|
|
|
689
713
|
}
|
|
690
714
|
const std::string* entry = std::get_if<std::string>(&entryVec[indexCol]);
|
|
691
715
|
// A type mismatch just doesn't match, exactly as `==` would.
|
|
692
|
-
|
|
716
|
+
// First CHARACTER of the entry against the needle character:
|
|
717
|
+
// comparing first bytes made "é" and any other character
|
|
718
|
+
// sharing a lead byte look equal.
|
|
719
|
+
if (!entry || entry->empty() || utf8CharAt(*entry, 0) != needleChars[i]) continue;
|
|
693
720
|
++matchCount;
|
|
694
721
|
if (numReturns == 1) {
|
|
695
722
|
out.push_back(num(j));
|
|
@@ -1367,7 +1394,8 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
1367
1394
|
case BuiltinFnId::Len: {
|
|
1368
1395
|
const Value x = getArg(args, 0, "x", Value{});
|
|
1369
1396
|
if (const ListPtr* l = std::get_if<ListPtr>(&x); l && *l) return Value{static_cast<double>((*l)->items.size())};
|
|
1370
|
-
|
|
1397
|
+
// Characters, not bytes: the reference reports len("aé—z") as 4.
|
|
1398
|
+
if (const std::string* s = std::get_if<std::string>(&x)) return Value{static_cast<double>(utf8Length(*s))};
|
|
1371
1399
|
if (const ObjectPtr* o = std::get_if<ObjectPtr>(&x); o && *o) return Value{static_cast<double>((*o)->items.size())};
|
|
1372
1400
|
return Value{};
|
|
1373
1401
|
}
|
|
@@ -129,6 +129,20 @@ enum class DimRule {
|
|
|
129
129
|
None, // no dimension diagnostics at all
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
+
// Nodes that hand their children on as a list instead of combining them
|
|
133
|
+
// into one result, so a mixed 2D/3D list reaches nothing that has to pick a
|
|
134
|
+
// single dimension. generateTransform and generateColor map over the
|
|
135
|
+
// bodies; generateCsg's union keeps a 3D and a 2D accumulator side by side.
|
|
136
|
+
//
|
|
137
|
+
// difference and intersection are deliberately absent: subtracting or
|
|
138
|
+
// intersecting across dimensions has no meaning, and they still collapse to
|
|
139
|
+
// one result. hull and minkowski likewise build a single hull.
|
|
140
|
+
bool keepsMixedDimensions(const std::string& kind) {
|
|
141
|
+
return kind == "union" || kind == "color" || kind == "translate" ||
|
|
142
|
+
kind == "rotate" || kind == "scale" || kind == "mirror" ||
|
|
143
|
+
kind == "multmatrix" || kind == "resize";
|
|
144
|
+
}
|
|
145
|
+
|
|
132
146
|
DimRule dimRuleFor(const std::string& kind) {
|
|
133
147
|
if (kind == "linear_extrude" || kind == "rotate_extrude" || kind == "offset") return DimRule::Only2D;
|
|
134
148
|
if (kind == "projection") return DimRule::Only3D;
|
|
@@ -168,7 +182,11 @@ void Evaluator::applyDimensionRules(CSGNode& node) {
|
|
|
168
182
|
std::vector<CSGNode*> kids;
|
|
169
183
|
kids.reserve(node.children.size());
|
|
170
184
|
for (const std::unique_ptr<CSGNode>& c : node.children) kids.push_back(c.get());
|
|
171
|
-
|
|
185
|
+
// Only a Group-rule node can keep a mix. linear_extrude and friends take
|
|
186
|
+
// one dimension by definition, so a wrong-dimension child there is still
|
|
187
|
+
// dropped with its warning.
|
|
188
|
+
const bool keepMixed = rule == DimRule::Group && keepsMixedDimensions(node.kind);
|
|
189
|
+
applyDimensionRulesTo(kids, static_cast<int>(rule), keepMixed);
|
|
172
190
|
}
|
|
173
191
|
|
|
174
192
|
// `keepMixed` warns about a mixed group but drops nothing -- the top level's
|
|
@@ -199,7 +217,12 @@ void Evaluator::applyDimensionRulesTo(const std::vector<CSGNode*>& children, int
|
|
|
199
217
|
continue;
|
|
200
218
|
}
|
|
201
219
|
const oscad::Position* pos = child->node ? &child->node->position() : nullptr;
|
|
202
|
-
|
|
220
|
+
// Silent when both are kept: the reference says nothing for
|
|
221
|
+
// `cube(1); square(4);`, `translate(...) { ... }` or `union()
|
|
222
|
+
// { ... }` -- checked by running it -- and a warning here would
|
|
223
|
+
// fail any docs example that legitimately draws a 2D annotation
|
|
224
|
+
// beside a 3D part.
|
|
225
|
+
if (rule == DimRule::Group && !keepMixed && !warnedMixing) {
|
|
203
226
|
warn("Mixing 2D and 3D objects is not supported", pos);
|
|
204
227
|
warnedMixing = true;
|
|
205
228
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
2
|
+
#include "openscad_cpp_evaluator/utf8.hpp"
|
|
2
3
|
|
|
3
4
|
#include "openscad_cpp_evaluator/call_args.hpp"
|
|
4
5
|
#include "openscad_cpp_evaluator/eval_error.hpp"
|
|
@@ -803,8 +804,13 @@ Value Evaluator::applyIndexAccess(const Value& obj, const Value& idx) {
|
|
|
803
804
|
if (const std::string* s = std::get_if<std::string>(&obj)) {
|
|
804
805
|
if (const double* d = std::get_if<double>(&idx)) {
|
|
805
806
|
const int i = static_cast<int>(*d);
|
|
806
|
-
if (i < 0
|
|
807
|
-
|
|
807
|
+
if (i < 0) return Value{};
|
|
808
|
+
// The i-th CHARACTER. Taking byte i split a multi-byte one and
|
|
809
|
+
// handed back half of it, which then failed to decode at the
|
|
810
|
+
// boundary -- an error, not merely a wrong answer.
|
|
811
|
+
std::string ch = utf8CharAt(*s, static_cast<size_t>(i));
|
|
812
|
+
if (ch.empty()) return Value{};
|
|
813
|
+
return Value{std::move(ch)};
|
|
808
814
|
}
|
|
809
815
|
return Value{};
|
|
810
816
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// Printing a function literal back out as source, the way the reference
|
|
2
|
+
// spells it when you `str()` or `echo()` one.
|
|
3
|
+
//
|
|
4
|
+
// Neither existing printer would do. The parser's own ASTNode::toString()
|
|
5
|
+
// is precedence-MINIMAL -- it drops any parenthesis the grammar does not
|
|
6
|
+
// need -- and 94 places inside the parser depend on that spelling. The
|
|
7
|
+
// reference is the opposite: it parenthesises every binary operator and
|
|
8
|
+
// every ternary whether or not precedence requires it, so `f(a + b)` comes
|
|
9
|
+
// back as `f((a + b))`.
|
|
10
|
+
//
|
|
11
|
+
// BOSL2's fnliterals tests compare these strings exactly (61 of them), so
|
|
12
|
+
// this has to agree with the reference character for character. Every rule
|
|
13
|
+
// below was read off the reference by echoing the shape and copying what it
|
|
14
|
+
// printed -- see test_format_closure.cpp, which carries the same shapes as
|
|
15
|
+
// cases.
|
|
16
|
+
#include "openscad_cpp_evaluator/format_closure.hpp"
|
|
17
|
+
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
#include "openscad_cpp_parser/ast.hpp"
|
|
21
|
+
|
|
22
|
+
namespace oscadeval {
|
|
23
|
+
namespace {
|
|
24
|
+
|
|
25
|
+
using oscad::ASTNode;
|
|
26
|
+
using oscad::NodeKind;
|
|
27
|
+
|
|
28
|
+
std::string fmt(const ASTNode& n);
|
|
29
|
+
|
|
30
|
+
// `(a + b)`, always. The reference parenthesises a binary operator
|
|
31
|
+
// unconditionally -- nesting one inside another gives `((a + b) + c)`, and
|
|
32
|
+
// even a call argument gets them: `f((a + b))`.
|
|
33
|
+
//
|
|
34
|
+
// Templated on the node type rather than casting every operator to one of
|
|
35
|
+
// them. The binary classes are generated by a single macro so they do share
|
|
36
|
+
// a layout, but they are distinct types with no common binary base, and
|
|
37
|
+
// casting a MultiplicationOp to an AdditionOp would be undefined however
|
|
38
|
+
// reliably it happens to work.
|
|
39
|
+
template <typename T>
|
|
40
|
+
std::string binary(const ASTNode& n, const char* op) {
|
|
41
|
+
const auto& e = static_cast<const T&>(n);
|
|
42
|
+
return "(" + fmt(*e.left) + " " + op + " " + fmt(*e.right) + ")";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// `-x`, not `(-x)`. A unary operator is never parenthesised itself, though
|
|
46
|
+
// its operand still follows the rules: `-(a + b)`.
|
|
47
|
+
template <typename T>
|
|
48
|
+
std::string unary(const ASTNode& n, const char* op) {
|
|
49
|
+
const auto& e = static_cast<const T&>(n);
|
|
50
|
+
return std::string(op) + fmt(*e.expr);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
template <typename Seq>
|
|
54
|
+
std::string join(const Seq& items, const char* sep) {
|
|
55
|
+
std::string out;
|
|
56
|
+
for (size_t i = 0; i < items.size(); ++i) {
|
|
57
|
+
if (i) out += sep;
|
|
58
|
+
out += fmt(*items[i]);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// A list-comprehension body is wrapped, on top of whatever the body itself
|
|
64
|
+
// prints: `[for(i = [0 : a]) ((i + 1))]`, and `(i)` for a bare identifier.
|
|
65
|
+
// A plain vector element is NOT wrapped -- `[1, (2 + 3), a]` -- so the
|
|
66
|
+
// parentheses belong to for/if/each rather than to the vector.
|
|
67
|
+
std::string wrapped(const ASTNode& n) { return "(" + fmt(n) + ")"; }
|
|
68
|
+
|
|
69
|
+
std::string fmt(const ASTNode& n) {
|
|
70
|
+
switch (n.kind()) {
|
|
71
|
+
case NodeKind::Identifier:
|
|
72
|
+
return static_cast<const oscad::Identifier&>(n).name;
|
|
73
|
+
case NodeKind::StringLiteral:
|
|
74
|
+
case NodeKind::NumberLiteral:
|
|
75
|
+
case NodeKind::BooleanLiteral:
|
|
76
|
+
case NodeKind::UndefinedLiteral:
|
|
77
|
+
// The parser already spells these the way the reference does,
|
|
78
|
+
// escaping and number formatting included.
|
|
79
|
+
return n.toString();
|
|
80
|
+
|
|
81
|
+
case NodeKind::RangeLiteral: {
|
|
82
|
+
const auto& r = static_cast<const oscad::RangeLiteral&>(n);
|
|
83
|
+
// implicitStep, not just `step != nullptr`: the parser fills in a
|
|
84
|
+
// step of 1 for `[0:a]`, and printing it back gives `[0 : 1 : a]`
|
|
85
|
+
// where the reference says `[0 : a]`. The flag is there to tell the
|
|
86
|
+
// two apart.
|
|
87
|
+
std::string s = "[" + fmt(*r.start) + " : ";
|
|
88
|
+
if (r.step && !r.implicitStep) s += fmt(*r.step) + " : ";
|
|
89
|
+
return s + fmt(*r.end) + "]";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case NodeKind::UnaryMinusOp: return unary<oscad::UnaryMinusOp>(n, "-");
|
|
93
|
+
case NodeKind::LogicalNotOp: return unary<oscad::LogicalNotOp>(n, "!");
|
|
94
|
+
case NodeKind::BitwiseNotOp: return unary<oscad::BitwiseNotOp>(n, "~");
|
|
95
|
+
|
|
96
|
+
case NodeKind::AdditionOp: return binary<oscad::AdditionOp>(n, "+");
|
|
97
|
+
case NodeKind::SubtractionOp: return binary<oscad::SubtractionOp>(n, "-");
|
|
98
|
+
case NodeKind::MultiplicationOp: return binary<oscad::MultiplicationOp>(n, "*");
|
|
99
|
+
case NodeKind::DivisionOp: return binary<oscad::DivisionOp>(n, "/");
|
|
100
|
+
case NodeKind::ModuloOp: return binary<oscad::ModuloOp>(n, "%");
|
|
101
|
+
case NodeKind::ExponentOp: return binary<oscad::ExponentOp>(n, "^");
|
|
102
|
+
case NodeKind::BitwiseAndOp: return binary<oscad::BitwiseAndOp>(n, "&");
|
|
103
|
+
case NodeKind::BitwiseOrOp: return binary<oscad::BitwiseOrOp>(n, "|");
|
|
104
|
+
case NodeKind::BitwiseShiftLeftOp: return binary<oscad::BitwiseShiftLeftOp>(n, "<<");
|
|
105
|
+
case NodeKind::BitwiseShiftRightOp: return binary<oscad::BitwiseShiftRightOp>(n, ">>");
|
|
106
|
+
case NodeKind::LogicalAndOp: return binary<oscad::LogicalAndOp>(n, "&&");
|
|
107
|
+
case NodeKind::LogicalOrOp: return binary<oscad::LogicalOrOp>(n, "||");
|
|
108
|
+
case NodeKind::EqualityOp: return binary<oscad::EqualityOp>(n, "==");
|
|
109
|
+
case NodeKind::InequalityOp: return binary<oscad::InequalityOp>(n, "!=");
|
|
110
|
+
case NodeKind::GreaterThanOp: return binary<oscad::GreaterThanOp>(n, ">");
|
|
111
|
+
case NodeKind::GreaterThanOrEqualOp: return binary<oscad::GreaterThanOrEqualOp>(n, ">=");
|
|
112
|
+
case NodeKind::LessThanOp: return binary<oscad::LessThanOp>(n, "<");
|
|
113
|
+
case NodeKind::LessThanOrEqualOp: return binary<oscad::LessThanOrEqualOp>(n, "<=");
|
|
114
|
+
|
|
115
|
+
case NodeKind::TernaryOp: {
|
|
116
|
+
const auto& t = static_cast<const oscad::TernaryOp&>(n);
|
|
117
|
+
return "(" + fmt(*t.condition) + " ? " + fmt(*t.trueExpr) + " : " + fmt(*t.falseExpr) + ")";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
case NodeKind::PrimaryCall: {
|
|
121
|
+
const auto& c = static_cast<const oscad::PrimaryCall&>(n);
|
|
122
|
+
return fmt(*c.left) + "(" + join(c.arguments, ", ") + ")";
|
|
123
|
+
}
|
|
124
|
+
case NodeKind::PrimaryIndex: {
|
|
125
|
+
const auto& i = static_cast<const oscad::PrimaryIndex&>(n);
|
|
126
|
+
return fmt(*i.left) + "[" + fmt(*i.index) + "]";
|
|
127
|
+
}
|
|
128
|
+
case NodeKind::PrimaryMember: {
|
|
129
|
+
const auto& m = static_cast<const oscad::PrimaryMember&>(n);
|
|
130
|
+
return fmt(*m.left) + "." + m.member->name;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case NodeKind::PositionalArgument:
|
|
134
|
+
return fmt(*static_cast<const oscad::PositionalArgument&>(n).expr);
|
|
135
|
+
case NodeKind::NamedArgument: {
|
|
136
|
+
const auto& a = static_cast<const oscad::NamedArgument&>(n);
|
|
137
|
+
return a.name->name + " = " + fmt(*a.expr);
|
|
138
|
+
}
|
|
139
|
+
case NodeKind::Assignment: {
|
|
140
|
+
const auto& a = static_cast<const oscad::Assignment&>(n);
|
|
141
|
+
return a.name->name + " = " + fmt(*a.expr);
|
|
142
|
+
}
|
|
143
|
+
case NodeKind::ParameterDeclaration: {
|
|
144
|
+
const auto& p = static_cast<const oscad::ParameterDeclaration&>(n);
|
|
145
|
+
return p.defaultValue ? p.name->name + " = " + fmt(*p.defaultValue) : p.name->name;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
case NodeKind::FunctionLiteral: {
|
|
149
|
+
const auto& f = static_cast<const oscad::FunctionLiteral&>(n);
|
|
150
|
+
return "function(" + join(f.parameters, ", ") + ") " + fmt(*f.body);
|
|
151
|
+
}
|
|
152
|
+
case NodeKind::LetOp: {
|
|
153
|
+
const auto& l = static_cast<const oscad::LetOp&>(n);
|
|
154
|
+
// Body bare: `let(c = (a + b)) c`. What looks like a wrapped body
|
|
155
|
+
// in `let(a = 1) (a + x)` is the binary parenthesising itself.
|
|
156
|
+
return "let(" + join(l.assignments, ", ") + ") " + fmt(*l.body);
|
|
157
|
+
}
|
|
158
|
+
case NodeKind::EchoOp: {
|
|
159
|
+
const auto& e = static_cast<const oscad::EchoOp&>(n);
|
|
160
|
+
return "echo(" + join(e.arguments, ", ") + ") " + fmt(*e.body);
|
|
161
|
+
}
|
|
162
|
+
case NodeKind::AssertOp: {
|
|
163
|
+
const auto& a = static_cast<const oscad::AssertOp&>(n);
|
|
164
|
+
return "assert(" + join(a.arguments, ", ") + ") " + fmt(*a.body);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case NodeKind::ListComprehension: {
|
|
168
|
+
const auto& c = static_cast<const oscad::ListComprehension&>(n);
|
|
169
|
+
return "[" + join(c.elements, ", ") + "]";
|
|
170
|
+
}
|
|
171
|
+
case NodeKind::ListCompFor: {
|
|
172
|
+
const auto& f = static_cast<const oscad::ListCompFor&>(n);
|
|
173
|
+
return "for(" + join(f.assignments, ", ") + ") " + wrapped(*f.body);
|
|
174
|
+
}
|
|
175
|
+
case NodeKind::ListCompCFor: {
|
|
176
|
+
const auto& f = static_cast<const oscad::ListCompCFor&>(n);
|
|
177
|
+
// The odd one out: no space after the semicolons, and the body is
|
|
178
|
+
// NOT wrapped -- `[for(i = 0;(i < a);i = (i + 1)) i]`.
|
|
179
|
+
return "for(" + join(f.inits, ", ") + ";" + fmt(*f.condition) + ";" +
|
|
180
|
+
join(f.incrs, ", ") + ") " + fmt(*f.body);
|
|
181
|
+
}
|
|
182
|
+
case NodeKind::ListCompIf: {
|
|
183
|
+
const auto& i = static_cast<const oscad::ListCompIf&>(n);
|
|
184
|
+
return "if(" + fmt(*i.condition) + ") " + wrapped(*i.trueExpr);
|
|
185
|
+
}
|
|
186
|
+
case NodeKind::ListCompIfElse: {
|
|
187
|
+
const auto& i = static_cast<const oscad::ListCompIfElse&>(n);
|
|
188
|
+
return "if(" + fmt(*i.condition) + ") " + wrapped(*i.trueExpr) +
|
|
189
|
+
" else " + wrapped(*i.falseExpr);
|
|
190
|
+
}
|
|
191
|
+
case NodeKind::ListCompEach: {
|
|
192
|
+
const auto& e = static_cast<const oscad::ListCompEach&>(n);
|
|
193
|
+
return "each " + wrapped(*e.body);
|
|
194
|
+
}
|
|
195
|
+
case NodeKind::ListCompLet: {
|
|
196
|
+
const auto& l = static_cast<const oscad::ListCompLet&>(n);
|
|
197
|
+
return "let(" + join(l.assignments, ", ") + ") " + fmt(*l.body);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
case NodeKind::CommentedExpr:
|
|
201
|
+
// Comments are not part of what the reference prints.
|
|
202
|
+
return fmt(*static_cast<const oscad::CommentedExpr&>(n).expr);
|
|
203
|
+
|
|
204
|
+
default:
|
|
205
|
+
// Anything not reachable from inside a function literal's body --
|
|
206
|
+
// statements, module instantiations, include directives. Falling
|
|
207
|
+
// back to the parser's own spelling is closer than an empty string,
|
|
208
|
+
// and the value here is diagnostic rather than compared.
|
|
209
|
+
return n.toString();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
} // namespace
|
|
214
|
+
|
|
215
|
+
std::string formatFunctionLiteral(const oscad::FunctionLiteral& fn) { return fmt(fn); }
|
|
216
|
+
|
|
217
|
+
} // namespace oscadeval
|