openscad-cpp-evaluator 1.28.4__tar.gz → 1.28.6__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 (223) hide show
  1. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/PKG-INFO +1 -1
  2. openscad_cpp_evaluator-1.28.6/external/openscad_cpp_parser/src/.DS_Store +0 -0
  3. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/api.cpp +24 -9
  4. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/CMakeLists.txt +1 -0
  5. openscad_cpp_evaluator-1.28.6/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +56 -0
  6. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/pyproject.toml +1 -1
  7. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/primitives_3d.cpp +36 -6
  8. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_primitives.cpp +16 -0
  9. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_python_bindings.py +34 -0
  10. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/uv.lock +1 -1
  11. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/.github/workflows/ci.yml +0 -0
  12. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/.github/workflows/wheels.yml +0 -0
  13. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/.gitignore +0 -0
  14. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/.gitmodules +0 -0
  15. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/CLAUDE.md +0 -0
  16. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/LICENSE +0 -0
  18. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/README.md +0 -0
  19. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/bindings/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/bindings/ast_to_py.cpp +0 -0
  21. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/bindings/module.cpp +0 -0
  22. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/cmake/embed_font.cmake +0 -0
  23. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/examples/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/examples/eval_perf_benchmark.cpp +0 -0
  25. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/examples/manifold_cache_reuse.cpp +0 -0
  26. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/examples/minimal_debugger.cpp +0 -0
  27. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  28. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/.gitignore +0 -0
  29. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  30. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  31. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/README.md +0 -0
  32. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  33. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  34. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  35. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  36. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  37. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  38. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  39. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  40. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  41. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  42. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  43. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  44. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  45. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  46. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  47. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  48. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  49. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  50. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  51. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  52. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  53. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  54. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  55. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  56. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  57. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  58. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  59. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  60. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  61. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  62. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  63. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  64. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  65. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/position.cpp +0 -0
  66. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  67. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  68. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  69. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  70. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  71. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  72. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  73. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  74. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  75. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  76. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  77. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  78. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  79. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  80. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  81. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  82. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  83. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  84. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  85. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  86. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  87. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  88. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  89. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  90. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  91. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  92. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  93. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  94. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  95. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  96. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  97. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  98. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  99. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  100. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  101. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  102. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  103. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  104. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  105. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  106. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  107. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  108. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  109. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  110. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  111. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/export.hpp +0 -0
  112. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  113. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  114. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  115. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  117. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  118. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  119. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  120. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  121. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  122. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  123. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  124. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  125. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  126. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  127. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  128. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/value.hpp +0 -0
  129. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  130. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/python/openscad_cpp_evaluator/__init__.py +0 -0
  131. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/resources/fonts/LICENSE +0 -0
  132. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  133. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  134. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  135. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  136. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/CMakeLists.txt +0 -0
  137. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/booleans.cpp +0 -0
  138. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/builtins.hpp +0 -0
  139. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/call_args.cpp +0 -0
  140. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/color.cpp +0 -0
  141. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/control.cpp +0 -0
  142. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/dxf_dim.cpp +0 -0
  143. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/extrude.cpp +0 -0
  144. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/function_builtins.cpp +0 -0
  145. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/import.cpp +0 -0
  146. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/modifiers.cpp +0 -0
  147. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/primitives_2d.cpp +0 -0
  148. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/registry.cpp +0 -0
  149. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/roof.cpp +0 -0
  150. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/segments.cpp +0 -0
  151. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/surface.cpp +0 -0
  152. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/text.cpp +0 -0
  153. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/topology.cpp +0 -0
  154. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/builtins/transforms.cpp +0 -0
  155. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/bytecode_compiler.cpp +0 -0
  156. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/bytecode_vm.cpp +0 -0
  157. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/colored_body.cpp +0 -0
  158. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/coverage.cpp +0 -0
  159. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/csg_generate.cpp +0 -0
  160. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/csg_resolve.cpp +0 -0
  161. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/css_colors.cpp +0 -0
  162. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/debug/debug_repl.cpp +0 -0
  163. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/debug_profile.cpp +0 -0
  164. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/eval_context.cpp +0 -0
  165. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/eval_error.cpp +0 -0
  166. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/eval_use.cpp +0 -0
  167. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/export.cpp +0 -0
  168. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/expr_eval.cpp +0 -0
  169. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/format_closure.cpp +0 -0
  170. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/import/dxf_import.cpp +0 -0
  171. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/import/mesh_import.cpp +0 -0
  172. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/import/surface_load.cpp +0 -0
  173. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/import/svg_import.cpp +0 -0
  174. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/manifold_cache.cpp +0 -0
  175. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/measure_geometry.cpp +0 -0
  176. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/mesh_check.cpp +0 -0
  177. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/stmt_eval.cpp +0 -0
  178. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/text/font_match.cpp +0 -0
  179. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/text/freetype_font_provider.cpp +0 -0
  180. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/text/text_metrics.cpp +0 -0
  181. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/user_calls.cpp +0 -0
  182. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/utf8.cpp +0 -0
  183. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/value.cpp +0 -0
  184. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/src/zip_stored.cpp +0 -0
  185. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/CMakeLists.txt +0 -0
  186. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_booleans.cpp +0 -0
  187. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_bytecode_compiler.cpp +0 -0
  188. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_children_separate.cpp +0 -0
  189. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_cli.cpp +0 -0
  190. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_control_flow.cpp +0 -0
  191. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_coverage.cpp +0 -0
  192. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_csg_tree.cpp +0 -0
  193. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_debug_hooks.cpp +0 -0
  194. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_dxf_svg_import.cpp +0 -0
  195. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_eval_context.cpp +0 -0
  196. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_export.cpp +0 -0
  197. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_expr_eval.cpp +0 -0
  198. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_extrude_roof.cpp +0 -0
  199. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_feature_detection.cpp +0 -0
  200. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_function_builtins.cpp +0 -0
  201. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_helpers.hpp +0 -0
  202. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_import_export.cpp +0 -0
  203. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_include_cache.cpp +0 -0
  204. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_manifold_cache.cpp +0 -0
  205. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_mesh_check.cpp +0 -0
  206. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_multi_color_merge.cpp +0 -0
  207. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_profiling.cpp +0 -0
  208. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_render_expr.cpp +0 -0
  209. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_scope_trail.cpp +0 -0
  210. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_scoping.cpp +0 -0
  211. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_surface.cpp +0 -0
  212. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_tail_calls.cpp +0 -0
  213. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_text.cpp +0 -0
  214. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_transforms.cpp +0 -0
  215. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_unexpected_args.cpp +0 -0
  216. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_use.cpp +0 -0
  217. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_value.cpp +0 -0
  218. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_viewport_params.cpp +0 -0
  219. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tests/test_zip_stored.cpp +0 -0
  220. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tools/cli/CMakeLists.txt +0 -0
  221. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tools/cli/cli_lib.cpp +0 -0
  222. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tools/cli/cli_lib.hpp +0 -0
  223. {openscad_cpp_evaluator-1.28.4 → openscad_cpp_evaluator-1.28.6}/tools/cli/main.cpp +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.28.4
3
+ Version: 1.28.6
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -119,10 +119,25 @@ std::vector<std::unique_ptr<ASTNode>> parseAst(const std::string& code, const st
119
119
 
120
120
  ParserDriver driver(origin);
121
121
  driver.strictCommas = g_strictCommas;
122
- lexerBeginString(code);
123
- yy::parser parser(driver);
124
- int rc = parser.parse();
125
- lexerEnd();
122
+ int rc;
123
+ {
124
+ // The flex scanner is global state (grammar/lexer_api.hpp), so two
125
+ // threads parsing at once lexed each other's input and crashed -- in
126
+ // BelfrySCAD, a render worker parsing its file while the UI thread
127
+ // parsed the buffer for the Customizer: an intermittent 0xC0000005
128
+ // on Windows. One parse at a time; a parse is milliseconds, and the
129
+ // shared file cache means a library is parsed once, not per thread.
130
+ // ponytail: a global lock; a reentrant scanner (%option reentrant +
131
+ // a yyscan_t through the driver) if parsing ever needs to scale.
132
+ static std::mutex scannerMutex;
133
+ std::lock_guard<std::mutex> lock(scannerMutex);
134
+ struct EndLexer { // lexerEnd() even when parse() throws
135
+ ~EndLexer() { lexerEnd(); }
136
+ } endLexer;
137
+ lexerBeginString(code);
138
+ yy::parser parser(driver);
139
+ rc = parser.parse();
140
+ }
126
141
 
127
142
  if (rc != 0 || driver.hadError) {
128
143
  throw ParseError(formatSyntaxError(driver, code, origin, sourceMap));
@@ -409,11 +424,11 @@ FileAstPtr parseFileShared(const std::string& absPath, bool includeComments) {
409
424
  return it->second.ast;
410
425
  }
411
426
 
412
- // Parsed OUTSIDE the lock: parsing a library takes tens of
413
- // milliseconds, and holding a global lock across it would serialise
414
- // every thread. Two threads racing the same file both parse and one
415
- // result is dropped -- wasteful once, never wrong, and far cheaper than
416
- // the alternative.
427
+ // Parsed outside THIS lock, so a cache lookup never waits behind a
428
+ // parse. (parseAst serialises the scanner itself: this comment once
429
+ // called concurrent parses "never wrong", and they were -- the scanner
430
+ // is global state.) Two threads racing the same file each parse it in
431
+ // turn and one result is dropped: wasteful once, never wrong.
417
432
  auto parsed = std::make_shared<const FileAst>(parseSingleFile(absPath, includeComments));
418
433
  std::lock_guard<std::mutex> lock(g_astCacheMutex);
419
434
  CacheEntry& entry = g_astCache[key];
@@ -27,6 +27,7 @@ add_executable(oscad_tests
27
27
  test_node_str.cpp
28
28
  test_render_expression.cpp
29
29
  test_strict_commas.cpp
30
+ test_concurrent_parse.cpp
30
31
  )
31
32
  target_link_libraries(oscad_tests PRIVATE openscad_cpp_parser GTest::gtest_main)
32
33
 
@@ -0,0 +1,56 @@
1
+ // The flex scanner is non-reentrant, global state (grammar/lexer_api.hpp),
2
+ // so two threads parsing at once used to lex each other's input. In
3
+ // BelfrySCAD that was an intermittent 0xC0000005 on Windows: a render worker
4
+ // parsing its file while the UI thread parsed the buffer for the Customizer.
5
+ // parseAst now serialises the scanner. Every thread here parses its OWN
6
+ // source and checks it got its own tree back, so corruption fails the test
7
+ // even where it does not crash.
8
+ #include "openscad_cpp_parser/api.hpp"
9
+
10
+ #include <gtest/gtest.h>
11
+
12
+ #include <atomic>
13
+ #include <string>
14
+ #include <thread>
15
+ #include <vector>
16
+
17
+ using namespace oscad;
18
+
19
+ namespace {
20
+ std::string sourceFor(int thread, int round) {
21
+ // Different identifiers and lengths per thread, so a lexer reading
22
+ // another thread's buffer produces a different tree, not an equal one.
23
+ std::string name = "v" + std::to_string(thread) + "_" + std::to_string(round);
24
+ std::string src;
25
+ for (int i = 0; i < 20 + thread; ++i) {
26
+ src += name + "_" + std::to_string(i) + " = [" + std::to_string(i) + ", " +
27
+ std::to_string(thread) + ", \"s" + std::to_string(round) + "\"];\n";
28
+ }
29
+ return src;
30
+ }
31
+ } // namespace
32
+
33
+ TEST(ConcurrentParse, ThreadsParsingAtOnceEachGetTheirOwnTree) {
34
+ constexpr int kThreads = 8, kRounds = 150;
35
+ std::atomic<int> wrong{0};
36
+ std::vector<std::thread> threads;
37
+ for (int t = 0; t < kThreads; ++t) {
38
+ threads.emplace_back([t, &wrong] {
39
+ for (int r = 0; r < kRounds; ++r) {
40
+ const std::string src = sourceFor(t, r);
41
+ try {
42
+ auto ast = parseAst(src);
43
+ const auto* first = ast.empty() ? nullptr : dynamic_cast<const Assignment*>(ast[0].get());
44
+ const std::string want = "v" + std::to_string(t) + "_" + std::to_string(r) + "_0";
45
+ if (ast.size() != static_cast<size_t>(20 + t) || !first || first->name->name != want) {
46
+ ++wrong;
47
+ }
48
+ } catch (...) {
49
+ ++wrong; // a syntax error from lexing another thread's input
50
+ }
51
+ }
52
+ });
53
+ }
54
+ for (auto& th : threads) th.join();
55
+ EXPECT_EQ(wrong.load(), 0);
56
+ }
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.28.4"
7
+ version = "1.28.6"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -53,11 +53,18 @@ struct BoundaryEdges {
53
53
  // three bad edges in a few thousand triangles cannot be found by eye,
54
54
  // which is what happened in BelfrySCAD #521 / issue #187.
55
55
  std::optional<std::pair<uint32_t, uint32_t>> first;
56
+ // An edge both of whose faces run it the SAME way: the mesh is closed
57
+ // but a face is wound backwards. Named so the author knows which face
58
+ // to flip (BelfrySCAD #566, where "not closed -- 0 boundary edges"
59
+ // described a closed tetrahedron with two reversed faces).
60
+ size_t misoriented = 0;
61
+ std::optional<std::pair<uint32_t, uint32_t>> firstMisoriented;
56
62
  };
57
63
 
58
64
  template <typename M>
59
65
  BoundaryEdges findBoundaryEdges(const M& mesh) {
60
66
  std::map<std::pair<uint32_t, uint32_t>, int> uses;
67
+ std::map<std::pair<uint32_t, uint32_t>, int> forward; // uses running low -> high
61
68
  for (size_t i = 0; i + 2 < mesh.triVerts.size(); i += 3) {
62
69
  const uint32_t v[3] = {static_cast<uint32_t>(mesh.triVerts[i]),
63
70
  static_cast<uint32_t>(mesh.triVerts[i + 1]),
@@ -65,6 +72,7 @@ BoundaryEdges findBoundaryEdges(const M& mesh) {
65
72
  for (int e = 0; e < 3; ++e) {
66
73
  const uint32_t a = v[e], b = v[(e + 1) % 3];
67
74
  ++uses[{std::min(a, b), std::max(a, b)}];
75
+ if (a < b) ++forward[{a, b}];
68
76
  }
69
77
  }
70
78
  BoundaryEdges out;
@@ -75,7 +83,16 @@ BoundaryEdges findBoundaryEdges(const M& mesh) {
75
83
  // A cluster of open edges is nearly always one hole, so the first is
76
84
  // usually representative of the rest.
77
85
  for (const auto& [edge, count] : uses) {
78
- if (count % 2 == 0) continue;
86
+ if (count % 2 == 0) {
87
+ // Closed here; consistently wound only if the uses split evenly
88
+ // between the two directions.
89
+ const auto f = forward.find(edge);
90
+ if ((f == forward.end() ? 0 : f->second) * 2 != count) {
91
+ ++out.misoriented;
92
+ if (!out.firstMisoriented) out.firstMisoriented = edge;
93
+ }
94
+ continue;
95
+ }
79
96
  ++out.count;
80
97
  if (!out.first) out.first = edge;
81
98
  }
@@ -857,10 +874,22 @@ std::vector<ColoredBody> generatePolyhedron(Evaluator& ev, const CSGParams& para
857
874
  // Name one of them. Where the hole IS is the only part of this
858
875
  // message an author can act on; the count alone just says to go
859
876
  // looking, across the whole mesh.
860
- const std::string where =
861
- open.first ? ", first at " + describeEdge(mesh, *open.first) : std::string();
862
- ev.warn("polyhedron: mesh is not closed -- " + std::to_string(open.count) +
863
- " boundary edge(s)" + where +
877
+ std::string what;
878
+ if (open.count) {
879
+ what = "mesh is not closed -- " + std::to_string(open.count) + " boundary edge(s)" +
880
+ (open.first ? ", first at " + describeEdge(mesh, *open.first) : std::string());
881
+ } else if (open.misoriented) {
882
+ // Closed, but some faces wound backwards: "not closed -- 0
883
+ // boundary edge(s)" contradicted itself (BelfrySCAD #566).
884
+ what = "faces are not consistently wound -- " + std::to_string(open.misoriented) +
885
+ " edge(s) run the same way by both faces sharing them" +
886
+ (open.firstMisoriented ? ", first at " + describeEdge(mesh, *open.firstMisoriented)
887
+ : std::string()) +
888
+ "; reverse the point order of the faces on one side of it";
889
+ } else {
890
+ what = "mesh is not a valid solid (" + checkMesh(mesh).summary() + ")";
891
+ }
892
+ ev.warn("polyhedron: " + what +
864
893
  // "drawing it as a surface" was read by a reporter as
865
894
  // the HOLE being surfaced over -- i.e. as automatic
866
895
  // repair hiding their defect. Nothing is repaired: the
@@ -868,7 +897,8 @@ std::vector<ColoredBody> generatePolyhedron(Evaluator& ev, const CSGParams& para
868
897
  // object as the thing being drawn, and say what it is
869
898
  // NOT, since "surface" alone does not imply "not solid"
870
899
  // to someone who did not know it could be either.
871
- "; drawing the object as an open surface rather than a "
900
+ (open.count ? "; drawing the object as an open surface rather than a "
901
+ : "; drawing the object as a surface rather than a ") +
872
902
  "solid -- nothing is patched. hull() can still "
873
903
  "use its points, but it cannot take part in union/difference/intersection",
874
904
  &node.position());
@@ -442,6 +442,22 @@ TEST(SphereStyle, StyleRespectsFnAndRadius) {
442
442
  // cube()'s size as OpenSCAD 2026.02.01 reads it: undef, absent or explicit,
443
443
  // is the default of 1; anything but a number or three numbers warns and
444
444
  // falls back to 1. Explicit undef built a zero-size cube -- nothing at all.
445
+ // BelfrySCAD #566: a closed tetrahedron with two faces wound backwards was
446
+ // reported as "mesh is not closed -- 0 boundary edge(s)". It is closed; say
447
+ // what is actually wrong, name an edge, and still draw it.
448
+ TEST(Primitives, PolyhedronWithReversedFacesSaysSo) {
449
+ std::vector<std::string> log;
450
+ Evaluated e = evalSrc("polyhedron([[0,0,0], [10,0,0], [0,10,0], [0,0,10]],\n"
451
+ " [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]);",
452
+ [&](const std::string& m) { log.push_back(m); });
453
+ ASSERT_EQ(log.size(), 1u);
454
+ EXPECT_NE(log[0].find("faces are not consistently wound -- 4 edge(s)"), std::string::npos) << log[0];
455
+ EXPECT_NE(log[0].find("first at [0, 0, 0] - [10, 0, 0]"), std::string::npos) << log[0];
456
+ EXPECT_EQ(log[0].find("not closed"), std::string::npos) << log[0];
457
+ EXPECT_EQ(log[0].find("open surface"), std::string::npos) << log[0];
458
+ ASSERT_EQ(e.bodies.size(), 1u); // still drawn, as a display-only surface
459
+ }
460
+
445
461
  TEST(Primitives, CubeBadSizeIsAUnitCube) {
446
462
  const std::pair<const char*, const char*> cases[] = {
447
463
  {"undef", ""},
@@ -1121,3 +1121,37 @@ def test_a_viewer_can_ask_for_a_thin_2d_slab_and_export_keeps_1(tmp_path):
1121
1121
  nverts = int(lines[1].split()[0])
1122
1122
  zs = [float(l.split()[2]) for l in lines[2:2 + nverts]]
1123
1123
  assert abs(max(zs) - min(zs) - 1.0) < 1e-6, "export is the 1-unit slab"
1124
+
1125
+
1126
+ def test_parsing_from_two_threads_at_once_is_safe(tmp_path):
1127
+ """BelfrySCAD's render worker parses its file (`parse`) while the UI
1128
+ thread parses the buffer for the Customizer (`parse_ast_string`); the
1129
+ parser's flex scanner is global state, and the two used to collide --
1130
+ an intermittent 0xC0000005 on Windows. The parser now serialises it
1131
+ (openscad_cpp_parser #12). Run in a subprocess, since the failure mode
1132
+ is a crash, not an exception."""
1133
+ import subprocess
1134
+ code = r'''
1135
+ import sys, threading
1136
+ from openscad_cpp_evaluator import parse, parse_ast_string
1137
+ path = sys.argv[1]
1138
+ errors = []
1139
+ def by_path():
1140
+ try:
1141
+ for _ in range(300): parse(path)
1142
+ except Exception as e: errors.append(repr(e))
1143
+ def by_string():
1144
+ try:
1145
+ for i in range(300):
1146
+ ast = parse_ast_string("a%d = [1, 2, 3];\nb = a%d;\n" % (i, i))
1147
+ assert len(ast) == 2, ast
1148
+ except Exception as e: errors.append(repr(e))
1149
+ threads = [threading.Thread(target=f) for f in (by_path, by_string, by_path, by_string)]
1150
+ for t in threads: t.start()
1151
+ for t in threads: t.join()
1152
+ print("errors:", errors); sys.exit(1 if errors else 0)
1153
+ '''
1154
+ src = tmp_path / "m.scad"
1155
+ src.write_text("".join(f"x{i} = [{i}, {i} + 1, \"s{i}\"];\n" for i in range(200)))
1156
+ r = subprocess.run([sys.executable, "-c", code, str(src)], capture_output=True, text=True, timeout=300)
1157
+ assert r.returncode == 0, (r.returncode, r.stdout[-500:], r.stderr[-1500:])
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "1.28.4"
58
+ version = "1.28.6"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },