openscad-cpp-evaluator 1.49.1__tar.gz → 1.49.3__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 (235) hide show
  1. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/bytecode.hpp +4 -0
  3. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/evaluator.hpp +16 -0
  4. openscad_cpp_evaluator-1.49.3/include/openscad_cpp_evaluator/reassign.hpp +61 -0
  5. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/pyproject.toml +1 -1
  6. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/bytecode_compiler.cpp +5 -1
  7. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/csg_resolve.cpp +1 -0
  8. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/eval_use.cpp +59 -3
  9. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/stmt_eval.cpp +32 -1
  10. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/zip_stored.cpp +43 -10
  11. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_scoping.cpp +17 -0
  12. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_use.cpp +31 -0
  13. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_zip_stored.cpp +87 -0
  14. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/uv.lock +1 -1
  15. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/.github/workflows/ci.yml +0 -0
  16. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/.github/workflows/wheels.yml +0 -0
  17. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/.gitignore +0 -0
  18. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/.gitmodules +0 -0
  19. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/CLAUDE.md +0 -0
  20. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/CMakeLists.txt +0 -0
  21. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/LICENSE +0 -0
  22. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/README.md +0 -0
  23. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/bindings/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/bindings/ast_to_py.cpp +0 -0
  25. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/bindings/module.cpp +0 -0
  26. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/cmake/embed_font.cmake +0 -0
  27. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/cmake/patch_manifold.cmake +0 -0
  28. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/examples/CMakeLists.txt +0 -0
  29. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/examples/eval_perf_benchmark.cpp +0 -0
  30. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/examples/manifold_cache_reuse.cpp +0 -0
  31. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/examples/minimal_debugger.cpp +0 -0
  32. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  33. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/.gitignore +0 -0
  34. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  35. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  36. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/LICENSE +0 -0
  37. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/README.md +0 -0
  38. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  39. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  40. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  41. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  42. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  43. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  44. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  45. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  46. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  47. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  48. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  49. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  50. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  51. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  52. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  53. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  54. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  55. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  56. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/api.cpp +0 -0
  57. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  58. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  59. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  60. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  61. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  62. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  63. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  64. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  65. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  66. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  67. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  68. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  69. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  70. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  71. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  72. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  73. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/position.cpp +0 -0
  74. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  75. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  76. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  77. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  78. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  79. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  80. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  81. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  82. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  83. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  84. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  85. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  86. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  87. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  88. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  89. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  90. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  91. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  92. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  93. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  94. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  95. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  96. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  97. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  98. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  99. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  100. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  101. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  102. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  103. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  104. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  105. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  106. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  107. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  108. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  109. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  110. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  111. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  112. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  113. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  114. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  115. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  116. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  117. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  118. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  119. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  120. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/export.hpp +0 -0
  121. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  122. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  123. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  124. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  125. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  126. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  127. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  128. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  129. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  130. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  131. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  132. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  133. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  134. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  135. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  136. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  137. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/value.hpp +0 -0
  138. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  139. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/python/openscad_cpp_evaluator/__init__.py +0 -0
  140. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/resources/fonts/LICENSE +0 -0
  141. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  142. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  143. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  144. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  145. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/CMakeLists.txt +0 -0
  146. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/booleans.cpp +0 -0
  147. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/builtins.hpp +0 -0
  148. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/call_args.cpp +0 -0
  149. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/color.cpp +0 -0
  150. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/control.cpp +0 -0
  151. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/dxf_dim.cpp +0 -0
  152. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/extrude.cpp +0 -0
  153. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/function_builtins.cpp +0 -0
  154. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/import.cpp +0 -0
  155. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/modifiers.cpp +0 -0
  156. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/primitives_2d.cpp +0 -0
  157. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/primitives_3d.cpp +0 -0
  158. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/profile_time.cpp +0 -0
  159. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/registry.cpp +0 -0
  160. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/roof.cpp +0 -0
  161. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/segments.cpp +0 -0
  162. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/surface.cpp +0 -0
  163. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/text.cpp +0 -0
  164. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/topology.cpp +0 -0
  165. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/builtins/transforms.cpp +0 -0
  166. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/bytecode_vm.cpp +0 -0
  167. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/colored_body.cpp +0 -0
  168. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/coverage.cpp +0 -0
  169. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/csg_generate.cpp +0 -0
  170. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/css_colors.cpp +0 -0
  171. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/debug/debug_repl.cpp +0 -0
  172. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/debug_profile.cpp +0 -0
  173. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/eval_context.cpp +0 -0
  174. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/eval_error.cpp +0 -0
  175. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/export.cpp +0 -0
  176. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/expr_eval.cpp +0 -0
  177. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/format_closure.cpp +0 -0
  178. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/import/dxf_import.cpp +0 -0
  179. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/import/mesh_import.cpp +0 -0
  180. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/import/scene_import.cpp +0 -0
  181. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/import/surface_load.cpp +0 -0
  182. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/import/svg_import.cpp +0 -0
  183. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/manifold_cache.cpp +0 -0
  184. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/measure_geometry.cpp +0 -0
  185. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/mesh_check.cpp +0 -0
  186. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/text/font_match.cpp +0 -0
  187. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/text/freetype_font_provider.cpp +0 -0
  188. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/text/text_metrics.cpp +0 -0
  189. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/user_calls.cpp +0 -0
  190. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/utf8.cpp +0 -0
  191. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/value.cpp +0 -0
  192. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/xkcd_colors.cpp +0 -0
  193. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/src/xkcd_colors.hpp +0 -0
  194. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/CMakeLists.txt +0 -0
  195. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_booleans.cpp +0 -0
  196. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_bytecode_compiler.cpp +0 -0
  197. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_children_separate.cpp +0 -0
  198. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_cli.cpp +0 -0
  199. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_control_flow.cpp +0 -0
  200. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_coverage.cpp +0 -0
  201. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_csg_tree.cpp +0 -0
  202. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_debug_hooks.cpp +0 -0
  203. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_discretizer.cpp +0 -0
  204. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_dxf_svg_import.cpp +0 -0
  205. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_eval_context.cpp +0 -0
  206. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_export.cpp +0 -0
  207. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_expr_eval.cpp +0 -0
  208. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_extrude_roof.cpp +0 -0
  209. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_feature_detection.cpp +0 -0
  210. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_function_builtins.cpp +0 -0
  211. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_helpers.hpp +0 -0
  212. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_import_export.cpp +0 -0
  213. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_include_cache.cpp +0 -0
  214. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_manifold_cache.cpp +0 -0
  215. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_mesh_check.cpp +0 -0
  216. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_multi_color_merge.cpp +0 -0
  217. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_openscad_parity.cpp +0 -0
  218. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_primitives.cpp +0 -0
  219. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_profile_time.cpp +0 -0
  220. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_profiling.cpp +0 -0
  221. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_python_bindings.py +0 -0
  222. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_render_expr.cpp +0 -0
  223. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_scope_trail.cpp +0 -0
  224. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_surface.cpp +0 -0
  225. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_tail_calls.cpp +0 -0
  226. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_text.cpp +0 -0
  227. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_transforms.cpp +0 -0
  228. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_undef_args.cpp +0 -0
  229. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_unexpected_args.cpp +0 -0
  230. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_value.cpp +0 -0
  231. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tests/test_viewport_params.cpp +0 -0
  232. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tools/cli/CMakeLists.txt +0 -0
  233. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tools/cli/cli_lib.cpp +0 -0
  234. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/tools/cli/cli_lib.hpp +0 -0
  235. {openscad_cpp_evaluator-1.49.1 → openscad_cpp_evaluator-1.49.3}/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.49.1
3
+ Version: 1.49.3
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT AND OFL-1.1
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "openscad_cpp_evaluator/function_builtins.hpp"
4
+ #include "openscad_cpp_evaluator/reassign.hpp"
4
5
  #include "openscad_cpp_evaluator/value.hpp"
5
6
 
6
7
  #include "openscad_cpp_parser/ast/ast_node.hpp"
@@ -756,6 +757,9 @@ struct CompiledChunk {
756
757
  // with how many: the Evaluator warns for them once, when the chunk is
757
758
  // compiled (it has the warning channel; the compiler does not).
758
759
  std::vector<std::pair<const oscad::StringLiteral*, int>> undefinedEscapeLiterals;
760
+ // Reassignments merged in the statement lists compiled into this chunk
761
+ // (see mergeReassignments), warned about the same way.
762
+ std::vector<Reassignment> reassignments;
759
763
  struct Param {
760
764
  std::string name;
761
765
  int slot = 0; // unused when isDyn is true -- $-params are never slot-addressed
@@ -592,8 +592,11 @@ public:
592
592
  return false;
593
593
  }
594
594
  std::unordered_set<const oscad::StringLiteral*> undefinedEscapesWarned_;
595
+ // Warnings a chunk's compile found, given once it is built: undefined
596
+ // string escapes, and reassignments its statement lists merged.
595
597
  void warnUndefinedEscapes(const CompiledChunk& chunk) {
596
598
  for (const auto& [lit, n] : chunk.undefinedEscapeLiterals) warnUndefinedEscapes(*lit, n);
599
+ for (const Reassignment& r : chunk.reassignments) warnReassignment(r);
597
600
  }
598
601
  // Hands one already-formatted warning line to echoFn_, recording it in
599
602
  // warnCapture first when one is active. Every warning path goes through
@@ -1393,6 +1396,19 @@ private:
1393
1396
  };
1394
1397
  std::unordered_map<std::pair<const oscad::ASTNode*, size_t>, std::optional<CompiledChunk>, ChildrenListKeyHash>
1395
1398
  childrenListChunkCache_;
