openscad-cpp-evaluator 0.32.2__tar.gz → 0.33.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/value.hpp +14 -1
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/function_builtins.cpp +359 -112
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/expr_eval.cpp +49 -34
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/value.cpp +169 -65
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_bytecode_compiler.cpp +5 -4
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_control_flow.cpp +2 -2
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_expr_eval.cpp +107 -6
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_function_builtins.cpp +151 -5
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_value.cpp +3 -3
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/uv.lock +1 -1
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/tools/cli/main.cpp +0 -0
|
@@ -147,6 +147,12 @@ bool oscComparable(const Value& a, const Value& b);
|
|
|
147
147
|
Value scale(double scalarValue, const Value& value);
|
|
148
148
|
Value divScale(const Value& value, double divisor);
|
|
149
149
|
|
|
150
|
+
// `number / vector`, the mirror of divScale's `vector / number` -- the
|
|
151
|
+
// reference divides the scalar by each element in turn (Value.cc's
|
|
152
|
+
// `operator/` NUMBER/VECTOR branch, `*this / vecval`), recursing into
|
|
153
|
+
// nested lists the same way. `5 / [1, 2]` is `[5, 2.5]`, not undef.
|
|
154
|
+
Value divInto(double numerator, const Value& value);
|
|
155
|
+
|
|
150
156
|
// Elementwise `+`/`-` between two lists (zip semantics: truncates to the
|
|
151
157
|
// shorter length when lengths differ), or the scalar/mismatched-type
|
|
152
158
|
// fallback (numeric + numeric, everything else -> undef). `vecAdd` rejects
|
|
@@ -167,7 +173,14 @@ Value vecSub(const Value& a, const Value& b);
|
|
|
167
173
|
// a Python bool silently). This divergence is undocumented/untested
|
|
168
174
|
// behavior in the reference implementation -- revisit only if a real
|
|
169
175
|
// script's `*` on a bool-containing vector is shown to need it.
|
|
170
|
-
|
|
176
|
+
//
|
|
177
|
+
// `error`, when non-null, receives the reference's own diagnostic text for
|
|
178
|
+
// whichever undefined case was hit (multiply_visitor / multvecvec /
|
|
179
|
+
// multmatvec / multvecmat in the reference's Value.cc) -- "vector*vector
|
|
180
|
+
// requires matching lengths (2 != 3)", "Matrix must be rectangular. Problem
|
|
181
|
+
// at row 1", and so on. applyBinaryOp warns with it; a caller that only
|
|
182
|
+
// wants the value can leave it null. It is only written on an undef result.
|
|
183
|
+
Value matmul(const Value& a, const Value& b, std::string* error = nullptr);
|
|
171
184
|
|
|
172
185
|
// OpenSCAD's echo()/str() number formatting: at most 6 significant digits,
|
|
173
186
|
// fixed-point for exponents in [-5, 5], scientific notation with no leading
|
{openscad_cpp_evaluator-0.32.2 → openscad_cpp_evaluator-0.33.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#include <algorithm>
|
|
11
11
|
#include <cmath>
|
|
12
|
+
#include <functional>
|
|
12
13
|
#include <limits>
|
|
13
14
|
#include <numbers>
|
|
14
15
|
#include <random>
|
|
@@ -186,97 +187,130 @@ Value builtinRands(double minv, double maxv, double nArg, const Value& seedArg)
|
|
|
186
187
|
return numList(out);
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
|
|
190
|
+
// Ported branch-for-branch from the reference's builtin_search plus its
|
|
191
|
+
// three static search() overloads (builtin_functions.cc). The dispatch is on
|
|
192
|
+
// what is being searched FOR -- number, string, or vector; every other type
|
|
193
|
+
// (undef, bool, range, function, object) falls off the end into a bare
|
|
194
|
+
// undef. What is being searched IN is never type-checked at all: the
|
|
195
|
+
// reference just calls .toVector() on it, which yields an EMPTY vector for
|
|
196
|
+
// any non-vector, so searching in undef/a number/a bool finds nothing rather
|
|
197
|
+
// than failing. Only the string-in-string form treats a string haystack as
|
|
198
|
+
// a sequence of characters -- a `search(["a"], "abc")` vector needle sees
|
|
199
|
+
// that same string as an empty table, not as characters.
|
|
200
|
+
Value builtinSearch(const CallArgs& args, Evaluator& ev, const oscad::Position* pos) {
|
|
190
201
|
const Value matchArg = getArg(args, 0, "match", Value{});
|
|
191
|
-
const Value
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
std::
|
|
201
|
-
const std::vector<Value
|
|
202
|
-
if (const ListPtr* vecPtr = std::get_if<ListPtr>(&vectorArg); vecPtr && *vecPtr) {
|
|
203
|
-
vecItems = &(*vecPtr)->items;
|
|
204
|
-
} else if (const std::string* vecStr = std::get_if<std::string>(&vectorArg)) {
|
|
205
|
-
stringHaystack.reserve(vecStr->size());
|
|
206
|
-
for (char c : *vecStr) stringHaystack.push_back(Value{std::string(1, c)});
|
|
207
|
-
vecItems = &stringHaystack;
|
|
208
|
-
} else {
|
|
209
|
-
return Value{};
|
|
210
|
-
}
|
|
211
|
-
const auto& vec = *vecItems;
|
|
212
|
-
const int numReturns = static_cast<int>(toDoubleLenient(getArg(args, 2, "num_returns", Value{1.0})));
|
|
213
|
-
const int col = static_cast<int>(toDoubleLenient(getArg(args, 3, "index_col", Value{0.0})));
|
|
214
|
-
|
|
215
|
-
const auto findAll = [&](const Value& val) {
|
|
216
|
-
const bool valIsList = std::holds_alternative<ListPtr>(val);
|
|
217
|
-
std::vector<int> results;
|
|
218
|
-
for (size_t i = 0; i < vec.size(); ++i) {
|
|
219
|
-
Value target;
|
|
220
|
-
if (valIsList) {
|
|
221
|
-
target = vec[i];
|
|
222
|
-
} else if (const ListPtr* itemList = std::get_if<ListPtr>(&vec[i]); itemList && *itemList) {
|
|
223
|
-
// ponytail: an out-of-range index_col silently matches
|
|
224
|
-
// nothing for that row rather than aborting the whole
|
|
225
|
-
// search() call (the reference raises and the outer
|
|
226
|
-
// try/except turns the whole call into undef) -- a narrow
|
|
227
|
-
// divergence on a malformed-argument edge case.
|
|
228
|
-
target = (col >= 0 && static_cast<size_t>(col) < (*itemList)->items.size()) ? (*itemList)->items[static_cast<size_t>(col)]
|
|
229
|
-
: Value{};
|
|
230
|
-
} else {
|
|
231
|
-
target = vec[i];
|
|
232
|
-
}
|
|
233
|
-
if (oscEqual(target, val)) results.push_back(static_cast<int>(i));
|
|
234
|
-
}
|
|
235
|
-
return results;
|
|
236
|
-
};
|
|
202
|
+
const Value tableArg = getArg(args, 1, "vector", Value{});
|
|
203
|
+
const double nrRaw = toDoubleLenient(getArg(args, 2, "num_returns", Value{1.0}));
|
|
204
|
+
const double icRaw = toDoubleLenient(getArg(args, 3, "index_col", Value{0.0}));
|
|
205
|
+
// Both are `unsigned int` in the reference, so a negative argument wraps
|
|
206
|
+
// to a huge positive rather than meaning "unlimited"/"column 0".
|
|
207
|
+
const unsigned numReturns = static_cast<unsigned>(static_cast<long long>(nrRaw));
|
|
208
|
+
const unsigned indexCol = static_cast<unsigned>(static_cast<long long>(icRaw));
|
|
209
|
+
|
|
210
|
+
static const std::vector<Value> kNoItems;
|
|
211
|
+
const ListPtr* tablePtr = std::get_if<ListPtr>(&tableArg);
|
|
212
|
+
const std::vector<Value>& table = (tablePtr && *tablePtr) ? (*tablePtr)->items : kNoItems;
|
|
237
213
|
|
|
238
|
-
const auto
|
|
239
|
-
|
|
240
|
-
return
|
|
214
|
+
const auto itemsOf = [](const Value& v) -> const std::vector<Value>& {
|
|
215
|
+
const ListPtr* l = std::get_if<ListPtr>(&v);
|
|
216
|
+
return (l && *l) ? (*l)->items : kNoItems;
|
|
241
217
|
};
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
218
|
+
// The reference's two-clause hit test, verbatim: the whole entry counts
|
|
219
|
+
// as a match only at index_col 0, and the indexed sub-element counts
|
|
220
|
+
// only when the entry really is a long enough vector.
|
|
221
|
+
const auto hits = [&](const Value& needle, const Value& entry) {
|
|
222
|
+
if (indexCol == 0 && oscEqual(needle, entry)) return true;
|
|
223
|
+
const std::vector<Value>& ev2 = itemsOf(entry);
|
|
224
|
+
return indexCol < ev2.size() && oscEqual(needle, ev2[indexCol]);
|
|
247
225
|
};
|
|
226
|
+
const auto num = [](size_t j) { return Value{static_cast<double>(j)}; };
|
|
248
227
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
};
|
|
228
|
+
if (std::holds_alternative<double>(matchArg)) {
|
|
229
|
+
std::vector<Value> out;
|
|
230
|
+
unsigned matchCount = 0;
|
|
231
|
+
for (size_t j = 0; j < table.size(); ++j) {
|
|
232
|
+
if (!hits(matchArg, table[j])) continue;
|
|
233
|
+
out.push_back(num(j));
|
|
234
|
+
if (numReturns != 0 && ++matchCount >= numReturns) break;
|
|
235
|
+
}
|
|
236
|
+
return listOf(std::move(out));
|
|
237
|
+
}
|
|
260
238
|
|
|
261
|
-
if (const std::string*
|
|
262
|
-
std::vector<Value>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
239
|
+
if (const std::string* needle = std::get_if<std::string>(&matchArg)) {
|
|
240
|
+
std::vector<Value> out;
|
|
241
|
+
if (const std::string* hay = std::get_if<std::string>(&tableArg)) {
|
|
242
|
+
for (size_t i = 0; i < needle->size(); ++i) {
|
|
243
|
+
unsigned matchCount = 0;
|
|
244
|
+
std::vector<Value> resultvec;
|
|
245
|
+
for (size_t j = 0; j < hay->size(); ++j) {
|
|
246
|
+
if ((*needle)[i] != (*hay)[j]) continue;
|
|
247
|
+
++matchCount;
|
|
248
|
+
if (numReturns == 1) {
|
|
249
|
+
out.push_back(num(j));
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
resultvec.push_back(num(j));
|
|
253
|
+
if (numReturns > 1 && matchCount >= numReturns) break;
|
|
254
|
+
}
|
|
255
|
+
if (numReturns == 0 || numReturns > 1) out.push_back(listOf(std::move(resultvec)));
|
|
256
|
+
}
|
|
257
|
+
return listOf(std::move(out));
|
|
267
258
|
}
|
|
268
|
-
|
|
259
|
+
// String needle, vector table: every entry must itself be a vector
|
|
260
|
+
// with more than index_col elements. A single bad entry aborts the
|
|
261
|
+
// WHOLE call with an empty result, not just that row -- which is
|
|
262
|
+
// why `search("a", ["a","b"])` is [] and not [0].
|
|
263
|
+
for (size_t i = 0; i < needle->size(); ++i) {
|
|
264
|
+
unsigned matchCount = 0;
|
|
265
|
+
std::vector<Value> resultvec;
|
|
266
|
+
for (size_t j = 0; j < table.size(); ++j) {
|
|
267
|
+
const std::vector<Value>& entryVec = itemsOf(table[j]);
|
|
268
|
+
if (entryVec.size() <= indexCol) {
|
|
269
|
+
ev.warn("Invalid entry in search vector at index " + std::to_string(j) +
|
|
270
|
+
", required number of values in the entry: " + std::to_string(indexCol + 1) +
|
|
271
|
+
". Invalid entry: " + fmtValue(table[j]),
|
|
272
|
+
pos);
|
|
273
|
+
return listOf({});
|
|
274
|
+
}
|
|
275
|
+
const std::string* entry = std::get_if<std::string>(&entryVec[indexCol]);
|
|
276
|
+
// A type mismatch just doesn't match, exactly as `==` would.
|
|
277
|
+
if (!entry || entry->empty() || (*entry)[0] != (*needle)[i]) continue;
|
|
278
|
+
++matchCount;
|
|
279
|
+
if (numReturns == 1) {
|
|
280
|
+
out.push_back(num(j));
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
resultvec.push_back(num(j));
|
|
284
|
+
if (numReturns > 1 && matchCount >= numReturns) break;
|
|
285
|
+
}
|
|
286
|
+
if (numReturns == 0 || numReturns > 1) out.push_back(listOf(std::move(resultvec)));
|
|
287
|
+
}
|
|
288
|
+
return listOf(std::move(out));
|
|
269
289
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
for (const Value&
|
|
274
|
-
|
|
290
|
+
|
|
291
|
+
if (std::holds_alternative<ListPtr>(matchArg)) {
|
|
292
|
+
std::vector<Value> out;
|
|
293
|
+
for (const Value& needle : itemsOf(matchArg)) {
|
|
294
|
+
unsigned matchCount = 0;
|
|
295
|
+
std::vector<Value> resultvec;
|
|
296
|
+
for (size_t j = 0; j < table.size(); ++j) {
|
|
297
|
+
if (!hits(needle, table[j])) continue;
|
|
298
|
+
++matchCount;
|
|
299
|
+
if (numReturns == 1) {
|
|
300
|
+
out.push_back(num(j));
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
resultvec.push_back(num(j));
|
|
304
|
+
if (numReturns > 1 && matchCount >= numReturns) break;
|
|
305
|
+
}
|
|
306
|
+
if ((numReturns == 1 && matchCount == 0) || numReturns == 0 || numReturns > 1) {
|
|
307
|
+
out.push_back(listOf(std::move(resultvec)));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return listOf(std::move(out));
|
|
275
311
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if (numReturns == 0) return toIdxList(matches);
|
|
279
|
-
return toIdxList(capped(matches, numReturns));
|
|
312
|
+
|
|
313
|
+
return Value{};
|
|
280
314
|
}
|
|
281
315
|
|
|
282
316
|
Value builtinLookup(const CallArgs& args) {
|
|
@@ -506,10 +540,174 @@ const std::unordered_map<std::string, BuiltinFnId>& builtinFnIds() {
|
|
|
506
540
|
return ids;
|
|
507
541
|
}
|
|
508
542
|
|
|
543
|
+
// -- reference-parity argument diagnostics -------------------------------
|
|
544
|
+
//
|
|
545
|
+
// Real OpenSCAD checks a builtin's arguments before running it and warns in
|
|
546
|
+
// two fixed shapes (Parameters.cc / builtin_functions.cc):
|
|
547
|
+
//
|
|
548
|
+
// NAME() number of parameters does not match: expected N, found M
|
|
549
|
+
// NAME() parameter could not be converted: WHERE: expected T, found T (v)
|
|
550
|
+
//
|
|
551
|
+
// and returns undef either way. We used to do neither -- the value came out
|
|
552
|
+
// undef, silently, so `ord(undef)` and `abs("a")` alike said nothing at all.
|
|
553
|
+
// Every spec below (arity text included, since it is free-form per builtin)
|
|
554
|
+
// was read off OpenSCAD 2026.02.01 directly rather than guessed.
|
|
555
|
+
enum : unsigned {
|
|
556
|
+
TUndef = 1u << 0,
|
|
557
|
+
TBool = 1u << 1,
|
|
558
|
+
TNum = 1u << 2,
|
|
559
|
+
TStr = 1u << 3,
|
|
560
|
+
TVec = 1u << 4,
|
|
561
|
+
TRange = 1u << 5,
|
|
562
|
+
TFunc = 1u << 6,
|
|
563
|
+
TObj = 1u << 7,
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
unsigned typeBit(const Value& v) {
|
|
567
|
+
if (std::holds_alternative<bool>(v)) return TBool;
|
|
568
|
+
if (std::holds_alternative<double>(v)) return TNum;
|
|
569
|
+
if (std::holds_alternative<std::string>(v)) return TStr;
|
|
570
|
+
if (std::holds_alternative<ListPtr>(v)) return TVec;
|
|
571
|
+
if (std::holds_alternative<OscRange>(v)) return TRange;
|
|
572
|
+
if (std::holds_alternative<ClosurePtr>(v)) return TFunc;
|
|
573
|
+
if (std::holds_alternative<ObjectPtr>(v)) return TObj;
|
|
574
|
+
return TUndef;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
struct ArgReq {
|
|
578
|
+
unsigned allowed;
|
|
579
|
+
const char* expected; // the word the reference prints, which is not
|
|
580
|
+
// always the full allowed set -- len() accepts a
|
|
581
|
+
// vector or an object but still says "string".
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
struct BuiltinCheck {
|
|
585
|
+
int minArgs; // -1 disables the arity check entirely
|
|
586
|
+
int maxArgs; // -1 = unbounded
|
|
587
|
+
const char* arityText; // free-form, e.g. "1", "3 or 4", "between 2 and 4"
|
|
588
|
+
std::vector<ArgReq> types;
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
const ArgReq kNum{TNum, "number"};
|
|
592
|
+
const ArgReq kVec{TVec, "vector"};
|
|
593
|
+
|
|
594
|
+
const std::unordered_map<int, BuiltinCheck>& builtinChecks() {
|
|
595
|
+
static const auto build = [] {
|
|
596
|
+
std::unordered_map<int, BuiltinCheck> m;
|
|
597
|
+
const auto add = [&m](BuiltinFnId id, BuiltinCheck c) { m.emplace(static_cast<int>(id), std::move(c)); };
|
|
598
|
+
for (BuiltinFnId id : {BuiltinFnId::Abs, BuiltinFnId::Sign, BuiltinFnId::Ceil, BuiltinFnId::Floor,
|
|
599
|
+
BuiltinFnId::Round, BuiltinFnId::Sqrt, BuiltinFnId::Ln, BuiltinFnId::Log,
|
|
600
|
+
BuiltinFnId::Exp, BuiltinFnId::Sin, BuiltinFnId::Cos, BuiltinFnId::Tan,
|
|
601
|
+
BuiltinFnId::Asin, BuiltinFnId::Acos, BuiltinFnId::Atan}) {
|
|
602
|
+
add(id, {1, 1, "1", {kNum}});
|
|
603
|
+
}
|
|
604
|
+
add(BuiltinFnId::Atan2, {2, 2, "2", {kNum, kNum}});
|
|
605
|
+
add(BuiltinFnId::Pow, {2, 2, "2", {kNum, kNum}});
|
|
606
|
+
add(BuiltinFnId::Cross, {2, 2, "2", {kVec, kVec}});
|
|
607
|
+
add(BuiltinFnId::Lookup, {2, 2, "2", {kNum, kVec}});
|
|
608
|
+
add(BuiltinFnId::Norm, {1, 1, "1", {kVec}});
|
|
609
|
+
// len() takes a vector or an object happily, and still calls the
|
|
610
|
+
// expected type "string" when handed anything else.
|
|
611
|
+
add(BuiltinFnId::Len, {1, 1, "1", {{TStr | TVec | TObj, "string"}}});
|
|
612
|
+
add(BuiltinFnId::Ord, {1, 1, "1", {{TStr, "string"}}});
|
|
613
|
+
add(BuiltinFnId::Rands, {3, 4, "3 or 4", {kNum, kNum, kNum, kNum}});
|
|
614
|
+
add(BuiltinFnId::Search, {2, 4, "between 2 and 4", {}});
|
|
615
|
+
// parent_module() with no argument defaults to 1 rather than
|
|
616
|
+
// warning, so only the too-many case is an arity error.
|
|
617
|
+
add(BuiltinFnId::ParentModule, {0, 1, "1", {kNum}});
|
|
618
|
+
add(BuiltinFnId::HasKey, {-1, -1, nullptr, {{TObj, "object"}, {TStr, "string"}}});
|
|
619
|
+
for (BuiltinFnId id : {BuiltinFnId::IsUndef, BuiltinFnId::IsNum, BuiltinFnId::IsBool,
|
|
620
|
+
BuiltinFnId::IsString, BuiltinFnId::IsList, BuiltinFnId::IsFunction,
|
|
621
|
+
BuiltinFnId::IsObject}) {
|
|
622
|
+
add(id, {1, 1, "1", {}});
|
|
623
|
+
}
|
|
624
|
+
return m;
|
|
625
|
+
};
|
|
626
|
+
static const std::unordered_map<int, BuiltinCheck> checks = build();
|
|
627
|
+
return checks;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
void warnArity(Evaluator& ev, const std::string& name, const char* expected, size_t found,
|
|
631
|
+
const oscad::Position* pos) {
|
|
632
|
+
ev.warn(name + "() number of parameters does not match: expected " + expected + ", found " +
|
|
633
|
+
std::to_string(found),
|
|
634
|
+
pos);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
void warnConversion(Evaluator& ev, const std::string& name, const std::string& where, const char* expected,
|
|
638
|
+
const Value& found, const oscad::Position* pos) {
|
|
639
|
+
ev.warn(name + "() parameter could not be converted: " + where + ": expected " + expected + ", found " +
|
|
640
|
+
oscTypeName(found) + " (" + fmtValue(found) + ")",
|
|
641
|
+
pos);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// max()/min() are their own shape: either one vector of numbers, or N bare
|
|
645
|
+
// numbers, with a distinct "at least 1 vector element" arity text for the
|
|
646
|
+
// empty-vector case.
|
|
647
|
+
bool checkMinMax(Evaluator& ev, const std::string& name, const CallArgs& args, const oscad::Position* pos) {
|
|
648
|
+
const std::vector<Value> positional = allPositional(args);
|
|
649
|
+
const size_t count = positional.size() + args.named.size();
|
|
650
|
+
if (count < 1) {
|
|
651
|
+
warnArity(ev, name, "at least 1", count, pos);
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
if (positional.size() == 1 && std::holds_alternative<ListPtr>(positional[0])) {
|
|
655
|
+
static const std::vector<Value> kEmptyItems;
|
|
656
|
+
const ListPtr& l = std::get<ListPtr>(positional[0]);
|
|
657
|
+
const std::vector<Value>& items = l ? l->items : kEmptyItems;
|
|
658
|
+
if (items.empty()) {
|
|
659
|
+
warnArity(ev, name, "at least 1 vector element", 0, pos);
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
for (size_t i = 0; i < items.size(); ++i) {
|
|
663
|
+
if (!std::holds_alternative<double>(items[i])) {
|
|
664
|
+
warnConversion(ev, name, "vector element " + std::to_string(i), "number", items[i], pos);
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
for (size_t i = 0; i < positional.size(); ++i) {
|
|
671
|
+
if (!std::holds_alternative<double>(positional[i])) {
|
|
672
|
+
warnConversion(ev, name, "argument " + std::to_string(i), "number", positional[i], pos);
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Returns false when a diagnostic was emitted and the call must answer undef.
|
|
680
|
+
bool checkBuiltinArgs(Evaluator& ev, const std::string& name, BuiltinFnId id, const CallArgs& args,
|
|
681
|
+
const oscad::Position* pos) {
|
|
682
|
+
if (id == BuiltinFnId::Max || id == BuiltinFnId::Min) return checkMinMax(ev, name, args, pos);
|
|
683
|
+
const auto& checks = builtinChecks();
|
|
684
|
+
const auto it = checks.find(static_cast<int>(id));
|
|
685
|
+
if (it == checks.end()) return true;
|
|
686
|
+
const BuiltinCheck& c = it->second;
|
|
687
|
+
|
|
688
|
+
const std::vector<Value> positional = allPositional(args);
|
|
689
|
+
const size_t count = positional.size() + args.named.size();
|
|
690
|
+
if (c.arityText && (static_cast<int>(count) < c.minArgs || (c.maxArgs >= 0 && static_cast<int>(count) > c.maxArgs))) {
|
|
691
|
+
warnArity(ev, name, c.arityText, count, pos);
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
// Positional only: a named argument's position in the reference's own
|
|
695
|
+
// flat argument list can't be reconstructed from our split
|
|
696
|
+
// positional/named form, and warning on the wrong index would be worse
|
|
697
|
+
// than staying quiet on a spelling nobody uses for these builtins.
|
|
698
|
+
for (size_t i = 0; i < c.types.size() && i < positional.size(); ++i) {
|
|
699
|
+
if (!(typeBit(positional[i]) & c.types[i].allowed)) {
|
|
700
|
+
warnConversion(ev, name, "argument " + std::to_string(i), c.types[i].expected, positional[i], pos);
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
|
|
509
708
|
} // namespace
|
|
510
709
|
|
|
511
710
|
Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs& args, const oscad::ASTNode& node) {
|
|
512
|
-
(void)node;
|
|
513
711
|
|
|
514
712
|
const auto& ids = builtinFnIds();
|
|
515
713
|
const auto idIt = ids.find(name);
|
|
@@ -529,17 +727,10 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
529
727
|
}
|
|
530
728
|
}
|
|
531
729
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
if (!std::holds_alternative<double>(positional[i])) return Value{};
|
|
537
|
-
}
|
|
538
|
-
} else if (numericOnlyNames().count(name)) {
|
|
539
|
-
for (const Value& v : allPositional(args)) {
|
|
540
|
-
if (isBoolOrListContainsBool(v)) return Value{};
|
|
541
|
-
}
|
|
542
|
-
}
|
|
730
|
+
// Arity and argument types, with the reference's own two diagnostics.
|
|
731
|
+
// This replaced a silent version of the same gate (scalarNumericArity /
|
|
732
|
+
// numericOnlyNames), which returned undef without ever saying why.
|
|
733
|
+
if (!checkBuiltinArgs(ev, name, idIt->second, args, &node.position())) return Value{};
|
|
543
734
|
|
|
544
735
|
switch (idIt->second) {
|
|
545
736
|
case BuiltinFnId::TextMetrics: return builtinTextmetrics(ev, args);
|
|
@@ -595,13 +786,39 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
595
786
|
case BuiltinFnId::Min: return builtinMinMax(args, false);
|
|
596
787
|
case BuiltinFnId::Pow: return builtinPow(toDoubleLenient(getArg(args, 0, "x", Value{})), toDoubleLenient(getArg(args, 1, "y", Value{})));
|
|
597
788
|
case BuiltinFnId::Norm: {
|
|
789
|
+
// The vector-ness of the argument is the table's job; a
|
|
790
|
+
// non-numeric ELEMENT gets this separate, terser message
|
|
791
|
+
// instead of the usual conversion one.
|
|
598
792
|
const auto v = allNumericList(getArg(args, 0, "v", Value{}));
|
|
599
|
-
if (!v)
|
|
793
|
+
if (!v) {
|
|
794
|
+
ev.warn("Incorrect arguments to norm()", &node.position());
|
|
795
|
+
return Value{};
|
|
796
|
+
}
|
|
600
797
|
double sum = 0;
|
|
601
798
|
for (double x : *v) sum += x * x;
|
|
602
799
|
return Value{std::sqrt(sum)};
|
|
603
800
|
}
|
|
604
|
-
case BuiltinFnId::Cross:
|
|
801
|
+
case BuiltinFnId::Cross: {
|
|
802
|
+
const Value a = getArg(args, 0, "a", Value{});
|
|
803
|
+
const Value b = getArg(args, 1, "b", Value{});
|
|
804
|
+
// Two distinct messages, and the size check runs first: a
|
|
805
|
+
// 4-element operand is a size complaint even when it also holds
|
|
806
|
+
// a string.
|
|
807
|
+
const auto sizeOf = [](const Value& v) {
|
|
808
|
+
const ListPtr* l = std::get_if<ListPtr>(&v);
|
|
809
|
+
return (l && *l) ? (*l)->items.size() : size_t{0};
|
|
810
|
+
};
|
|
811
|
+
const size_t na = sizeOf(a), nb = sizeOf(b);
|
|
812
|
+
if (na != nb || (na != 2 && na != 3)) {
|
|
813
|
+
ev.warn("Invalid vector size of parameter for cross()", &node.position());
|
|
814
|
+
return Value{};
|
|
815
|
+
}
|
|
816
|
+
if (!allNumericList(a) || !allNumericList(b)) {
|
|
817
|
+
ev.warn("Invalid value in parameter vector for cross()", &node.position());
|
|
818
|
+
return Value{};
|
|
819
|
+
}
|
|
820
|
+
return builtinCross(a, b);
|
|
821
|
+
}
|
|
605
822
|
case BuiltinFnId::Rands: {
|
|
606
823
|
ev.noteRandsCall();
|
|
607
824
|
return builtinRands(toDoubleLenient(getArg(args, 0, "min_value", Value{})), toDoubleLenient(getArg(args, 1, "max_value", Value{})),
|
|
@@ -637,19 +854,34 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
637
854
|
return Value{out};
|
|
638
855
|
}
|
|
639
856
|
case BuiltinFnId::Chr: {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if (
|
|
857
|
+
// Variadic, and every argument contributes: chr(65, 66) is "AB".
|
|
858
|
+
// A number only encodes when it is a codepoint g_unichar_validate
|
|
859
|
+
// would accept -- strictly positive, below 0x110000, and not a
|
|
860
|
+
// surrogate. Anything else contributes nothing at all. Without
|
|
861
|
+
// that range check chr(-1)/chr(1e9) emitted raw invalid UTF-8,
|
|
862
|
+
// which propagated out and broke the caller's own decoding.
|
|
863
|
+
const std::function<std::string(const Value&)> encode = [&](const Value& c) -> std::string {
|
|
864
|
+
if (const double* d = std::get_if<double>(&c)) {
|
|
865
|
+
if (!std::isfinite(*d) || *d <= 0) return {};
|
|
866
|
+
const auto cp = static_cast<std::uint32_t>(*d);
|
|
867
|
+
if (cp == 0 || cp > 0x10FFFFu || (cp >= 0xD800u && cp <= 0xDFFFu)) return {};
|
|
868
|
+
return utf8Encode(cp);
|
|
649
869
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
870
|
+
if (const ListPtr* l = std::get_if<ListPtr>(&c); l && *l) {
|
|
871
|
+
std::string out;
|
|
872
|
+
for (const Value& item : (*l)->items) out += encode(item);
|
|
873
|
+
return out;
|
|
874
|
+
}
|
|
875
|
+
if (const OscRange* r = std::get_if<OscRange>(&c)) {
|
|
876
|
+
std::string out;
|
|
877
|
+
for (const Value& item : expandIterable(Value{*r})) out += encode(item);
|
|
878
|
+
return out;
|
|
879
|
+
}
|
|
880
|
+
return {};
|
|
881
|
+
};
|
|
882
|
+
std::string out;
|
|
883
|
+
for (const Value& a : allPositional(args)) out += encode(a);
|
|
884
|
+
return Value{out};
|
|
653
885
|
}
|
|
654
886
|
case BuiltinFnId::Ord: {
|
|
655
887
|
const Value s = getArg(args, 0, "s", Value{});
|
|
@@ -677,15 +909,18 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
677
909
|
const Value x = getArg(args, 0, "x", Value{});
|
|
678
910
|
return Value{std::holds_alternative<ObjectPtr>(x) && std::get<ObjectPtr>(x) != nullptr};
|
|
679
911
|
}
|
|
680
|
-
case BuiltinFnId::Search: return builtinSearch(args);
|
|
912
|
+
case BuiltinFnId::Search: return builtinSearch(args, ev, &node.position());
|
|
681
913
|
case BuiltinFnId::Lookup: return builtinLookup(args);
|
|
682
914
|
case BuiltinFnId::HasKey: {
|
|
683
915
|
const Value objArg = getArg(args, 0, "object", Value{});
|
|
684
916
|
const ObjectPtr* obj = std::get_if<ObjectPtr>(&objArg);
|
|
685
917
|
if (!obj || !*obj) return Value{};
|
|
686
918
|
const Value keyArg = getArg(args, 1, "key", Value{});
|
|
919
|
+
// A non-string key is an argument-conversion failure in the
|
|
920
|
+
// reference (expected string), so it is undef -- not the "no,
|
|
921
|
+
// that key isn't present" false this used to answer.
|
|
687
922
|
const std::string* key = std::get_if<std::string>(&keyArg);
|
|
688
|
-
if (!key) return Value{
|
|
923
|
+
if (!key) return Value{};
|
|
689
924
|
for (const auto& [k, v] : (*obj)->items) {
|
|
690
925
|
if (k == *key) return Value{true};
|
|
691
926
|
}
|
|
@@ -714,8 +949,20 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
714
949
|
if (!v || (v->size() != 2 && v->size() != 3)) return Value{};
|
|
715
950
|
return Value{(*v)[0] * 10000.0 + (*v)[1] * 100.0 + (v->size() == 3 ? (*v)[2] : 0.0)};
|
|
716
951
|
}
|
|
717
|
-
case BuiltinFnId::ParentModule:
|
|
718
|
-
|
|
952
|
+
case BuiltinFnId::ParentModule: {
|
|
953
|
+
// Defaults to 1, not 0, when called with no argument at all --
|
|
954
|
+
// and an index past the end of the stack is a warning, not a
|
|
955
|
+
// silent undef.
|
|
956
|
+
const Value* given = args.findPositional(0);
|
|
957
|
+
const int index = given ? static_cast<int>(toDoubleLenient(*given)) : 1;
|
|
958
|
+
Value r = ev.parentModuleName(index);
|
|
959
|
+
if (std::holds_alternative<std::monostate>(r)) {
|
|
960
|
+
ev.warn("Parent module index (" + std::to_string(index) +
|
|
961
|
+
") greater than the number of modules on the stack",
|
|
962
|
+
&node.position());
|
|
963
|
+
}
|
|
964
|
+
return r;
|
|
965
|
+
}
|
|
719
966
|
}
|
|
720
967
|
return Value{}; // unreachable: every BuiltinFnId has a case above
|
|
721
968
|
}
|