openscad-cpp-evaluator 0.9.2__tar.gz → 0.10.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 (194) hide show
  1. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bytecode.hpp +5 -1
  3. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/bytecode_compiler.cpp +11 -11
  5. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/bytecode_vm.cpp +54 -27
  6. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_bytecode_compiler.cpp +46 -23
  7. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_tail_calls.cpp +41 -0
  8. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/.github/workflows/ci.yml +0 -0
  9. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/.github/workflows/wheels.yml +0 -0
  10. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/.gitignore +0 -0
  11. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/.gitmodules +0 -0
  12. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/CLAUDE.md +0 -0
  13. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/CMakeLists.txt +0 -0
  14. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/LICENSE +0 -0
  15. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/README.md +0 -0
  16. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/bindings/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/bindings/module.cpp +0 -0
  18. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/cmake/embed_font.cmake +0 -0
  19. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/examples/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/examples/eval_perf_benchmark.cpp +0 -0
  21. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/examples/manifold_cache_reuse.cpp +0 -0
  22. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/examples/minimal_debugger.cpp +0 -0
  23. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  24. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/.gitignore +0 -0
  25. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  26. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/README.md +0 -0
  28. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  29. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  30. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  31. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  32. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  33. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  34. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  35. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  36. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  37. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  39. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  40. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  41. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  42. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  43. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  44. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  45. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  46. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  47. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  48. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  49. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  50. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  51. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  52. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  53. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  54. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  55. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  56. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  57. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  58. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  59. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  60. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  61. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  62. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  63. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  64. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  65. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  66. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  67. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  68. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  69. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  70. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  71. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  72. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  73. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  74. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  75. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  76. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  77. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  78. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  79. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  80. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  81. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  82. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  83. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  84. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  85. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  86. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  87. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  88. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  89. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  90. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  91. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  92. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  93. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  94. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  95. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  96. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  97. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  98. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  99. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  100. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  101. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  102. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  103. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  104. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  105. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  106. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  107. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  108. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  109. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  110. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  111. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  112. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  113. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  114. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  115. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  116. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  117. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  118. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  119. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  120. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/resources/fonts/LICENSE +0 -0
  121. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  122. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/CMakeLists.txt +0 -0
  123. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/booleans.cpp +0 -0
  124. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/builtins.hpp +0 -0
  125. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/call_args.cpp +0 -0
  126. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/color.cpp +0 -0
  127. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/control.cpp +0 -0
  128. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/extrude.cpp +0 -0
  129. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/function_builtins.cpp +0 -0
  130. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/import.cpp +0 -0
  131. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/modifiers.cpp +0 -0
  132. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/primitives_2d.cpp +0 -0
  133. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/primitives_3d.cpp +0 -0
  134. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/registry.cpp +0 -0
  135. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/roof.cpp +0 -0
  136. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/segments.cpp +0 -0
  137. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/surface.cpp +0 -0
  138. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/text.cpp +0 -0
  139. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/topology.cpp +0 -0
  140. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/builtins/transforms.cpp +0 -0
  141. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/colored_body.cpp +0 -0
  142. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/csg_generate.cpp +0 -0
  143. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/csg_resolve.cpp +0 -0
  144. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/css_colors.cpp +0 -0
  145. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/debug/debug_repl.cpp +0 -0
  146. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/debug_profile.cpp +0 -0
  147. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/eval_context.cpp +0 -0
  148. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/eval_error.cpp +0 -0
  149. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/eval_use.cpp +0 -0
  150. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/export.cpp +0 -0
  151. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/expr_eval.cpp +0 -0
  152. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/import/dxf_import.cpp +0 -0
  153. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/import/mesh_import.cpp +0 -0
  154. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/import/surface_load.cpp +0 -0
  155. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/import/svg_import.cpp +0 -0
  156. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/manifold_cache.cpp +0 -0
  157. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/stmt_eval.cpp +0 -0
  158. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/text/stb_font_provider.cpp +0 -0
  159. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/text/text_metrics.cpp +0 -0
  160. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/user_calls.cpp +0 -0
  161. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/value.cpp +0 -0
  162. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/src/zip_stored.cpp +0 -0
  163. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/CMakeLists.txt +0 -0
  164. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_booleans.cpp +0 -0
  165. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_cli.cpp +0 -0
  166. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_control_flow.cpp +0 -0
  167. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_csg_tree.cpp +0 -0
  168. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_debug_hooks.cpp +0 -0
  169. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_dxf_svg_import.cpp +0 -0
  170. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_eval_context.cpp +0 -0
  171. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_export.cpp +0 -0
  172. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_expr_eval.cpp +0 -0
  173. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_extrude_roof.cpp +0 -0
  174. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_function_builtins.cpp +0 -0
  175. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_helpers.hpp +0 -0
  176. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_import_export.cpp +0 -0
  177. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_manifold_cache.cpp +0 -0
  178. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_multi_color_merge.cpp +0 -0
  179. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_primitives.cpp +0 -0
  180. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_profiling.cpp +0 -0
  181. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_python_bindings.py +0 -0
  182. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_scope_trail.cpp +0 -0
  183. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_scoping.cpp +0 -0
  184. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_surface.cpp +0 -0
  185. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_text.cpp +0 -0
  186. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_transforms.cpp +0 -0
  187. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_use.cpp +0 -0
  188. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_value.cpp +0 -0
  189. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_viewport_params.cpp +0 -0
  190. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tests/test_zip_stored.cpp +0 -0
  191. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tools/cli/CMakeLists.txt +0 -0
  192. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tools/cli/cli_lib.cpp +0 -0
  193. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tools/cli/cli_lib.hpp +0 -0
  194. {openscad_cpp_evaluator-0.9.2 → openscad_cpp_evaluator-0.10.0}/tools/cli/main.cpp +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.9.2
3
+ Version: 0.10.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -241,7 +241,11 @@ struct Instruction {
241
241
  struct CompiledChunk {
242
242
  struct Param {
243
243
  std::string name;
244
- int slot = 0;
244
+ int slot = 0; // unused when isDyn is true -- $-params are never slot-addressed
245
+ // $-prefixed parameter: binds through ctx.dyn (dynamically scoped)
246
+ // instead of a slot -- see compileFunctionLike's own doc comment,
247
+ // bytecode_compiler.cpp, and bindCompiledArgs', bytecode_vm.cpp.
248
+ bool isDyn = false;
245
249
  };
246
250
 
247
251
  // A call site statically resolved at compile time (see
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.9.2"
7
+ version = "0.10.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -193,7 +193,7 @@ class Compiler;
193
193
  // (a non-empty `enclosing` chain, letting the literal's own free-variable
194
194
  // references resolve as upvalues into whatever lexically contains it).
195
195
  // Returns false (chunk left partially populated, discarded by the caller)
196
- // for a $-prefixed parameter or any NotCompilable thrown while compiling.
196
+ // for any NotCompilable thrown while compiling.
197
197
  bool compileFunctionLike(CompiledChunk& chunk, const oscad::Scope* staticScope, const oscad::ASTNode* selfDecl,
198
198
  std::vector<EnclosingLevel> enclosing,
199
199
  const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params,
@@ -1075,21 +1075,21 @@ bool compileFunctionLike(CompiledChunk& chunk, const oscad::Scope* staticScope,
1075
1075
  std::vector<EnclosingLevel> enclosing,
1076
1076
  const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params,
1077
1077
  const oscad::Expression& bodyExpr) {
1078
- for (const auto& p : params) {
1079
- // $-prefixed parameters bind through ctx.dyn (dynamically scoped,
1080
- // never slot-addressed) via the existing applyDefaults/bindArgs
1081
- // machinery -- out of scope for this design, see this file's
1082
- // header comment.
1083
- if (!p->name->name.empty() && p->name->name[0] == '$') return false;
1084
- }
1085
-
1086
1078
  chunk.selfDecl = selfDecl;
1087
1079
  Compiler compiler(chunk, staticScope, selfDecl, std::move(enclosing));
1088
1080
  CompileScope bodyScope;
1089
1081
  bodyScope.push();
1090
1082
  for (const auto& p : params) {
1091
- int slot = compiler.declareLocal(bodyScope, p->name->name);
1092
- chunk.params.push_back(CompiledChunk::Param{p->name->name, slot});
1083
+ // $-prefixed parameters bind through ctx.dyn (dynamically scoped)
1084
+ // rather than a slot -- never declared as a local, so a reference
1085
+ // to one inside the body already compiles to Op::LoadDyn via the
1086
+ // Identifier case's own $-check regardless of scope visibility.
1087
+ // Still occupies its declared POSITION among all parameters (see
1088
+ // bindCompiledArgs, bytecode_vm.cpp) for positional-argument
1089
+ // matching, exactly like the interpreter's own bindArgs.
1090
+ const bool isDyn = !p->name->name.empty() && p->name->name[0] == '$';
1091
+ const int slot = isDyn ? 0 : compiler.declareLocal(bodyScope, p->name->name);
1092
+ chunk.params.push_back(CompiledChunk::Param{p->name->name, slot, isDyn});
1093
1093
  }
1094
1094
  chunk.defaultCode.resize(params.size());
1095
1095
 
@@ -604,17 +604,18 @@ Value runChunk(Evaluator& ev, const CompiledChunk& chunk, const std::vector<Inst
604
604
  // Mirrors Evaluator::bindArgs' own positional/named matching exactly (same
605
605
  // "later write for a repeated name wins," same "evaluate every argument
606
606
  // expression even if its name doesn't match a declared parameter" side-
607
- // effect rule), but writes matched-plain-parameter values directly into
608
- // `slots` instead of a fresh unordered_map, and routes a matched-$-name
609
- // straight to `childCtx.dyn` (childCtx.dyn->set) since $-parameters never
610
- // reach compilation (see tryCompileFunction) -- so ANY $-named argument here
611
- // is necessarily an undeclared override, exactly like today's interpreter
612
- // path. An undeclared plain (non-$) name has no slot and nothing in this
613
- // chunk can ever reference it by name, so its already-evaluated value is
614
- // simply discarded -- ponytail: ctx.let_ isn't written for it either
615
- // (unlike the interpreter path), which changes nothing observable, since
616
- // nothing downstream of a compiled call ever reads ctx.let_ for a name a
617
- // compiled body didn't itself resolve to a slot.
607
+ // effect rule), but writes a matched plain-parameter value directly into
608
+ // `slots` instead of a fresh unordered_map, and routes a matched DECLARED
609
+ // $-parameter (chunk.params[i].isDyn) straight to `childCtx.dyn` instead of
610
+ // a slot -- see compileFunctionLike's own doc comment, bytecode_compiler.cpp.
611
+ // A $-named argument that doesn't match any declared parameter is still an
612
+ // undeclared override, exactly like today's interpreter path -- routed to
613
+ // `childCtx.dyn` too. An undeclared plain (non-$) name has no slot and
614
+ // nothing in this chunk can ever reference it by name, so its already-
615
+ // evaluated value is simply discarded -- ponytail: ctx.let_ isn't written
616
+ // for it either (unlike the interpreter path), which changes nothing
617
+ // observable, since nothing downstream of a compiled call ever reads
618
+ // ctx.let_ for a name a compiled body didn't itself resolve to a slot.
618
619
  void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
619
620
  const std::vector<std::unique_ptr<oscad::Argument>>& arguments, EvalContext& callerCtx,
620
621
  EvalContext& childCtx, std::vector<Value>& slots, std::vector<bool>& bound) {
@@ -628,7 +629,11 @@ void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
628
629
  bool matched = false;
629
630
  for (size_t i = 0; i < nparams; ++i) {
630
631
  if (chunk.params[i].name == name) {
631
- slots[static_cast<size_t>(chunk.params[i].slot)] = std::move(v);
632
+ if (chunk.params[i].isDyn) {
633
+ childCtx.dyn->set(name, std::move(v));
634
+ } else {
635
+ slots[static_cast<size_t>(chunk.params[i].slot)] = std::move(v);
636
+ }
632
637
  bound[i] = true;
633
638
  matched = true;
634
639
  break;
@@ -641,7 +646,12 @@ void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
641
646
  auto& a = static_cast<const oscad::PositionalArgument&>(*argPtr);
642
647
  Value v = ev.evalExpr(*a.expr, callerCtx);
643
648
  if (positionalIdx < nparams) {
644
- slots[static_cast<size_t>(chunk.params[positionalIdx].slot)] = std::move(v);
649
+ const CompiledChunk::Param& p = chunk.params[positionalIdx];
650
+ if (p.isDyn) {
651
+ childCtx.dyn->set(p.name, std::move(v));
652
+ } else {
653
+ slots[static_cast<size_t>(p.slot)] = std::move(v);
654
+ }
645
655
  bound[positionalIdx] = true;
646
656
  }
647
657
  ++positionalIdx;
@@ -649,6 +659,28 @@ void bindCompiledArgs(Evaluator& ev, const CompiledChunk& chunk,
649
659
  }
650
660
  }
651
661
 
662
+ // Shared by runCompiledFunction/runCompiledFunctionFromBound: applies each
663
+ // unbound parameter's default (or, for a $-parameter with none, an explicit
664
+ // undef -- mirrors Evaluator::applyDefaults' own "declaring a parameter
665
+ // always creates a fresh binding" rule, which for a slot-addressed plain
666
+ // parameter is already true for free via frame.slots' own zero-initialized
667
+ // Value{} fill, but for ctx.dyn needs the same explicit write applyDefaults
668
+ // makes, or a stale ancestor-level $-var would incorrectly show through).
669
+ void applyCompiledDefaults(Evaluator& ev, const CompiledChunk& chunk, VmFrame& frame, EvalContext& childCtx) {
670
+ for (size_t i = 0; i < chunk.params.size(); ++i) {
671
+ if (frame.bound[i]) continue;
672
+ const CompiledChunk::Param& p = chunk.params[i];
673
+ if (p.isDyn) {
674
+ childCtx.dyn->set(p.name, chunk.defaultCode[i].empty()
675
+ ? Value{}
676
+ : runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack));
677
+ } else if (!chunk.defaultCode[i].empty()) {
678
+ frame.slots[static_cast<size_t>(p.slot)] =
679
+ runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack);
680
+ }
681
+ }
682
+ }
683
+
652
684
  } // namespace
653
685
 
654
686
  Value runCompiledFunction(Evaluator& ev, const CompiledChunk& chunk,
@@ -667,12 +699,7 @@ Value runCompiledFunction(Evaluator& ev, const CompiledChunk& chunk,
667
699
  // started running, never during argument binding itself.
668
700
  ev.setCurrentCallVmFrame(&frame);
669
701
  bindCompiledArgs(ev, chunk, arguments, callerCtx, childCtx, frame.slots, frame.bound);
670
- for (size_t i = 0; i < chunk.params.size(); ++i) {
671
- if (!frame.bound[i] && !chunk.defaultCode[i].empty()) {
672
- frame.slots[static_cast<size_t>(chunk.params[i].slot)] =
673
- runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack);
674
- }
675
- }
702
+ applyCompiledDefaults(ev, chunk, frame, childCtx);
676
703
  return runChunk(ev, chunk, chunk.bodyCode, frame.slots, childCtx, frame.stack, tailOut);
677
704
  }
678
705
 
@@ -684,8 +711,13 @@ Value runCompiledFunctionFromBound(Evaluator& ev, const CompiledChunk& chunk, co
684
711
  frame.bound.assign(chunk.params.size(), false);
685
712
  ev.setCurrentCallVmFrame(&frame);
686
713
  for (size_t i = 0; i < chunk.params.size(); ++i) {
687
- if (const Value* v = bound.find(chunk.params[i].name)) {
688
- frame.slots[static_cast<size_t>(chunk.params[i].slot)] = *v;
714
+ const CompiledChunk::Param& p = chunk.params[i];
715
+ if (const Value* v = bound.find(p.name)) {
716
+ if (p.isDyn) {
717
+ childCtx.dyn->set(p.name, *v);
718
+ } else {
719
+ frame.slots[static_cast<size_t>(p.slot)] = *v;
720
+ }
689
721
  frame.bound[i] = true;
690
722
  }
691
723
  }
@@ -699,12 +731,7 @@ Value runCompiledFunctionFromBound(Evaluator& ev, const CompiledChunk& chunk, co
699
731
  }
700
732
  if (!matched && !k.empty() && k[0] == '$') childCtx.dyn->set(k, v);
701
733
  }
702
- for (size_t i = 0; i < chunk.params.size(); ++i) {
703
- if (!frame.bound[i] && !chunk.defaultCode[i].empty()) {
704
- frame.slots[static_cast<size_t>(chunk.params[i].slot)] =
705
- runChunk(ev, chunk, chunk.defaultCode[i], frame.slots, childCtx, frame.stack);
706
- }
707
- }
734
+ applyCompiledDefaults(ev, chunk, frame, childCtx);
708
735
  return runChunk(ev, chunk, chunk.bodyCode, frame.slots, childCtx, frame.stack, tailOut);
709
736
  }
710
737
 
@@ -175,10 +175,34 @@ TEST(BytecodeCompiler, NonCompilableFunctionContainingACallStillFallsBackCorrect
175
175
  "ECHO: 8");
176
176
  }
177
177
 
178
- TEST(BytecodeCompiler, DollarPrefixedParameterBailsCompilationAndStillWorks) {
179
- ScopedVm vm(true);
178
+ TEST(BytecodeCompiler, DollarPrefixedParameterCompiles) {
179
+ ScopedVm vm(true);
180
+ // $-prefixed parameters used to bail compileFunctionLike outright
181
+ // (bound through ctx.dyn, never slot-addressed) -- now they compile:
182
+ // never declared as a local (skipped in the params/declareLocal loop),
183
+ // so every reference inside the body still reaches Op::LoadDyn via the
184
+ // Identifier case's own $-check, exactly as an undeclared override
185
+ // already did.
180
186
  EXPECT_EQ(runCapturingEcho("function withFn($fn = 8) = $fn;\necho(withFn());"), "ECHO: 8");
181
187
  EXPECT_EQ(runCapturingEcho("function withFn($fn = 8) = $fn;\necho(withFn($fn = 20));"), "ECHO: 20");
188
+ // Positional binding: a $-param still occupies its declared POSITION
189
+ // among all parameters for a caller's positional arguments, exactly
190
+ // like the interpreter's own bindArgs.
191
+ EXPECT_EQ(runCapturingEcho("function withFn($fn, x) = $fn + x;\necho(withFn(10, 1));"), "ECHO: 11");
192
+ // Declared without a default and not passed: shadows to undef locally
193
+ // rather than inheriting an ancestor's dyn binding -- mirrors
194
+ // Evaluator::applyDefaults' explicit "declaring a parameter always
195
+ // creates a fresh binding" write.
196
+ EXPECT_EQ(runCapturingEcho("function withFn($fn) = $fn;\necho(let($fn = 99) withFn());"), "ECHO: undef");
197
+ // The proof this is actually running compiled, not silently falling
198
+ // back: same ternary-bodied shape and technique as
199
+ // FastContinueWithNoBreakpointInFunctionUsesVm above (3 stops =
200
+ // compiled, 5 = interpreted), with $fn substituted in for the plain
201
+ // parameter both as the declared name and every body reference.
202
+ const int stops = countDebugHookStops("function f($fn) = $fn > 0 ? $fn + 1 : $fn - 1;\n"
203
+ "echo(f(5));",
204
+ std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
205
+ EXPECT_EQ(stops, 3);
182
206
  }
183
207
 
184
208
  TEST(BytecodeCompiler, UndeclaredDollarNamedArgumentReachesDynInsideCompiledFunction) {
@@ -735,27 +759,26 @@ TEST(BytecodeCompiler, TernaryWrappedMutualRecursionResolvesCorrectly) {
735
759
  EXPECT_EQ(runCapturingEcho(script), "ECHO: true\nECHO: false");
736
760
  }
737
761
 
738
- TEST(BytecodeCompiler, ClosureWithDollarParameterStillBailsContainer) {
739
- ScopedVm vm(true);
740
- // Residual, deliberate limitation: a FunctionLiteral with its OWN
741
- // dollar-prefixed parameter still fails compileFunctionLike outright
742
- // (same $-prefixed-parameter rule as a plain named function, see
743
- // DollarPrefixedParameterBailsCompilationAndStillWorks above) -- there's
744
- // no lighter way left to discover what it captures in that case, so the
745
- // WHOLE containing declaration still bails (throw NotCompilable), same
746
- // as before Op::MakeClosure. Value must still be correct via the
747
- // interpreter fallback.
748
- const int stops = countDebugHookStops(
749
- "function outer(x) = let(g = function(y, $fn) y + x + $fn) g(5, $fn=2);\n"
750
- "echo(outer(10));",
751
- std::nullopt);
752
- // `outer` never compiles at all here (more than 1 stop for its own
753
- // call), unlike MakeClosureLetsContainerCompileDespiteNonEscapingClosure
754
- // above where the analogous ($-free) container compiles to 1.
755
- EXPECT_GT(stops, 2);
756
- EXPECT_EQ(runCapturingEcho("function outer(x) = let(g = function(y, $fn) y + x + $fn) g(5, $fn=2);\n"
757
- "echo(outer(10));"),
758
- "ECHO: 17");
762
+ TEST(BytecodeCompiler, ClosureWithDollarParameterNowCompilesToo) {
763
+ ScopedVm vm(true);
764
+ // A FunctionLiteral with its OWN dollar-prefixed parameter used to fail
765
+ // compileFunctionLike outright, bailing the WHOLE containing
766
+ // declaration (see DollarPrefixedParameterCompiles above for the plain-
767
+ // function case) -- now compileFunctionLike has no $-specific bail left
768
+ // at all, so this compiles too, container and closure both.
769
+ const std::string script = "function outer(x) = let(g = function(y, $fn) y + x + $fn) g(5, $fn=2);\n"
770
+ "echo(outer(10));";
771
+ EXPECT_EQ(runCapturingEcho(script), "ECHO: 17");
772
+ // Proof via the same fast-continue stop-count technique used
773
+ // throughout this file (nullopt is NOT a reliable signal here -- it
774
+ // forces every function to interpret regardless of eligibility, see
775
+ // DebugAttachedWithoutFastContinueAlwaysInterprets above -- a real
776
+ // breakpoint map on a non-matching line is what actually distinguishes
777
+ // compiled from interpreted). 3 stops: outer's own body-entry, g's
778
+ // call-site, g's own body-entry -- no ternary/sub-expression stops for
779
+ // either, since both now run compiled.
780
+ const int stops = countDebugHookStops(script, std::unordered_map<std::string, std::set<int>>{{"<string>", {2}}});
781
+ EXPECT_EQ(stops, 3);
759
782
  }
760
783
 
761
784
  // -- Tail-call optimization, VM path (Phase B) -----------------------------
@@ -119,6 +119,21 @@ TEST(TailCalls, DollarVarSetEarlyInATailChainStaysVisibleManyHopsLater) {
119
119
  EXPECT_EQ(runCapturingEcho(script), "ECHO: 77");
120
120
  }
121
121
 
122
+ TEST(TailCalls, DollarParameterThreadsThroughCompiledTailRecursion) {
123
+ ScopedVm vm(true);
124
+ // $fn declared as its own PARAMETER (not just read as an ambient dyn
125
+ // var, unlike the test above) rebinds via childCtx.dyn at every tail
126
+ // hop -- compileFunctionLike no longer bails on a $-prefixed parameter
127
+ // (see its own doc comment, bytecode_compiler.cpp). No tail-call-
128
+ // specific hazard: every hop's bindCompiledArgs call is identical to
129
+ // what a genuine non-tail call already does, and dyn already threads
130
+ // correctly across hops (proved by the test above).
131
+ const std::string script =
132
+ "function sum_with_fn($fn, n, acc=0) = n == 0 ? $fn + acc : sum_with_fn($fn, n - 1, acc + n);\n"
133
+ "echo(sum_with_fn(1000, 50000));";
134
+ EXPECT_EQ(runCapturingEcho(script), "ECHO: 1.25003e+9");
135
+ }
136
+
122
137
  TEST(TailCalls, NonTailRecursionIsUnaffectedByTheTrampoline) {
123
138
  ScopedVm vm(false);
124
139
  // `n * fact(n-1)` is NOT a tail position -- the multiply happens after
@@ -316,6 +331,32 @@ TEST(TailCalls, DeepNonTailRecursionHitsAControlledErrorInsteadOfCrashingCompile
316
331
  EXPECT_THROW(ev.resolveTree(ast, ctx), EvalError);
317
332
  }
318
333
 
334
+ TEST(TailCalls, CollatzStepsRecursesFromBothTernaryBranchesInterpreted) {
335
+ ScopedVm vm(false);
336
+ // Real-world example exercising tail calls from BOTH ternary branches at
337
+ // once (the even and odd cases each recurse) -- came up when auditing
338
+ // for un-trampolined tail positions (see this file's header). Scans
339
+ // every n < 10000 and takes the longest chain; the known record holder
340
+ // in that range is n=6171 at 261 steps, calling collatz_steps 10000
341
+ // times over.
342
+ const std::string script =
343
+ "function collatz_steps(n, steps=0) = n == 1 ? steps : "
344
+ "(n % 2 == 0 ? collatz_steps(n / 2, steps + 1) : collatz_steps(3 * n + 1, steps + 1));\n"
345
+ "lengths = [for (i = [1:10000]) collatz_steps(i)];\n"
346
+ "echo(max(lengths));";
347
+ EXPECT_EQ(runCapturingEcho(script), "ECHO: 261");
348
+ }
349
+
350
+ TEST(TailCalls, CollatzStepsRecursesFromBothTernaryBranchesCompiled) {
351
+ ScopedVm vm(true);
352
+ const std::string script =
353
+ "function collatz_steps(n, steps=0) = n == 1 ? steps : "
354
+ "(n % 2 == 0 ? collatz_steps(n / 2, steps + 1) : collatz_steps(3 * n + 1, steps + 1));\n"
355
+ "lengths = [for (i = [1:10000]) collatz_steps(i)];\n"
356
+ "echo(max(lengths));";
357
+ EXPECT_EQ(runCapturingEcho(script), "ECHO: 261");
358
+ }
359
+
319
360
  TEST(TailCalls, ShallowNonTailRecursionStillWorksUnderTheDepthGuard) {
320
361
  ScopedVm vm(true);
321
362
  // The guard must not fire for perfectly ordinary, shallow non-tail