1399
+ // A statement list with its reassignments merged (see
1400
+ // mergedStatementList), or nullopt when it has none -- the common
1401
+ // case, which then costs one lookup. Same key, same clearing, same
1402
+ // dangling-pointer reasoning as childrenListChunkCache_ above.
1403
+ std::unordered_map<std::pair<const oscad::ASTNode*, size_t>, std::optional<std::vector<const oscad::ASTNode*>>,
1404
+ ChildrenListKeyHash>
1405
+ mergedListCache_;
1406
+ // "origin:line:name" of each reassignment already warned about, so a
1407
+ // cleared mergedListCache_ never warns twice. Strings, not node
1408
+ // pointers: these outlive the ASTs the cache is cleared for.
1409
+ std::unordered_set<std::string> warnedReassignments_;
1410
+ const std::vector<const oscad::ASTNode*>& mergedStatementList(const std::vector<const oscad::ASTNode*>& children);
1411
+ void warnReassignment(const Reassignment& r);
1396
1412
 
1397
1413
  // See stmtExprChunkCache_'s own doc comment, immediately above, for why
1398
1414
  // this exists. Not a reentrancy guard (resolveTreeImpl is never called
@@ -0,0 +1,61 @@
1
+ #pragma once
2
+
3
+ #include "openscad_cpp_parser/ast/expression.hpp"
4
+ #include "openscad_cpp_parser/position.hpp"
5
+
6
+ #include <optional>
7
+ #include <string>
8
+ #include <unordered_map>
9
+ #include <utility>
10
+ #include <vector>
11
+
12
+ namespace oscadeval {
13
+
14
+ // One name assigned again in the same statement list.
15
+ struct Reassignment {
16
+ std::string name;
17
+ const oscad::Position* first; // where the variable is evaluated
18
+ const oscad::Position* here; // the assignment whose value it takes
19
+ };
20
+
21
+ // OpenSCAD's handle_assignment (parser.y): a name assigned twice in one
22
+ // statement list is ONE assignment, made where it was FIRST assigned but
23
+ // with the LAST expression. `x = 1; y = x * 10; x = 2;` gives y = 20.
24
+ // Running them in order gave 10 -- and with include<> spliced into the top
25
+ // level, that broke the usual way of configuring a library (include it,
26
+ // then override its defaults) for every value the library derives from one
27
+ // (BelfrySCAD #809).
28
+ //
29
+ // Returns the merged list -- the last assignment's node in the first one's
30
+ // place, later slots dropped -- or nullopt when nothing repeats. ASTs are
31
+ // shared and never modified; only this list of pointers changes. Each merge
32
+ // is appended to `found` for the caller to warn about.
33
+ inline std::optional<std::vector<const oscad::ASTNode*>> mergeReassignments(
34
+ const std::vector<const oscad::ASTNode*>& stmts, std::vector<Reassignment>* found = nullptr) {
35
+ if (stmts.size() < 2) return std::nullopt;
36
+ std::vector<const oscad::ASTNode*> out;
37
+ out.reserve(stmts.size());
38
+ std::unordered_map<std::string, std::pair<size_t, const oscad::Position*>> seen;
39
+ bool merged = false;
40
+ for (const oscad::ASTNode* node : stmts) {
41
+ if (node->kind() != oscad::NodeKind::Assignment) {
42
+ out.push_back(node);
43
+ continue;
44
+ }
45
+ const auto& a = static_cast<const oscad::Assignment&>(*node);
46
+ const std::string& name = a.name->name;
47
+ auto it = seen.find(name);
48
+ if (it == seen.end()) {
49
+ seen.emplace(name, std::make_pair(out.size(), &a.position()));
50
+ out.push_back(node);
51
+ continue;
52
+ }
53
+ if (found) found->push_back({name, it->second.second, &a.position()});
54
+ out[it->second.first] = node;
55
+ merged = true;
56
+ }
57
+ if (!merged) return std::nullopt;
58
+ return out;
59
+ }
60
+
61
+ } // namespace oscadeval
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.49.1"
7
+ version = "1.49.3"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -1283,7 +1283,11 @@ public:
1283
1283
  // necessarily one this Compiler's own declaration owns) -- see
1284
1284
  // tryCompileChildrenList's own doc comment, below, for the entry point
1285
1285
  // that needs this shape directly.
1286
- void compileStatementList(const std::vector<const oscad::ASTNode*>& children, std::vector<Instruction>& out) {
1286
+ void compileStatementList(const std::vector<const oscad::ASTNode*>& childrenIn, std::vector<Instruction>& out) {
1287
+ // Reassignments merged first, as Evaluator::evalChildren does for the
1288
+ // interpreted path: a list compiled inline here never reaches it.
1289
+ const auto merged = mergeReassignments(childrenIn, &chunk_.reassignments);
1290
+ const std::vector<const oscad::ASTNode*>& children = merged ? *merged : childrenIn;
1287
1291
  std::vector<const oscad::ASTNode*> assignments;
1288
1292
  std::vector<const oscad::ASTNode*> others;
1289
1293
  for (const oscad::ASTNode* c : children) {
@@ -247,6 +247,7 @@ std::vector<std::unique_ptr<CSGNode>> Evaluator::resolveTreeImpl(const NodeList&
247
247
  stmtExprChunkCache_.clear();
248
248
  assignBlockChunkCache_.clear();
249
249
  childrenListChunkCache_.clear();
250
+ mergedListCache_.clear();
250
251
  ResolvePassGuard resolvePassGuard(inResolvePass_);
251
252
  evalChildren(nodes, ctx);
252
253
  std::vector<std::unique_ptr<CSGNode>> tree = std::move(treeStack_.back());
@@ -1,7 +1,12 @@
1
1
  #include "openscad_cpp_evaluator/eval_use.hpp"
2
2
 
3
+ #include "openscad_cpp_evaluator/eval_error.hpp"
4
+ #include "openscad_cpp_evaluator/reassign.hpp"
3
5
  #include "openscad_cpp_parser/ast/declarations.hpp"
6
+ #include "openscad_cpp_parser/ast/expression.hpp"
4
7
 
8
+ #include <filesystem>
9
+ #include <unordered_map>
5
10
  #include <utility>
6
11
 
7
12
  namespace oscadeval {
@@ -19,6 +24,55 @@ ResolvedUseScopes resolveUseScopes(const std::vector<std::unique_ptr<oscad::ASTN
19
24
 
20
25
  namespace {
21
26
 
27
+ namespace fs = std::filesystem;
28
+
29
+ std::string normalPath(const std::string& p) {
30
+ std::error_code ec;
31
+ fs::path abs = fs::absolute(fs::path(p), ec);
32
+ return (ec ? fs::path(p) : abs).lexically_normal().generic_string();
33
+ }
34
+
35
+ // Each file's top-level list, merged before any scope is built (see
36
+ // mergeReassignments, reassign.hpp), so the interpreter, the compiled VM
37
+ // and the scope tables all see one assignment per name. This is where an
38
+ // include<>'d library's defaults meet the main file's overrides.
39
+ //
40
+ // Warnings follow parser.y's rules exactly. The deliberate silence is the
41
+ // main file overriding an included file's variable: that is configuration,
42
+ // not a mistake. Two different included files overriding each other are
43
+ // silent too.
44
+ std::vector<const oscad::ASTNode*> mergeTopLevel(const std::vector<const oscad::ASTNode*>& nodes,
45
+ const std::string& mainFile,
46
+ const std::function<void(const std::string&)>& logFn) {
47
+ std::vector<Reassignment> found;
48
+ auto merged = mergeReassignments(nodes, &found);
49
+ if (!merged) return nodes;
50
+ const std::string main = normalPath(mainFile);
51
+ for (const Reassignment& r : found) {
52
+ const std::string prevFile = normalPath(r.first->origin);
53
+ const std::string currFile = normalPath(r.here->origin);
54
+ std::string where; // the file named in the message, if not the main one
55
+ bool warnIt = false;
56
+ if (prevFile == main && currFile == main) {
57
+ warnIt = true;
58
+ } else if (prevFile == currFile) {
59
+ warnIt = r.first->line != r.here->line; // equal: one file included twice
60
+ where = prevFile;
61
+ } else if (prevFile == main) {
62
+ warnIt = true; // the main file's value overwritten by an include
63
+ where = prevFile;
64
+ }
65
+ if (!warnIt || !logFn) continue;
66
+ std::string msg = r.name + " was assigned on line " + std::to_string(r.first->line);
67
+ if (!where.empty()) {
68
+ const fs::path rel = fs::path(where).lexically_relative(fs::path(main).parent_path());
69
+ msg += " of " + (rel.empty() ? where : rel.generic_string());
70
+ }
71
+ logFn(formatWarning(msg + " but was overwritten", r.here, {}));
72
+ }
73
+ return *merged;
74
+ }
75
+
22
76
  // The recursion. `table` is threaded through every level rather than each
23
77
  // level building its own: `use <file>` gives each used file its own root
24
78
  // Scope, but one evaluation reads all of them back, so every node's scope
@@ -26,11 +80,13 @@ namespace {
26
80
  // could not see anything a nested resolution recorded -- every node of a
27
81
  // used file read back as having no scope, and `use` silently resolved to
28
82
  // undef. BelfrySCAD's own suite caught that; this repo's did not.
29
- ResolvedUseScopes resolveUseScopesInto(const std::vector<const oscad::ASTNode*>& ownNodes,
83
+ ResolvedUseScopes resolveUseScopesInto(const std::vector<const oscad::ASTNode*>& ownNodesIn,
30
84
  const std::string& currentFile,
85
+ const std::string& mainFile,
31
86
  const std::function<void(const std::string&)>& logFn,
32
87
  oscad::ScopeTable& table) {
33
88
  ResolvedUseScopes result;
89
+ const std::vector<const oscad::ASTNode*> ownNodes = mergeTopLevel(ownNodesIn, mainFile, logFn);
34
90
 
35
91
  std::vector<const oscad::ASTNode*> injected;
36
92
  // Declarations pulled in from one used file, paired with that file's
@@ -70,7 +126,7 @@ ResolvedUseScopes resolveUseScopesInto(const std::vector<const oscad::ASTNode*>&
70
126
  std::vector<const oscad::ASTNode*> libBorrowed;
71
127
  libBorrowed.reserve(libAst.size());
72
128
  for (const auto& n : libAst) libBorrowed.push_back(n.get());
73
- ResolvedUseScopes nested = resolveUseScopesInto(libBorrowed, lib.resolvedPath, logFn, table);
129
+ ResolvedUseScopes nested = resolveUseScopesInto(libBorrowed, lib.resolvedPath, mainFile, logFn, table);
74
130
 
75
131
  std::vector<const oscad::ASTNode*> libInjected;
76
132
  for (const oscad::ASTNode* n : nested.ownNodesFiltered) {
@@ -137,7 +193,7 @@ ResolvedUseScopes resolveUseScopes(const std::vector<const oscad::ASTNode*>& own
137
193
  // One table for the whole resolution, handed to the root scope at the
138
194
  // end so it lives exactly as long as the scopes it points into.
139
195
  auto table = std::make_unique<oscad::ScopeTable>();
140
- ResolvedUseScopes result = resolveUseScopesInto(ownNodes, currentFile, logFn, *table);
196
+ ResolvedUseScopes result = resolveUseScopesInto(ownNodes, currentFile, currentFile, logFn, *table);
141
197
  if (result.rootScope) result.rootScope->adoptTable(std::move(table));
142
198
  return result;
143
199
  }
@@ -1,5 +1,6 @@
1
1
  #include "openscad_cpp_evaluator/evaluator.hpp"
2
2
  #include "openscad_cpp_evaluator/format_closure.hpp"
3
+ #include "openscad_cpp_evaluator/reassign.hpp"
3
4
 
4
5
  #include "openscad_cpp_evaluator/call_args.hpp"
5
6
 
@@ -325,7 +326,37 @@ void Evaluator::evalStatement(const oscad::ASTNode& node, EvalContext& ctx) {
325
326
  }
326
327
  }
327
328
 
328
- void Evaluator::evalChildren(const std::vector<const oscad::ASTNode*>& children, EvalContext& ctx) {
329
+ // A statement list inside a module body or a block, with its reassignments
330
+ // merged (mergeReassignments, reassign.hpp). The top level of each file is
331
+ // merged once before scopes are built (eval_use.cpp); these lists live in
332
+ // shared, immutable ASTs, so they are merged here, as they are run, and
333
+ // cached. The compiler merges the lists it compiles inline itself
334
+ // (compileStatementList), so both paths agree.
335
+ const std::vector<const oscad::ASTNode*>& Evaluator::mergedStatementList(
336
+ const std::vector<const oscad::ASTNode*>& children) {
337
+ if (children.size() < 2) return children;
338
+ const auto key = std::make_pair(children.front(), children.size());
339
+ auto it = mergedListCache_.find(key);
340
+ if (it == mergedListCache_.end()) {
341
+ std::vector<Reassignment> found;
342
+ it = mergedListCache_.emplace(key, mergeReassignments(children, &found)).first;
343
+ for (const Reassignment& r : found) warnReassignment(r);
344
+ }
345
+ return it->second ? *it->second : children;
346
+ }
347
+
348
+ void Evaluator::warnReassignment(const Reassignment& r) {
349
+ // A body is one file, so parser.y's rule reduces to: always warn (its
350
+ // same-line exemption is for one file included twice, which only the
351
+ // top level can see). Once per assignment however often its list is
352
+ // run or recompiled.
353
+ const std::string tag = r.here->origin + ":" + std::to_string(r.here->start_offset) + ":" + r.name;
354
+ if (!warnedReassignments_.insert(tag).second) return;
355
+ warn(r.name + " was assigned on line " + std::to_string(r.first->line) + " but was overwritten", r.here);
356
+ }
357
+
358
+ void Evaluator::evalChildren(const std::vector<const oscad::ASTNode*>& childrenIn, EvalContext& ctx) {
359
+ const std::vector<const oscad::ASTNode*>& children = mergedStatementList(childrenIn);
329
360
  // Whole-LIST compile first (see tryRunCompiledChildren's own doc
330
361
  // comment, evaluator.hpp) -- tries the FULL `children` as one chunk
331
362
  // before falling back to assignments-then-others below. This is what
@@ -52,6 +52,7 @@ uint16_t readU16(const uint8_t* p) { return static_cast<uint16_t>(p[0] | (p[1] <
52
52
  uint32_t readU32(const uint8_t* p) {
53
53
  return static_cast<uint32_t>(p[0] | (p[1] << 8) | (p[2] << 16) | (static_cast<uint32_t>(p[3]) << 24));
54
54
  }
55
+ uint64_t readU64(const uint8_t* p) { return readU32(p) | (static_cast<uint64_t>(readU32(p + 4)) << 32); }
55
56
 
56
57
  // A fixed, valid DOS date/time (2020-01-01 00:00:00) -- the exact instant
57
58
  // doesn't matter for a generated 3MF, just that it's a well-formed DOS
@@ -156,9 +157,9 @@ void writeZipEntries(const std::string& path, const std::vector<PlacedEntry>& en
156
157
  struct CentralDirEntry {
157
158
  std::string name;
158
159
  uint16_t method;
159
- uint32_t compressedSize;
160
- uint32_t uncompressedSize;
161
- uint32_t localHeaderOffset;
160
+ uint64_t compressedSize;
161
+ uint64_t uncompressedSize;
162
+ uint64_t localHeaderOffset;
162
163
  };
163
164
 
164
165
  std::vector<uint8_t> readWholeFile(const std::string& path) {
@@ -189,25 +190,57 @@ std::vector<CentralDirEntry> parseCentralDirectory(const std::vector<uint8_t>& f
189
190
  }
190
191
  if (eocd == std::string::npos) throw std::runtime_error("'" + path + "' is not a valid ZIP archive (no EOCD)");
191
192
 
192
- const uint16_t entryCount = readU16(&file[eocd + 10]);
193
- const uint32_t cdOffset = readU32(&file[eocd + 16]);
193
+ uint64_t entryCount = readU16(&file[eocd + 10]);
194
+ uint64_t cdOffset = readU32(&file[eocd + 16]);
195
+ // ZIP64: a field too big for the classic record is all ones, and the
196
+ // real value is in the ZIP64 end record the locator just before it
197
+ // points to. lib3mf -- behind OpenSCAD's and most slicers' 3MF export --
198
+ // writes one even for a tiny file, so without this nearly every 3MF
199
+ // read as "corrupt central directory offset" (BelfrySCAD #800).
200
+ if ((entryCount == 0xFFFFu || cdOffset == 0xFFFFFFFFu) && eocd >= 20 &&
201
+ readU32(&file[eocd - 20]) == 0x07064b50u) {
202
+ const uint64_t z64 = readU64(&file[eocd - 20 + 8]);
203
+ if (z64 + 56 > file.size() || readU32(&file[z64]) != 0x06064b50u) {
204
+ throw std::runtime_error("'" + path + "': corrupt ZIP64 end of central directory");
205
+ }
206
+ entryCount = readU64(&file[z64 + 32]);
207
+ cdOffset = readU64(&file[z64 + 48]);
208
+ }
194
209
  if (cdOffset >= file.size()) throw std::runtime_error("'" + path + "': corrupt central directory offset");
195
210
 
196
211
  std::vector<CentralDirEntry> entries;
197
212
  size_t p = cdOffset;
198
- for (uint16_t i = 0; i < entryCount; ++i) {
213
+ for (uint64_t i = 0; i < entryCount; ++i) {
199
214
  if (p + 46 > file.size() || readU32(&file[p]) != 0x02014b50u) {
200
215
  throw std::runtime_error("'" + path + "': corrupt central directory entry");
201
216
  }
202
217
  const uint16_t method = readU16(&file[p + 10]);
203
- const uint32_t compressedSize = readU32(&file[p + 20]);
204
- const uint32_t uncompressedSize = readU32(&file[p + 24]);
218
+ uint64_t compressedSize = readU32(&file[p + 20]);
219
+ uint64_t uncompressedSize = readU32(&file[p + 24]);
205
220
  const uint16_t nameLen = readU16(&file[p + 28]);
206
221
  const uint16_t extraLen = readU16(&file[p + 30]);
207
222
  const uint16_t commentLen = readU16(&file[p + 32]);
208
- const uint32_t localHeaderOffset = readU32(&file[p + 42]);
209
- if (p + 46 + nameLen > file.size()) throw std::runtime_error("'" + path + "': corrupt central directory entry name");
223
+ uint64_t localHeaderOffset = readU32(&file[p + 42]);
224
+ if (p + 46 + nameLen + extraLen > file.size()) throw std::runtime_error("'" + path + "': corrupt central directory entry name");
210
225
  std::string name(reinterpret_cast<const char*>(&file[p + 46]), nameLen);
226
+ // A ZIP64 extra field (id 1) holds, in this order, only the values
227
+ // whose 32-bit field above is all ones.
228
+ for (size_t x = p + 46 + nameLen, xEnd = x + extraLen; x + 4 <= xEnd;) {
229
+ const uint16_t id = readU16(&file[x]);
230
+ const uint16_t len = readU16(&file[x + 2]);
231
+ size_t q = x + 4;
232
+ const size_t qEnd = std::min<size_t>(q + len, xEnd);
233
+ if (id == 0x0001u) {
234
+ for (uint64_t* v : {&uncompressedSize, &compressedSize, &localHeaderOffset}) {
235
+ if (*v != 0xFFFFFFFFu) continue;
236
+ if (q + 8 > qEnd) throw std::runtime_error("'" + path + "': corrupt ZIP64 extra field");
237
+ *v = readU64(&file[q]);
238
+ q += 8;
239
+ }
240
+ break;
241
+ }
242
+ x = q + len;
243
+ }
211
244
  entries.push_back(CentralDirEntry{std::move(name), method, compressedSize, uncompressedSize, localHeaderOffset});
212
245
  p += 46 + nameLen + extraLen + commentLen;
213
246
  }
@@ -195,3 +195,20 @@ TEST(SpecialVariables, PreviewIsAlwaysFalse) {
195
195
  ASSERT_EQ(msgs.size(), 1u) << (msgs.empty() ? "" : msgs[0]);
196
196
  EXPECT_EQ(msgs[0], "ECHO: false, true, \"r\"");
197
197
  }
198
+
199
+ // OpenSCAD evaluates a reassigned variable once, at its FIRST assignment,
200
+ // with its LAST value (parser.y's handle_assignment) -- in a module body or
201
+ // a block as at the top level. Checked against OpenSCAD 2026.02.01.
202
+ TEST(Scoping, ReassignedVariableTakesItsLastValueAtItsFirstPosition) {
203
+ std::vector<std::string> out;
204
+ evaluateSrc("x = 1;\ny = x * 10;\nx = 2;\necho(y = y);\n"
205
+ "module m() {\n u = 1;\n v = u * 10;\n u = 2;\n echo(v = v);\n}\nm();\n"
206
+ "if (true) { a = 1; b = a + 1; a = 5; echo(b = b); }\n",
207
+ [&](const std::string& m) { out.push_back(m); });
208
+ EXPECT_TRUE(anyContains(out, "ECHO: y = 20"));
209
+ EXPECT_TRUE(anyContains(out, "ECHO: v = 20"));
210
+ EXPECT_TRUE(anyContains(out, "ECHO: b = 6"));
211
+ size_t warnings = 0;
212
+ for (const auto& m : out) warnings += m.find("but was overwritten") != std::string::npos;
213
+ EXPECT_EQ(warnings, 3u) << "one per reassignment, even though m() could run again";
214
+ }
@@ -173,3 +173,34 @@ TEST(UseStatement, MainFileGlobalReadFromFunctionIsStable) {
173
173
  EXPECT_EQ(echoed[2], "ECHO: undef");
174
174
  std::filesystem::remove(main);
175
175
  }
176
+
177
+ // BelfrySCAD #809: OpenSCAD evaluates a reassigned variable at its FIRST
178
+ // assignment with its LAST value, so a main file overriding an included
179
+ // library's default changes everything the library derives from it -- the
180
+ // standard way to configure a library. And it says nothing: overriding an
181
+ // included file's variable is configuration, not a mistake. Expected values
182
+ // and warnings checked against OpenSCAD 2026.02.01.
183
+ TEST(IncludeReassignment, MainFileOverrideReachesValuesTheLibraryDerives) {
184
+ auto lib = writeFile("reassign_lib.scad", "x = 1;\ny = x * 10;\no = object(k = 5);\np = o.k;\n");
185
+ auto main = writeFile("reassign_main.scad", "include <" + lib.string() + ">\n"
186
+ "x = 2;\no = object(k = 7);\necho(y = y, p = p);\n");
187
+ std::vector<std::string> out;
188
+ evalFile(main, [&](const std::string& m) { out.push_back(m); });
189
+ ASSERT_EQ(out.size(), 1u) << "no warnings: overriding an include is configuration";
190
+ EXPECT_EQ(out[0], "ECHO: y = 20, p = 7");
191
+ std::filesystem::remove(lib);
192
+ std::filesystem::remove(main);
193
+ }
194
+
195
+ TEST(IncludeReassignment, IncludeOverwritingTheMainFileWarnsNamingIt) {
196
+ auto lib = writeFile("reassign_lib2.scad", "a = 1;\n");
197
+ auto main = writeFile("reassign_main2.scad", "a = 2;\ninclude <" + lib.string() + ">\necho(a = a);\n");
198
+ std::vector<std::string> out;
199
+ evalFile(main, [&](const std::string& m) { out.push_back(m); });
200
+ ASSERT_EQ(out.size(), 2u);
201
+ EXPECT_EQ(out[0], "WARNING: a was assigned on line 1 of " + main.filename().string() +
202
+ " but was overwritten in file " + lib.string() + ", line 1");
203
+ EXPECT_EQ(out[1], "ECHO: a = 1");
204
+ std::filesystem::remove(lib);
205
+ std::filesystem::remove(main);
206
+ }
@@ -253,3 +253,90 @@ TEST(ZipStored, StoredZipWritesMethod0) {
253
253
  EXPECT_EQ(centralDirMethodBySuffix(path.string(), "3dmodel.model"), 0);
254
254
  std::filesystem::remove(path);
255
255
  }
256
+
257
+ TEST(ZipStored, ReadsZip64Archive) {
258
+ // The shape lib3mf writes (BelfrySCAD #800): every size and offset in the
259
+ // central directory and classic end record all ones, the real values in a
260
+ // ZIP64 extra field and a ZIP64 end record found through its locator.
261
+ const std::string name = "3D/3dmodel.model";
262
+ const std::string content = "zip64 payload";
263
+ std::vector<uint8_t> out;
264
+
265
+ putLE<uint32_t>(out, 0x04034b50u);
266
+ putLE<uint16_t>(out, 45);
267
+ putLE<uint16_t>(out, 0);
268
+ putLE<uint16_t>(out, 0);
269
+ putLE<uint16_t>(out, 0);
270
+ putLE<uint16_t>(out, 0);
271
+ putLE<uint32_t>(out, 0);
272
+ putLE<uint32_t>(out, static_cast<uint32_t>(content.size()));
273
+ putLE<uint32_t>(out, static_cast<uint32_t>(content.size()));
274
+ putLE<uint16_t>(out, static_cast<uint16_t>(name.size()));
275
+ putLE<uint16_t>(out, 0);
276
+ out.insert(out.end(), name.begin(), name.end());
277
+ out.insert(out.end(), content.begin(), content.end());
278
+
279
+ const uint64_t cdStart = out.size();
280
+ putLE<uint32_t>(out, 0x02014b50u);
281
+ putLE<uint16_t>(out, 45);
282
+ putLE<uint16_t>(out, 45);
283
+ putLE<uint16_t>(out, 0);
284
+ putLE<uint16_t>(out, 0);
285
+ putLE<uint16_t>(out, 0);
286
+ putLE<uint16_t>(out, 0);
287
+ putLE<uint32_t>(out, 0);
288
+ putLE<uint32_t>(out, 0xFFFFFFFFu);
289
+ putLE<uint32_t>(out, 0xFFFFFFFFu);
290
+ putLE<uint16_t>(out, static_cast<uint16_t>(name.size()));
291
+ putLE<uint16_t>(out, 4 + 24 + 4 + 4); // an unrelated field first, then ZIP64
292
+ putLE<uint16_t>(out, 0);
293
+ putLE<uint16_t>(out, 0);
294
+ putLE<uint16_t>(out, 0);
295
+ putLE<uint32_t>(out, 0);
296
+ putLE<uint32_t>(out, 0xFFFFFFFFu);
297
+ out.insert(out.end(), name.begin(), name.end());
298
+ putLE<uint16_t>(out, 0x5455);
299
+ putLE<uint16_t>(out, 4);
300
+ putLE<uint32_t>(out, 0);
301
+ putLE<uint16_t>(out, 0x0001);
302
+ putLE<uint16_t>(out, 24);
303
+ putLE<uint64_t>(out, content.size());
304
+ putLE<uint64_t>(out, content.size());
305
+ putLE<uint64_t>(out, 0);
306
+ const uint64_t cdSize = out.size() - cdStart;
307
+
308
+ const uint64_t z64 = out.size();
309
+ putLE<uint32_t>(out, 0x06064b50u);
310
+ putLE<uint64_t>(out, 44);
311
+ putLE<uint16_t>(out, 45);
312
+ putLE<uint16_t>(out, 45);
313
+ putLE<uint32_t>(out, 0);
314
+ putLE<uint32_t>(out, 0);
315
+ putLE<uint64_t>(out, 1);
316
+ putLE<uint64_t>(out, 1);
317
+ putLE<uint64_t>(out, cdSize);
318
+ putLE<uint64_t>(out, cdStart);
319
+
320
+ putLE<uint32_t>(out, 0x07064b50u);
321
+ putLE<uint32_t>(out, 0);
322
+ putLE<uint64_t>(out, z64);
323
+ putLE<uint32_t>(out, 1);
324
+
325
+ putLE<uint32_t>(out, 0x06054b50u);
326
+ putLE<uint16_t>(out, 0);
327
+ putLE<uint16_t>(out, 0);
328
+ putLE<uint16_t>(out, 0xFFFF);
329
+ putLE<uint16_t>(out, 0xFFFF);
330
+ putLE<uint32_t>(out, 0xFFFFFFFFu);
331
+ putLE<uint32_t>(out, 0xFFFFFFFFu);
332
+ putLE<uint16_t>(out, 0);
333
+
334
+ const auto path = tempPath("zip64.zip");
335
+ {
336
+ std::ofstream f(path, std::ios::binary);
337
+ f.write(reinterpret_cast<const char*>(out.data()), static_cast<std::streamsize>(out.size()));
338
+ }
339
+ const std::vector<uint8_t> read = readStoredZipEntry(path.string(), name);
340
+ EXPECT_EQ(std::string(read.begin(), read.end()), content);
341
+ std::filesystem::remove(path);
342
+ }
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "1.49.1"
58
+ version = "1.49.3"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },