openscad-cpp-evaluator 0.40.0__tar.gz → 0.41.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +10 -2
  3. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/expression.cpp +4 -0
  4. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +4 -2
  5. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +3 -1
  6. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +13 -0
  7. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/evaluator.hpp +6 -1
  8. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/value.hpp +1 -16
  9. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/pyproject.toml +1 -1
  10. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/control.cpp +0 -2
  11. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/function_builtins.cpp +1 -5
  12. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/bytecode_compiler.cpp +1 -1
  13. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/bytecode_vm.cpp +1 -5
  14. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/expr_eval.cpp +22 -4
  15. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/stmt_eval.cpp +0 -2
  16. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/user_calls.cpp +1 -3
  17. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/value.cpp +6 -18
  18. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_control_flow.cpp +41 -16
  19. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/.github/workflows/ci.yml +0 -0
  20. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/.github/workflows/wheels.yml +0 -0
  21. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/.gitignore +0 -0
  22. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/.gitmodules +0 -0
  23. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/CLAUDE.md +0 -0
  24. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/CMakeLists.txt +0 -0
  25. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/LICENSE +0 -0
  26. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/README.md +0 -0
  27. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/bindings/CMakeLists.txt +0 -0
  28. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/bindings/ast_to_py.cpp +0 -0
  29. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/bindings/module.cpp +0 -0
  30. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/cmake/embed_font.cmake +0 -0
  31. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/examples/CMakeLists.txt +0 -0
  32. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/examples/eval_perf_benchmark.cpp +0 -0
  33. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/examples/manifold_cache_reuse.cpp +0 -0
  34. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/examples/minimal_debugger.cpp +0 -0
  35. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  36. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/.gitignore +0 -0
  37. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  38. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  39. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/README.md +0 -0
  40. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  41. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  42. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  43. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  44. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  45. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  46. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  47. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  48. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  49. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  50. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  51. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  52. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  53. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  54. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  55. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  56. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  57. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  58. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  59. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  60. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  61. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  62. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  63. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  64. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  65. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  66. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  67. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  68. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  69. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  70. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  71. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  72. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  73. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  74. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  75. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  76. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  77. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  78. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  79. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  80. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  81. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  82. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  83. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  84. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  85. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  86. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  87. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  88. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  89. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  90. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  91. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  92. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  93. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  94. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  95. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  96. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  97. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  98. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  99. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  100. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  101. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  102. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  103. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  104. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  105. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  106. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  107. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  108. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  109. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  110. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  111. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  112. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  113. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  114. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  115. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  117. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  118. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  119. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  120. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  121. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  122. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  123. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  124. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  125. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  126. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  127. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  128. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  129. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/resources/fonts/LICENSE +0 -0
  130. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  131. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/CMakeLists.txt +0 -0
  132. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/booleans.cpp +0 -0
  133. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/builtins.hpp +0 -0
  134. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/call_args.cpp +0 -0
  135. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/color.cpp +0 -0
  136. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/dxf_dim.cpp +0 -0
  137. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/extrude.cpp +0 -0
  138. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/import.cpp +0 -0
  139. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/modifiers.cpp +0 -0
  140. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/primitives_2d.cpp +0 -0
  141. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/primitives_3d.cpp +0 -0
  142. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/registry.cpp +0 -0
  143. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/roof.cpp +0 -0
  144. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/segments.cpp +0 -0
  145. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/surface.cpp +0 -0
  146. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/text.cpp +0 -0
  147. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/topology.cpp +0 -0
  148. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/builtins/transforms.cpp +0 -0
  149. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/colored_body.cpp +0 -0
  150. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/csg_generate.cpp +0 -0
  151. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/csg_resolve.cpp +0 -0
  152. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/css_colors.cpp +0 -0
  153. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/debug/debug_repl.cpp +0 -0
  154. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/debug_profile.cpp +0 -0
  155. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/eval_context.cpp +0 -0
  156. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/eval_error.cpp +0 -0
  157. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/eval_use.cpp +0 -0
  158. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/export.cpp +0 -0
  159. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/import/dxf_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/import/mesh_import.cpp +0 -0
  161. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/import/surface_load.cpp +0 -0
  162. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/import/svg_import.cpp +0 -0
  163. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/manifold_cache.cpp +0 -0
  164. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/measure_geometry.cpp +0 -0
  165. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/mesh_check.cpp +0 -0
  166. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/text/font_match.cpp +0 -0
  167. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/text/freetype_font_provider.cpp +0 -0
  168. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/text/text_metrics.cpp +0 -0
  169. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/src/zip_stored.cpp +0 -0
  170. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/CMakeLists.txt +0 -0
  171. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_booleans.cpp +0 -0
  172. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_bytecode_compiler.cpp +0 -0
  173. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_cli.cpp +0 -0
  174. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_csg_tree.cpp +0 -0
  175. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_debug_hooks.cpp +0 -0
  176. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_dxf_svg_import.cpp +0 -0
  177. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_eval_context.cpp +0 -0
  178. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_export.cpp +0 -0
  179. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_expr_eval.cpp +0 -0
  180. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_extrude_roof.cpp +0 -0
  181. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_function_builtins.cpp +0 -0
  182. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_helpers.hpp +0 -0
  183. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_import_export.cpp +0 -0
  184. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_manifold_cache.cpp +0 -0
  185. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_mesh_check.cpp +0 -0
  186. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_multi_color_merge.cpp +0 -0
  187. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_primitives.cpp +0 -0
  188. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_profiling.cpp +0 -0
  189. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_python_bindings.py +0 -0
  190. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_render_expr.cpp +0 -0
  191. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_scope_trail.cpp +0 -0
  192. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_scoping.cpp +0 -0
  193. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_surface.cpp +0 -0
  194. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_tail_calls.cpp +0 -0
  195. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_text.cpp +0 -0
  196. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_transforms.cpp +0 -0
  197. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_unexpected_args.cpp +0 -0
  198. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_use.cpp +0 -0
  199. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_value.cpp +0 -0
  200. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_viewport_params.cpp +0 -0
  201. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tests/test_zip_stored.cpp +0 -0
  202. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tools/cli/CMakeLists.txt +0 -0
  203. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tools/cli/cli_lib.cpp +0 -0
  204. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tools/cli/cli_lib.hpp +0 -0
  205. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/tools/cli/main.cpp +0 -0
  206. {openscad_cpp_evaluator-0.40.0 → openscad_cpp_evaluator-0.41.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.40.0
3
+ Version: 0.41.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -80,14 +80,22 @@ public:
80
80
  class RangeLiteral : public Primary {
81
81
  public:
82
82
  RangeLiteral(Position position, std::unique_ptr<Expression> start, std::unique_ptr<Expression> end,
83
- std::unique_ptr<Expression> step)
83
+ std::unique_ptr<Expression> step, bool implicitStep = false)
84
84
  : Primary(NodeKind::RangeLiteral, std::move(position)), start(std::move(start)), end(std::move(end)),
85
- step(std::move(step)) {}
85
+ step(std::move(step)), implicitStep(implicitStep) {}
86
86
 
87
87
  std::unique_ptr<Expression> start;
88
88
  std::unique_ptr<Expression> end;
89
89
  std::unique_ptr<Expression> step;
90
90
 
91
+ // True when the source wrote the two-argument form `[a:b]` and the step
92
+ // node below is the literal 1.0 this parser synthesized for it. Consumers
93
+ // that only need the value can ignore this and read `step` as always
94
+ // present; it exists because "the author did not choose a step" is not
95
+ // recoverable from the synthesized node, and the evaluator's backwards-range
96
+ // warning fires only for that case (an explicit step is taken as deliberate).
97
+ bool implicitStep = false;
98
+
91
99
  std::string toString() const override;
92
100
  void buildScope(Scope& parentScope) override;
93
101
  };
