openscad-cpp-evaluator 1.36.0__tar.gz → 1.37.1__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.
Files changed (229) hide show
  1. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/CLAUDE.md +8 -1
  2. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/grammar/lexer.l +21 -1
  4. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +27 -0
  5. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/call_args.hpp +12 -0
  6. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/pyproject.toml +1 -1
  7. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/call_args.cpp +19 -2
  8. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/extrude.cpp +4 -2
  9. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/registry.cpp +2 -2
  10. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/CMakeLists.txt +1 -0
  11. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_python_bindings.py +18 -0
  12. openscad_cpp_evaluator-1.37.1/tests/test_undef_args.cpp +100 -0
  13. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/uv.lock +1 -1
  14. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/.github/workflows/ci.yml +0 -0
  15. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/.github/workflows/wheels.yml +0 -0
  16. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/.gitignore +0 -0
  17. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/.gitmodules +0 -0
  18. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/LICENSE +0 -0
  20. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/README.md +0 -0
  21. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/bindings/CMakeLists.txt +0 -0
  22. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/bindings/ast_to_py.cpp +0 -0
  23. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/bindings/module.cpp +0 -0
  24. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/cmake/embed_font.cmake +0 -0
  25. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/cmake/patch_manifold.cmake +0 -0
  26. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/examples/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/examples/eval_perf_benchmark.cpp +0 -0
  28. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/examples/manifold_cache_reuse.cpp +0 -0
  29. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/examples/minimal_debugger.cpp +0 -0
  30. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  31. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/.gitignore +0 -0
  32. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  33. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  34. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/README.md +0 -0
  35. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  36. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  37. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  39. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  40. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  41. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  42. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  43. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  44. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  45. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  46. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  47. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  48. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  49. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  50. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  51. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  52. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  53. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
  54. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  55. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  56. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  57. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  58. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  59. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  60. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  61. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  62. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  63. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  64. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  65. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  66. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  67. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  68. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  69. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
  70. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  71. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  72. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  73. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  74. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  75. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  76. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  77. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  78. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  79. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  80. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  81. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  82. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  83. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  84. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  85. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  86. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  87. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  88. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  89. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  90. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  91. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  92. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  93. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  94. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  95. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  96. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  97. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  98. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  99. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  100. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  101. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  102. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  103. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  104. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  105. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  106. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  107. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  108. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  109. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  110. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  111. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  112. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  113. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  114. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  115. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
  116. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  117. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  118. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  119. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  120. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  121. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  122. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  123. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  124. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  125. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  126. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  127. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  128. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  129. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  130. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  131. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  132. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
  133. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  134. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
  135. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/resources/fonts/LICENSE +0 -0
  136. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  137. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  138. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  139. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  140. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/CMakeLists.txt +0 -0
  141. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/booleans.cpp +0 -0
  142. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/builtins.hpp +0 -0
  143. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/color.cpp +0 -0
  144. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/control.cpp +0 -0
  145. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/dxf_dim.cpp +0 -0
  146. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/function_builtins.cpp +0 -0
  147. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/import.cpp +0 -0
  148. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/modifiers.cpp +0 -0
  149. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/primitives_2d.cpp +0 -0
  150. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/primitives_3d.cpp +0 -0
  151. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/roof.cpp +0 -0
  152. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/segments.cpp +0 -0
  153. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/surface.cpp +0 -0
  154. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/text.cpp +0 -0
  155. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/topology.cpp +0 -0
  156. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/builtins/transforms.cpp +0 -0
  157. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/bytecode_compiler.cpp +0 -0
  158. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/bytecode_vm.cpp +0 -0
  159. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/colored_body.cpp +0 -0
  160. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/coverage.cpp +0 -0
  161. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/csg_generate.cpp +0 -0
  162. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/csg_resolve.cpp +0 -0
  163. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/css_colors.cpp +0 -0
  164. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/debug/debug_repl.cpp +0 -0
  165. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/debug_profile.cpp +0 -0
  166. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/eval_context.cpp +0 -0
  167. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/eval_error.cpp +0 -0
  168. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/eval_use.cpp +0 -0
  169. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/export.cpp +0 -0
  170. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/expr_eval.cpp +0 -0
  171. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/format_closure.cpp +0 -0
  172. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/import/dxf_import.cpp +0 -0
  173. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/import/mesh_import.cpp +0 -0
  174. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/import/scene_import.cpp +0 -0
  175. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/import/surface_load.cpp +0 -0
  176. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/import/svg_import.cpp +0 -0
  177. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/manifold_cache.cpp +0 -0
  178. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/measure_geometry.cpp +0 -0
  179. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/mesh_check.cpp +0 -0
  180. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/stmt_eval.cpp +0 -0
  181. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/text/font_match.cpp +0 -0
  182. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/text/freetype_font_provider.cpp +0 -0
  183. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/text/text_metrics.cpp +0 -0
  184. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/user_calls.cpp +0 -0
  185. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/utf8.cpp +0 -0
  186. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/value.cpp +0 -0
  187. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/xkcd_colors.cpp +0 -0
  188. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/xkcd_colors.hpp +0 -0
  189. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/src/zip_stored.cpp +0 -0
  190. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_booleans.cpp +0 -0
  191. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_bytecode_compiler.cpp +0 -0
  192. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_children_separate.cpp +0 -0
  193. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_cli.cpp +0 -0
  194. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_control_flow.cpp +0 -0
  195. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_coverage.cpp +0 -0
  196. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_csg_tree.cpp +0 -0
  197. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_debug_hooks.cpp +0 -0
  198. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_discretizer.cpp +0 -0
  199. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_dxf_svg_import.cpp +0 -0
  200. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_eval_context.cpp +0 -0
  201. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_export.cpp +0 -0
  202. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_expr_eval.cpp +0 -0
  203. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_extrude_roof.cpp +0 -0
  204. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_feature_detection.cpp +0 -0
  205. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_function_builtins.cpp +0 -0
  206. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_helpers.hpp +0 -0
  207. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_import_export.cpp +0 -0
  208. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_include_cache.cpp +0 -0
  209. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_manifold_cache.cpp +0 -0
  210. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_mesh_check.cpp +0 -0
  211. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_multi_color_merge.cpp +0 -0
  212. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_primitives.cpp +0 -0
  213. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_profiling.cpp +0 -0
  214. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_render_expr.cpp +0 -0
  215. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_scope_trail.cpp +0 -0
  216. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_scoping.cpp +0 -0
  217. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_surface.cpp +0 -0
  218. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_tail_calls.cpp +0 -0
  219. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_text.cpp +0 -0
  220. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_transforms.cpp +0 -0
  221. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_unexpected_args.cpp +0 -0
  222. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_use.cpp +0 -0
  223. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_value.cpp +0 -0
  224. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_viewport_params.cpp +0 -0
  225. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tests/test_zip_stored.cpp +0 -0
  226. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tools/cli/CMakeLists.txt +0 -0
  227. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tools/cli/cli_lib.cpp +0 -0
  228. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tools/cli/cli_lib.hpp +0 -0
  229. {openscad_cpp_evaluator-1.36.0 → openscad_cpp_evaluator-1.37.1}/tools/cli/main.cpp +0 -0