@@ -22,6 +22,10 @@ std::string NumberLiteral::toString() const {
22
22
  }
23
23
 
24
24
  std::string RangeLiteral::toString() const {
25
+ // Print back the form that was written. Emitting the synthesized step for
26
+ // a two-argument range would round-trip `[5:0]` into `[5 : 1 : 0]`, which
27
+ // reads identically but suppresses the evaluator's backwards-range warning.
28
+ if (implicitStep) return "[" + start->toString() + " : " + end->toString() + "]";
25
29
  return "[" + start->toString() + " : " + step->toString() + " : " + end->toString() + "]";
26
30
  }
27
31
 
@@ -46,11 +46,13 @@ NodePtr makeUndefinedLiteral(ParserDriver& driver, const OscadLocation& loc) {
46
46
  }
47
47
 
48
48
  NodePtr makeRangeLiteral(ParserDriver& driver, const OscadLocation& loc, NodePtr start, NodePtr end, NodePtr step) {
49
- if (!step) {
49
+ const bool implicitStep = !step;
50
+ if (implicitStep) {
50
51
  step = std::make_unique<NumberLiteral>(driver.toPosition(loc), 1.0);
51
52
  }
52
53
  return std::make_unique<RangeLiteral>(driver.toPosition(loc), nodeCast<Expression>(std::move(start)),
53
- nodeCast<Expression>(std::move(end)), nodeCast<Expression>(std::move(step)));
54
+ nodeCast<Expression>(std::move(end)), nodeCast<Expression>(std::move(step)),
55
+ implicitStep);
54
56
  }
55
57
 
56
58
  NodePtr makePositionalArgument(ParserDriver& driver, const OscadLocation& loc, NodePtr expr) {
@@ -112,6 +112,7 @@ json toJsonImpl(const ASTNode& node, bool includePos) {
112
112
  j["start"] = valueToJson(n.start.get(), includePos);
113
113
  j["end"] = valueToJson(n.end.get(), includePos);
114
114
  j["step"] = valueToJson(n.step.get(), includePos);
115
+ if (n.implicitStep) j["implicitStep"] = true;
115
116
  break;
116
117
  }
117
118
  case NodeKind::ParameterDeclaration: {
@@ -410,7 +411,8 @@ const std::unordered_map<std::string, Builder>& registry() {
410
411
  {"RangeLiteral",
411
412
  [](const json& j, Position pos) -> std::unique_ptr<ASTNode> {
412
413
  return std::make_unique<RangeLiteral>(std::move(pos), childFromJson<Expression>(j, "start"),
413
- childFromJson<Expression>(j, "end"), childFromJson<Expression>(j, "step"));
414
+ childFromJson<Expression>(j, "end"), childFromJson<Expression>(j, "step"),
415
+ j.value("implicitStep", false));
414
416
  }},
415
417
  {"ParameterDeclaration",
416
418
  [](const json& j, Position pos) -> std::unique_ptr<ASTNode> {
@@ -515,3 +515,16 @@ TEST(PrettyPrint, AssignmentRhsWithLeadingCommentAndTrailingLineComment) {
515
515
  // no other translation unit -- including this test file -- can name them.
516
516
  // Left as a known, permanent coverage ceiling rather than weakening the
517
517
  // encapsulation just to reach 100%.
518
+
519
+ // The two-argument range keeps its shape through a print/reparse cycle.
520
+ // It would be easy to always print the synthesized step, and the result
521
+ // would still be a correct program -- but `[5:0]` and `[5:1:0]` mean
522
+ // different things to the evaluator's backwards-range warning, so
523
+ // reformatting a file must not quietly convert one into the other.
524
+ TEST(PrettyPrint, ImplicitRangeStepSurvivesRoundTrip) {
525
+ auto ast = parseAst("a = [5:0];\nb = [5:1:0];\n");
526
+ const std::string printed = toOpenscad(ast);
527
+ EXPECT_NE(printed.find("[5 : 0]"), std::string::npos) << printed;
528
+ EXPECT_NE(printed.find("[5 : 1 : 0]"), std::string::npos) << printed;
529
+ expectStablePrint("a = [5:0];\nb = [5:1:0];\n");
530
+ }
@@ -392,7 +392,12 @@ public:
392
392
  Value applyUnaryOp(oscad::NodeKind kind, const Value& v, const oscad::Position& pos);
393
393
  Value applyIndexAccess(const Value& obj, const Value& idx);
394
394
  Value applyMemberAccess(const Value& obj, const std::string& member);
395
- Value applyRange(const Value& startV, const Value& endV, const Value& stepV);
395
+ // `implicitStep` is the RangeLiteral's own flag: true when the source
396
+ // wrote `[a:b]` and the parser supplied the 1.0. It only affects whether
397
+ // the backwards-range warning fires; the resulting value is the same
398
+ // either way. `pos` locates that warning.
399
+ Value applyRange(const Value& startV, const Value& endV, const Value& stepV, bool implicitStep = false,
400
+ const oscad::Position* pos = nullptr);
396
401
 
397
402
  // Entry point for a call site resolved INSIDE compiled bytecode (the
398
403
  // CALL_FN opcode, bytecode_vm.cpp): `bound` is already fully evaluated
@@ -355,20 +355,6 @@ private:
355
355
  // it, keeping expandIterable() itself free of any Evaluator/echo coupling.
356
356
  using RangeTooManyFn = std::function<void(size_t count)>;
357
357
 
358
- // Called when a range's step points AWAY from its end -- begin > end with a
359
- // positive step, or begin < end with a negative step. Such a range is
360
- // naturally empty, and the reference warns rather than iterating zero times
361
- // in silence. `stepPositive` picks which of its two messages applies; use
362
- // rangeDirectionWarning() to build the text.
363
- //
364
- // Separate from RangeTooManyFn for the same reason that one exists: it
365
- // keeps expandIterable() free of any Evaluator/echo coupling.
366
- using RangeDirectionFn = std::function<void(bool stepPositive)>;
367
-
368
- // The reference's own wording, quoted verbatim, for a range whose step
369
- // points away from its end. Shared so the five call sites cannot drift.
370
- std::string rangeDirectionWarning(bool stepPositive);
371
-
372
358
  // Converts a for()/intersection_for() loop assignment's evaluated RHS into
373
359
  // the sequence of values to iterate: undef -> empty, range -> a LAZY
374
360
  // sequence (matching OscRange's own start/step/end iteration, half-open
@@ -394,8 +380,7 @@ std::string rangeDirectionWarning(bool stepPositive);
394
380
  // one with a live consumer (BelfrySCAD) that surfaced it. The count is
395
381
  // computed in closed form (not IterableValues::size()'s O(n) walk) so a
396
382
  // legitimate huge-but-under-the-limit range never pays an eager pass.
397
- IterableValues expandIterable(const Value& v, const RangeTooManyFn& onTooMany = nullptr,
398
- const RangeDirectionFn& onWrongDirection = nullptr);
383
+ IterableValues expandIterable(const Value& v, const RangeTooManyFn& onTooMany = nullptr);
399
384
 
400
385
  // `each <body>`'s own flatten-one-level rule, shared by the AST interpreter
401
386
  // (evalListLiteral/evalListElement's ListCompEach handling, expr_eval.cpp)
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.40.0"
7
+ version = "0.41.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -128,8 +128,6 @@ CSGParams resolveIntersectionFor(Evaluator& ev, const oscad::ModularIntersection
128
128
  const oscad::Position* pos = &assign->position();
129
129
  IterableValues iter = expandIterable(values, [&](size_t count) {
130
130
  ev.warn("Bad range parameter in for statement: too many elements (" + std::to_string(count) + ")", pos);
131
- }, [&](bool stepPositive) {
132
- ev.warn(rangeDirectionWarning(stepPositive), pos);
133
131
  });
134
132
  for (const Value& val : iter) {
135
133
  EvalContext childCtx = parentCtx.childCtx(nullptr, std::nullopt, ctx.childrenNodes, ctx.childrenCallerCtx);
@@ -952,11 +952,7 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
952
952
  }
953
953
  if (const OscRange* r = std::get_if<OscRange>(&c)) {
954
954
  std::string out;
955
- // The reference warns here too -- chr([70:1:65]) is
956
- // as much a typo as for(i=[70:1:65]) would be.
957
- const IterableValues seq = expandIterable(Value{*r}, nullptr, [&](bool stepPositive) {
958
- ev.warn(rangeDirectionWarning(stepPositive), &node.position());
959
- });
955
+ const IterableValues seq = expandIterable(Value{*r});
960
956
  for (const Value& item : seq) out += encode(item);
961
957
  return out;
962
958
  }
@@ -466,7 +466,7 @@ public:
466
466
  compileExpr(*n.start, out, scope);
467
467
  compileExpr(*n.end, out, scope);
468
468
  compileExpr(*n.step, out, scope);
469
- out.push_back({Op::Range, 0, 0, &n.position()});
469
+ out.push_back({Op::Range, n.implicitStep ? 1 : 0, 0, &n.position()});
470
470
  return;
471
471
  }
472
472
  case NodeKind::PrimaryIndex: {
@@ -634,7 +634,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
634
634
  f.stack.pop_back();
635
635
  Value start = std::move(f.stack.back());
636
636
  f.stack.pop_back();
637
- f.stack.push_back(ev.applyRange(start, end, step));
637
+ f.stack.push_back(ev.applyRange(start, end, step, ins.a != 0, ins.pos));
638
638
  ++f.pc;
639
639
  break;
640
640
  }
@@ -740,8 +740,6 @@ Value driveVm(Evaluator& ev, size_t floor) {
740
740
  il.values = expandIterable(v, [&](size_t count) {
741
741
  ev.warn("Bad range parameter in for statement: too many elements (" + std::to_string(count) + ")",
742
742
  ins.pos);
743
- }, [&](bool stepPositive) {
744
- ev.warn(rangeDirectionWarning(stepPositive), ins.pos);
745
743
  });
746
744
  il.index = 0;
747
745
  il.total = il.values.size();
@@ -1466,8 +1464,6 @@ Value driveVm(Evaluator& ev, size_t floor) {
1466
1464
  il.values = expandIterable(v, [&](size_t count) {
1467
1465
  ev.warn("Bad range parameter in for statement: too many elements (" + std::to_string(count) + ")",
1468
1466
  ins.pos);
1469
- }, [&](bool stepPositive) {
1470
- ev.warn(rangeDirectionWarning(stepPositive), ins.pos);
1471
1467
  });
1472
1468
  il.index = 0;
1473
1469
  il.total = il.values.size();
@@ -177,8 +177,6 @@ void Evaluator::evalListElement(const oscad::ASTNode& elem, EvalContext& ctx, st
177
177
  const oscad::Position* pos = &assign->position();
178
178
  IterableValues iter = expandIterable(values, [&](size_t count) {
179
179
  warn("Bad range parameter in for statement: too many elements (" + std::to_string(count) + ")", pos);
180
- }, [&](bool stepPositive) {
181
- warn(rangeDirectionWarning(stepPositive), pos);
182
180
  });
183
181
  for (const Value& val : iter) {
184
182
  EvalContext childCtx = parentCtx.letChildCtx();
@@ -294,16 +292,36 @@ Value Evaluator::evalListLiteral(const oscad::ListComprehension& node, EvalConte
294
292
  }
295
293
 
296
294
  Value Evaluator::evalRangeLiteral(const oscad::RangeLiteral& node, EvalContext& ctx) {
297
- return applyRange(evalExpr(*node.start, ctx), evalExpr(*node.end, ctx), evalExpr(*node.step, ctx));
295
+ return applyRange(evalExpr(*node.start, ctx), evalExpr(*node.end, ctx), evalExpr(*node.step, ctx),
296
+ node.implicitStep, &node.position());
298
297
  }
299
298
 
300
299
  // Shared with the bytecode VM's RANGE opcode -- see applyBinaryOp's own
301
300
  // comment on why these are factored out as plain Value x Value x Value ->
302
301
  // Value functions.
303
- Value Evaluator::applyRange(const Value& startV, const Value& endV, const Value& stepV) {
302
+ Value Evaluator::applyRange(const Value& startV, const Value& endV, const Value& stepV, bool implicitStep,
303
+ const oscad::Position* pos) {
304
304
  double start = std::holds_alternative<std::monostate>(startV) ? 0.0 : toDoubleLenient(startV);
305
305
  double end = std::holds_alternative<std::monostate>(endV) ? 0.0 : toDoubleLenient(endV);
306
306
  double step = std::holds_alternative<std::monostate>(stepV) ? 1.0 : toDoubleLenient(stepV);
307
+ // A range whose begin is already past its end iterates zero times, which
308
+ // is almost always a typo -- [5:0] where [5:-1:0] was meant. Reported
309
+ // here, at construction, because that is where the reference reports it:
310
+ // `r = [5:0];` warns even if nothing ever iterates r.
311
+ //
312
+ // Only an IMPLICIT step is checked. Writing the step out is taken as
313
+ // deliberate, so [5:1:0] and [0:-1:5] stay silent -- a deliberate
314
+ // divergence from the reference, which warns for those too. An implicit
315
+ // step is always exactly 1, so the reference's other wording ("begin is
316
+ // smaller than the end, but step is negative") cannot arise here and no
317
+ // longer exists in this port.
318
+ //
319
+ // The epsilon matches the one the iteration path used before this check
320
+ // moved here, so a range built from float arithmetic that lands a hair
321
+ // past its end is still treated as empty-but-fine rather than a typo.
322
+ if (implicitStep && (start - end) > 1e-10) {
323
+ warn("begin is greater than the end, but step is positive", pos);
324
+ }
307
325
  return Value{OscRange{start, step, end}};
308
326
  }
309
327
 
@@ -112,8 +112,6 @@ void Evaluator::evalFor(const oscad::ModularFor& node, EvalContext& ctx) {
112
112
  const oscad::Position* pos = &assign->position();
113
113
  IterableValues iter = expandIterable(values, [&](size_t count) {
114
114
  warn("Bad range parameter in for statement: too many elements (" + std::to_string(count) + ")", pos);
115
- }, [&](bool stepPositive) {
116
- warn(rangeDirectionWarning(stepPositive), pos);
117
115
  });
118
116
  for (const Value& val : iter) {
119
117
  EvalContext childCtx = parentCtx.childCtx(nullptr, std::nullopt, ctx.childrenNodes, ctx.childrenCallerCtx);
@@ -1006,9 +1006,7 @@ std::optional<Evaluator::ChildrenForward> Evaluator::prepareChildrenForward(cons
1006
1006
  if (std::holds_alternative<double>(idxArg)) {
1007
1007
  indexValues.push_back(idxArg);
1008
1008
  } else if (std::holds_alternative<ListPtr>(idxArg) || std::holds_alternative<OscRange>(idxArg)) {
1009
- const IterableValues iter = expandIterable(idxArg, nullptr, [&](bool stepPositive) {
1010
- warn(rangeDirectionWarning(stepPositive), currentWarnEntry());
1011
- });
1009
+ const IterableValues iter = expandIterable(idxArg);
1012
1010
  for (const Value& v : iter) indexValues.push_back(v);
1013
1011
  } else {
1014
1012
  warn("Bad parameter type (" + fmtValue(idxArg) +
@@ -490,30 +490,18 @@ std::optional<size_t> rangeElementCount(const OscRange& r) {
490
490
  return static_cast<size_t>(std::floor(n + 1e-10)) + 1;
491
491
  }
492
492
 
493
- std::string rangeDirectionWarning(bool stepPositive) {
494
- return stepPositive ? "begin is greater than the end, but step is positive"
495
- : "begin is smaller than the end, but step is negative";
496
- }
497
-
498
- IterableValues expandIterable(const Value& v, const RangeTooManyFn& onTooMany,
499
- const RangeDirectionFn& onWrongDirection) {
493
+ IterableValues expandIterable(const Value& v, const RangeTooManyFn& onTooMany) {
500
494
  if (std::holds_alternative<std::monostate>(v)) return IterableValues{};
501
495
  if (const OscRange* r = std::get_if<OscRange>(&v)) {
502
496
  if (const std::optional<size_t> count = rangeElementCount(*r); count && *count >= 1'000'000) {
503
497
  if (onTooMany) onTooMany(*count);
504
498
  return IterableValues{};
505
499
  }
506
- // A step pointing away from the end yields nothing. That is almost
507
- // always a typo -- [1:0] where [1:-1:0] was meant -- so the
508
- // reference says so rather than silently running zero times.
509
- //
510
- // A zero step is deliberately NOT reported here: it is a different
511
- // failure (the reference calls it "too many elements") and shares
512
- // rangeElementCount's nullopt with this case only by coincidence.
513
- if (onWrongDirection && r->step != 0.0) {
514
- const double n = (r->end - r->start) / r->step;
515
- if (n < -1e-10) onWrongDirection(r->step > 0.0);
516
- }
500
+ // A step pointing away from the end yields nothing, and that is
501
+ // almost always a typo -- but the warning for it belongs to the
502
+ // range's CONSTRUCTION, not its iteration: only the range literal
503
+ // knows whether the author chose the step or the parser supplied
504
+ // it. See Evaluator::applyRange.
517
505
  // Lazy -- IterableValues itself reproduces this exact
518
506
  // termination condition (a zero step is naturally empty: neither
519
507
  // `x <= end` nor `x >= end` branch ever fires for it) without
@@ -1355,18 +1355,34 @@ const char* kSmaller = "begin is smaller than the end, but step is negative";
1355
1355
 
1356
1356
  } // namespace
1357
1357
 
1358
- TEST(RangeDirection, PositiveStepPastTheEndWarns) {
1359
- EXPECT_TRUE(hasWarning(warningsFrom("for (i=[3:1:1]) echo(i);"), kGreater));
1360
- EXPECT_TRUE(hasWarning(warningsFrom("for (i=[1:0]) echo(i);"), kGreater)); // implicit step
1358
+ TEST(RangeDirection, ImplicitStepPastTheEndWarns) {
1359
+ EXPECT_TRUE(hasWarning(warningsFrom("for (i=[1:0]) echo(i);"), kGreater));
1360
+ EXPECT_TRUE(hasWarning(warningsFrom("for (i=[3:1]) echo(i);"), kGreater));
1361
1361
  }
1362
1362
 
1363
- TEST(RangeDirection, NegativeStepPastTheEndWarns) {
1364
- EXPECT_TRUE(hasWarning(warningsFrom("for (i=[0:-1:3]) echo(i);"), kSmaller));
1363
+ TEST(RangeDirection, AnExplicitStepIsTakenAsDeliberate) {
1364
+ // Divergence from the reference, which warns for both of these. Writing
1365
+ // the step out is a statement of intent; the warning exists for the
1366
+ // author who wrote [3:1] meaning [3:-1:1] and got an empty loop.
1367
+ for (const char* src : {"for (i=[3:1:1]) echo(i);", "for (i=[0:-1:3]) echo(i);"}) {
1368
+ EXPECT_TRUE(warningsFrom(src).empty()) << src;
1369
+ }
1370
+ }
1371
+
1372
+ TEST(RangeDirection, TheNegativeStepWordingIsUnreachable) {
1373
+ // An implicit step is always exactly 1, so no input can produce the
1374
+ // reference's second message. If this ever fires, the gate has been
1375
+ // widened back to explicit steps and this port needs that wording back.
1376
+ for (const char* src : {"for (i=[0:-1:3]) echo(i);", "for (i=[3:1]) echo(i);",
1377
+ "x = [5:0];", "echo(chr([70:65]));"}) {
1378
+ EXPECT_FALSE(hasWarning(warningsFrom(src), kSmaller)) << src;
1379
+ }
1365
1380
  }
1366
1381
 
1367
1382
  TEST(RangeDirection, AWellFormedRangeIsSilent) {
1368
1383
  for (const char* src : {"for (i=[1:1:3]) echo(i);", "for (i=[3:-1:1]) echo(i);",
1369
- "for (i=[3:3]) echo(i);", "for (i=[0:0.5:2]) echo(i);"}) {
1384
+ "for (i=[3:3]) echo(i);", "for (i=[0:0.5:2]) echo(i);",
1385
+ "for (i=[1:3]) echo(i);"}) {
1370
1386
  EXPECT_TRUE(warningsFrom(src).empty()) << src;
1371
1387
  }
1372
1388
  }
@@ -1380,24 +1396,33 @@ TEST(RangeDirection, AZeroStepIsNotReportedAsADirectionProblem) {
1380
1396
  EXPECT_FALSE(hasWarning(w, kSmaller));
1381
1397
  }
1382
1398
 
1383
- TEST(RangeDirection, EveryRangeIteratingConstructWarns) {
1384
- // The point of wiring this into expandIterable rather than one caller.
1399
+ TEST(RangeDirection, WarnsWhereTheRangeIsBuiltNotWhereItIsIterated) {
1400
+ // The reference reports this against the range literal, so a range that
1401
+ // is assigned and never iterated still warns -- matched here.
1402
+ EXPECT_TRUE(hasWarning(warningsFrom("r = [5:0];\ncube(1);"), kGreater));
1403
+ // And a range built once, iterated twice, warns once.
1404
+ const std::vector<std::string> w =
1405
+ warningsFrom("r = [5:0];\nfor (i=r) echo(i);\nfor (j=r) echo(j);");
1406
+ EXPECT_EQ(w.size(), 1u);
1407
+ }
1408
+
1409
+ TEST(RangeDirection, EveryRangeBuildingConstructWarns) {
1410
+ // Construction is one shared site, but each of these reaches it by its
1411
+ // own route -- the interpreter's literal, the VM's Range op, and the
1412
+ // argument-evaluation path into a builtin.
1385
1413
  {
1386
- const std::vector<std::string> w = warningsFrom("x = [for (i=[3:1:1]) i];");
1414
+ const std::vector<std::string> w = warningsFrom("x = [for (i=[3:1]) i];");
1387
1415
  std::string got;
1388
1416
  for (const std::string& m : w) got += "\n " + m;
1389
1417
  EXPECT_TRUE(hasWarning(w, kGreater)) << "list comprehension, got:" << got;
1390
1418
  }
1391
- EXPECT_TRUE(hasWarning(warningsFrom("intersection_for (i=[3:1:1]) cube(1);"), kGreater))
1419
+ EXPECT_TRUE(hasWarning(warningsFrom("intersection_for (i=[3:1]) cube(1);"), kGreater))
1392
1420
  << "intersection_for";
1393
- EXPECT_TRUE(hasWarning(warningsFrom("echo(chr([70:1:65]));"), kGreater))
1394
- << "chr";
1421
+ EXPECT_TRUE(hasWarning(warningsFrom("echo(chr([70:65]));"), kGreater)) << "chr";
1395
1422
  EXPECT_TRUE(hasWarning(warningsFrom(
1396
1423
  "module p() { children([1:0]); }\np() { cube(1); cube(2); }"), kGreater))
1397
1424
  << "children";
1425
+ EXPECT_TRUE(hasWarning(warningsFrom("function f() = [5:0];\nx = f();"), kGreater))
1426
+ << "returned from a function";
1398
1427
  }
1399
1428
 
1400
- TEST(RangeDirection, WarnsOncePerEvaluationNotPerAbsentIteration) {
1401
- const std::vector<std::string> w = warningsFrom("for (i=[3:1:1]) echo(i);");
1402
- EXPECT_EQ(w.size(), 1u);
1403
- }