@@ -730,7 +730,14 @@ grep for `ponytail:`.
730
730
  parameter names live in `builtinParamNames` (`registry.cpp`): each list is the union of real
731
731
  OpenSCAD's own `Parameters::parse` declaration and any extra name this port reads via `getArg`,
732
732
  so the port never warns about an argument it goes on to honour — **add to it whenever a builtin
733
- gains a parameter**, or that parameter starts warning. Builtin *functions* deliberately have no
733
+ gains a parameter**, or that parameter starts warning. **An argument given as `undef` is not given**: `getArg`
734
+ returns the default for it, as every upstream builtin does (`if (v.isDefined())`). Wrappers
735
+ forward their own optional parameters as undef -- BOSL2's `rotate_extrude` override passes
736
+ `angle=angle`, and reading that as 0 degrees flattened every plain `rotate_extrude()` (#680).
737
+ Checked argument by argument against OpenSCAD 2026.02 (47 cases, `tests/test_undef_args.cpp`).
738
+ Aliases (`linear_extrude`'s `h`, `rotate_extrude`'s `a`) go through `getArgOrAlias`, which
739
+ warns `Specified both "height" and "h"` as upstream and lets the primary name win.
740
+ `linear_extrude`'s default height is 100, upstream's (it was 1). Builtin *functions* deliberately have no
734
741
  entries beyond `textmetrics`/`fontmetrics`: upstream reads their arguments positionally without
735
742
  `Parameters::parse`, so `sin(bogus=30)` warns about nothing there (verified against 2022.08.22)
736
743
  and warning here would be a divergence. Not ported: the reference's `argument X supplied more
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.36.0
3
+ Version: 1.37.1
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -72,11 +72,19 @@ const std::unordered_map<std::string, KeywordFactory>& keywordTable() {
72
72
  %x INCFILE
73
73
 
74
74
  IDENT \$?[_A-Za-z][A-Za-z0-9_]*
75
+ UTF8CH [\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf4][\x80-\xbf]{3}
75
76
  NUMLIT 0[xX][0-9A-Fa-f]+|[0-9]+([.][0-9]*)?([eE][+-]?[0-9]+)?|[.][0-9]+([eE][+-]?[0-9]+)?
76
77
 
77
78
  %%
78
79
 
79
80
  [ \t\r\n]+ { /* skip whitespace */ }
81
+ \xc2\xa0|\xef\xbb\xbf|\xa0 {
82
+ // Also whitespace, as in OpenSCAD's lexer.l: U+00A0 NO-BREAK SPACE
83
+ // (C2 A0), which Qt's editor silently turns into a space -- so a file
84
+ // holding one parsed in the editor and failed from the command line --
85
+ // U+FEFF (EF BB BF, a byte-order mark, anywhere in the file) and a bare
86
+ // A0, Latin-1's no-break space.
87
+ }
80
88
  "//"[^\n]* { /* skip line comment */ }
81
89
  "/*"([^*]|\*+[^*/])*\*+"/" { /* skip block comment */ }
82
90
 
@@ -162,9 +170,21 @@ NUMLIT 0[xX][0-9A-Fa-f]+|[0-9]+([.][0-9]*)?([eE][+-]?[0-9]+)?|[.][0-9]+([eE][+-
162
170
  ">" { return yy::parser::make_GTOP(driver.currentTokenLoc()); }
163
171
  "#" { return yy::parser::make_HASHOP(driver.currentTokenLoc()); }
164
172
 
165
- . {
173
+ {UTF8CH} {
174
+ // A whole character, so the message stays valid UTF-8: quoting just its
175
+ // first byte made a message no consumer could decode.
166
176
  driver.reportError(driver.currentTokenLoc(), std::string("unexpected character '") + yytext + "'");
167
177
  }
178
+ . {
179
+ // A byte that starts no valid UTF-8 character: named, not quoted.
180
+ std::string shown(yytext);
181
+ const auto byte = static_cast<unsigned char>(yytext[0]);
182
+ if (byte >= 0x80 || byte < 0x20) {
183
+ static const char hex[] = "0123456789ABCDEF";
184
+ shown = std::string("\\x") + hex[byte >> 4] + hex[byte & 0xF];
185
+ }
186
+ driver.reportError(driver.currentTokenLoc(), "unexpected character '" + shown + "'");
187
+ }
168
188
 
169
189
  <<EOF>> { return yy::parser::make_YYEOF(driver.currentTokenLoc()); }
170
190
 
@@ -473,3 +473,30 @@ TEST(LexicalStrings, BackslashNewlineStillAdvancesTheLineNumber) {
473
473
  EXPECT_EQ(ast[0]->position().line, 1);
474
474
  EXPECT_EQ(ast[1]->position().line, 3) << "the continuation consumed a line";
475
475
  }
476
+
477
+ // As in OpenSCAD's lexer: U+00A0, U+FEFF and a bare Latin-1 A0 are whitespace.
478
+ TEST(LexicalWhitespace, NoBreakSpaceByteOrderMarkAndLatin1A0) {
479
+ for (const char* ws : {"\xc2\xa0", "\xef\xbb\xbf", "\xa0"}) {
480
+ auto ast = parseSrc(std::string(ws) + "a" + ws + "=" + ws + "1;" + ws + "\nb = 2;");
481
+ ASSERT_EQ(ast.size(), 2u);
482
+ EXPECT_EQ(ast[1]->position().line, 2);
483
+ }
484
+ }
485
+
486
+ // The message quotes a whole character, and names a byte that starts none,
487
+ // so it is always valid UTF-8.
488
+ TEST(LexicalWhitespace, UnexpectedCharacterMessageIsValidUtf8) {
489
+ const std::pair<const char*, const char*> cases[] = {
490
+ {"a = 1 \xc3\xa9;", "unexpected character '\xc3\xa9'"},
491
+ {"a = 1 \xc2;", "unexpected character '\\xC2'"},
492
+ {"a = 1 \xff;", "unexpected character '\\xFF'"},
493
+ };
494
+ for (const auto& [src, want] : cases) {
495
+ try {
496
+ parseSrc(src);
497
+ FAIL() << "expected a parse error for " << src;
498
+ } catch (const ParseError& e) {
499
+ EXPECT_NE(std::string(e.what()).find(want), std::string::npos) << e.what();
500
+ }
501
+ }
502
+ }
@@ -69,8 +69,20 @@ ResolvedCallArgs resolveCallArgs(Evaluator& ev, const std::vector<std::unique_pt
69
69
  // wins). `pos = std::nullopt` for an argument with no positional slot at
70
70
  // all (e.g. sphere's `d`, only ever named -- a later phase's need, plumbed
71
71
  // through now since the signature shape matters more than early callers).
72
+ //
73
+ // An argument given as undef counts as not given, as in every upstream
74
+ // builtin (`if (v.isDefined()) ...`): rotate_extrude(angle=undef) is 360
75
+ // degrees, not 0. It matters for wrappers that forward their own optional
76
+ // parameters -- BOSL2's rotate_extrude override passes `angle=angle`, which
77
+ // flattened every plain rotate_extrude() into its profile (#680).
72
78
  Value getArg(const CallArgs& args, std::optional<int> pos, const std::string& name, Value defaultValue = Value{});
73
79
 
80
+ // getArg for a parameter with an alternative name (linear_extrude's
81
+ // height/h, rotate_extrude's angle/a): `name` wins when both are given,
82
+ // with upstream's warning.
83
+ Value getArgOrAlias(Evaluator& ev, const oscad::Position* where, const CallArgs& args, std::optional<int> pos,
84
+ const std::string& name, const std::string& alias, Value defaultValue);
85
+
74
86
  // Encodes a CallArgs as a single Value (positional -> a list indexed 0..N,
75
87
  // named -> an object) so a resolve function can carry a call's raw
76
88
  // arguments across into CSGParams (which only holds Value) for a generate
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.36.0"
7
+ version = "1.37.1"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -56,14 +56,31 @@ CallArgs resolveArgs(Evaluator& ev, const std::vector<std::unique_ptr<oscad::Arg
56
56
  return result;
57
57
  }
58
58
 
59
+ namespace {
60
+ bool isUndef(const Value& v) { return std::holds_alternative<std::monostate>(v); }
61
+ } // namespace
62
+
59
63
  Value getArg(const CallArgs& args, std::optional<int> pos, const std::string& name, Value defaultValue) {
60
- if (const Value* named = args.findNamed(name)) return *named;
64
+ if (const Value* named = args.findNamed(name); named && !isUndef(*named)) return *named;
61
65
  if (pos.has_value()) {
62
- if (const Value* positional = args.findPositional(*pos)) return *positional;
66
+ if (const Value* positional = args.findPositional(*pos); positional && !isUndef(*positional))
67
+ return *positional;
63
68
  }
64
69
  return defaultValue;
65
70
  }
66
71
 
72
+ Value getArgOrAlias(Evaluator& ev, const oscad::Position* where, const CallArgs& args, std::optional<int> pos,
73
+ const std::string& name, const std::string& alias, Value defaultValue) {
74
+ const Value primary = getArg(args, pos, name, Value{});
75
+ const Value other = getArg(args, std::nullopt, alias, Value{});
76
+ if (!isUndef(primary)) {
77
+ if (!isUndef(other))
78
+ ev.warn("Specified both \"" + name + "\" and \"" + alias + "\"", where);
79
+ return primary;
80
+ }
81
+ return isUndef(other) ? defaultValue : other;
82
+ }
83
+
67
84
  ResolvedCallArgs resolveCallArgs(Evaluator& ev, const std::vector<std::unique_ptr<oscad::Argument>>& arguments,
68
85
  EvalContext& ctx) {
69
86
  CallArgs args = resolveArgs(ev, arguments, ctx);
@@ -118,7 +118,9 @@ manifold::Manifold extrudeTwisted(const manifold::Polygons& polys, double height
118
118
  BuiltinWrapParams computeLinearExtrudeParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
119
119
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
120
120
 
121
- const double height = toDoubleLenient(getArg(args, 0, "height", Value{1.0}));
121
+ // 100 when not given, as upstream (the old 1 was a guess), and `h` as
122
+ // upstream's alias -- BOSL2's linear_extrude override forwards it.
123
+ const double height = toDoubleLenient(getArgOrAlias(ev, &node.position(), args, 0, "height", "h", Value{100.0}));
122
124
  const bool center = truthy(getArg(args, std::nullopt, "center", Value{false}));
123
125
  const double twist = toDoubleLenient(getArg(args, std::nullopt, "twist", Value{0.0}));
124
126
  // Upstream's validate_integral: any finite number counts as given, and is
@@ -239,7 +241,7 @@ std::vector<ColoredBody> generateLinearExtrude(Evaluator& ev, const CSGParams& p
239
241
  BuiltinWrapParams computeRotateExtrudeParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
240
242
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
241
243
 
242
- const double angle = toDoubleLenient(getArg(args, 0, "angle", Value{360.0}));
244
+ const double angle = toDoubleLenient(getArgOrAlias(ev, &node.position(), args, 0, "angle", "a", Value{360.0}));
243
245
 
244
246
  CSGParams params;
245
247
  params["angle"] = Value{angle};
@@ -125,8 +125,8 @@ const std::vector<std::string>* builtinParamNames(const std::string& name) {
125
125
  {"import",
126
126
  {"file", "layer", "convexity", "origin", "scale", "width", "height", "filename", "layername", "center", "dpi",
127
127
  "id", "class", "repair", "tolerance"}},
128
- {"linear_extrude", {"height", "v", "scale", "center", "twist", "slices", "segments", "convexity"}},
129
- {"rotate_extrude", {"angle", "start", "convexity"}},
128
+ {"linear_extrude", {"height", "h", "v", "scale", "center", "twist", "slices", "segments", "convexity"}},
129
+ {"rotate_extrude", {"angle", "a", "start", "convexity"}},
130
130
  {"projection", {"cut", "convexity"}},
131
131
  {"roof", {"method", "convexity"}},
132
132
  {"fill", {}},
@@ -31,6 +31,7 @@ add_executable(oscad_eval_tests
31
31
  test_use.cpp
32
32
  test_coverage.cpp
33
33
  test_extrude_roof.cpp
34
+ test_undef_args.cpp
34
35
  test_discretizer.cpp
35
36
  test_surface.cpp
36
37
  test_dxf_svg_import.cpp
@@ -1259,6 +1259,24 @@ def test_a_cancel_signal_stops_a_silent_script_from_another_thread(tmp_path):
1259
1259
  assert time.perf_counter() - t0 < 5
1260
1260
 
1261
1261
 
1262
+ def test_no_break_space_in_code_is_whitespace_and_errors_stay_decodable(tmp_path):
1263
+ """U+00A0 and U+FEFF in code are whitespace, as in OpenSCAD (BelfrySCAD#679),
1264
+ and a parse error naming a non-ASCII character reaches Python as a
1265
+ readable EvalError -- it was `nanobind::str(): conversion error`, since
1266
+ the message quoted the character's first byte alone."""
1267
+ import pytest
1268
+ from openscad_cpp_evaluator import Evaluator, EvalError
1269
+
1270
+ ok = tmp_path / "nbsp.scad"
1271
+ ok.write_bytes(b"\xef\xbb\xbfcube(1);\xc2\xa0\nsphere(\xc2\xa02);\n")
1272
+ Evaluator(echo_fn=lambda _m: None).evaluate(str(ok), {})
1273
+
1274
+ bad = tmp_path / "bad.scad"
1275
+ bad.write_bytes("cube(1) \u00e9;\n".encode("utf-8"))
1276
+ with pytest.raises(EvalError, match="unexpected character '\u00e9'"):
1277
+ Evaluator(echo_fn=lambda _m: None).evaluate(str(bad), {})
1278
+
1279
+
1262
1280
  def main():
1263
1281
  """`python tests/test_python_bindings.py` -- how cibuildwheel runs this
1264
1282
  file -- runs the whole file under pytest. It used to loop over the test_
@@ -0,0 +1,100 @@
1
+ // A builtin argument given as undef means "not given" (#680), and the
2
+ // height/h and angle/a aliases, as upstream OpenSCAD -- each checked
3
+ // against OpenSCAD 2026.02 before being written down here.
4
+ //
5
+ // Wrappers forward their own optional parameters: BOSL2's rotate_extrude
6
+ // override passes angle=angle, undef when the user gave none, and taking
7
+ // that undef as 0 degrees flattened every plain rotate_extrude() into its
8
+ // profile.
9
+
10
+ #include "test_helpers.hpp"
11
+
12
+ #include <gtest/gtest.h>
13
+
14
+ #include <string>
15
+ #include <vector>
16
+
17
+ using namespace oscadeval;
18
+ using namespace oscadeval::test;
19
+
20
+ namespace {
21
+
22
+ struct Shape {
23
+ double volume;
24
+ manifold::Box box;
25
+ };
26
+
27
+ Shape shapeOf(const std::string& code) {
28
+ Evaluated e = evalSrc(code);
29
+ EXPECT_EQ(e.bodies.size(), 1u) << code;
30
+ if (e.bodies.empty() || !e.bodies[0].body.has_value()) return {0.0, {}};
31
+ return {e.bodies[0].body->Volume(), e.bodies[0].body->BoundingBox()};
32
+ }
33
+
34
+ void expectSameShape(const std::string& a, const std::string& b) {
35
+ const Shape x = shapeOf(a), y = shapeOf(b);
36
+ EXPECT_GT(x.volume, 0.0) << a;
37
+ EXPECT_NEAR(x.volume, y.volume, 1e-6) << a << " vs " << b;
38
+ EXPECT_NEAR(x.box.max.x, y.box.max.x, 1e-6) << a;
39
+ EXPECT_NEAR(x.box.max.y, y.box.max.y, 1e-6) << a;
40
+ EXPECT_NEAR(x.box.max.z, y.box.max.z, 1e-6) << a;
41
+ EXPECT_NEAR(x.box.min.x, y.box.min.x, 1e-6) << a;
42
+ }
43
+
44
+ std::vector<std::string> warningsFor(const std::string& code) {
45
+ std::vector<std::string> out;
46
+ Evaluated e = evalSrc(code, [&](const std::string& msg) {
47
+ if (msg.rfind("WARNING: ", 0) == 0) out.push_back(msg);
48
+ });
49
+ (void)e;
50
+ return out;
51
+ }
52
+
53
+ } // namespace
54
+
55
+ // Every argument that took an explicit undef as a value rather than as absent.
56
+ TEST(UndefArgs, AnUndefArgumentIsTheDefault) {
57
+ expectSameShape("rotate_extrude(angle=undef) translate([10,0]) square(5);",
58
+ "rotate_extrude() translate([10,0]) square(5);");
59
+ expectSameShape("linear_extrude(height=undef) square(5);", "linear_extrude() square(5);");
60
+ expectSameShape("cylinder(h=undef, r=2);", "cylinder(r=2);");
61
+ expectSameShape("linear_extrude(1) square(size=undef);", "linear_extrude(1) square();");
62
+ expectSameShape("linear_extrude(1) text(\"A\", size=undef);", "linear_extrude(1) text(\"A\");");
63
+ expectSameShape("linear_extrude(1) text(\"AB\", spacing=undef);", "linear_extrude(1) text(\"AB\");");
64
+ // Positional undef too.
65
+ expectSameShape("rotate_extrude(undef) translate([10,0]) square(5);",
66
+ "rotate_extrude() translate([10,0]) square(5);");
67
+ }
68
+
69
+ TEST(UndefArgs, AWrapperForwardingItsOwnOptionalAngleStillRevolvesFully) {
70
+ expectSameShape("module re(angle) rotate_extrude(angle=angle) children();\n"
71
+ "re() translate([10,0]) square(5);",
72
+ "rotate_extrude() translate([10,0]) square(5);");
73
+ }
74
+
75
+ TEST(UndefArgs, LinearExtrudeDefaultsToAHeightOfOneHundred) {
76
+ EXPECT_NEAR(shapeOf("linear_extrude() square(5);").box.max.z, 100.0, 1e-9);
77
+ const Shape centred = shapeOf("linear_extrude(center=true) square(5);");
78
+ EXPECT_NEAR(centred.box.min.z, -50.0, 1e-9);
79
+ EXPECT_NEAR(centred.box.max.z, 50.0, 1e-9);
80
+ }
81
+
82
+ TEST(UndefArgs, HIsHeightAndAIsAngle) {
83
+ EXPECT_NEAR(shapeOf("linear_extrude(h=7) square(5);").box.max.z, 7.0, 1e-9);
84
+ expectSameShape("rotate_extrude(a=90) translate([10,0]) square(5);",
85
+ "rotate_extrude(angle=90) translate([10,0]) square(5);");
86
+ EXPECT_TRUE(warningsFor("linear_extrude(h=7) square(5);").empty());
87
+ EXPECT_TRUE(warningsFor("rotate_extrude(a=90) translate([10,0]) square(5);").empty());
88
+ }
89
+
90
+ TEST(UndefArgs, BothNamesGivenWarnsAndThePrimaryWins) {
91
+ EXPECT_NEAR(shapeOf("linear_extrude(height=3, h=7) square(5);").box.max.z, 3.0, 1e-9);
92
+ const auto w = warningsFor("linear_extrude(height=3, h=7) square(5);");
93
+ ASSERT_EQ(w.size(), 1u);
94
+ EXPECT_NE(w[0].find("Specified both \"height\" and \"h\""), std::string::npos) << w[0];
95
+ expectSameShape("rotate_extrude(angle=45, a=90) translate([10,0]) square(5);",
96
+ "rotate_extrude(angle=45) translate([10,0]) square(5);");
97
+ const auto wa = warningsFor("rotate_extrude(angle=45, a=90) translate([10,0]) square(5);");
98
+ ASSERT_EQ(wa.size(), 1u);
99
+ EXPECT_NE(wa[0].find("Specified both \"angle\" and \"a\""), std::string::npos) << wa[0];
100
+ }
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "1.36.0"
58
+ version = "1.37.1"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },