openscad-cpp-evaluator 1.6.1__tar.gz → 1.7.2__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 (214) hide show
  1. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/bytecode.hpp +44 -0
  3. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/function_builtins.hpp +27 -0
  4. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/pyproject.toml +1 -1
  5. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/function_builtins.cpp +16 -12
  6. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/bytecode_compiler.cpp +61 -0
  7. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/bytecode_vm.cpp +134 -22
  8. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/mesh_check.cpp +191 -47
  9. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_bytecode_compiler.cpp +97 -0
  10. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_mesh_check.cpp +40 -0
  11. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/.github/workflows/ci.yml +0 -0
  12. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/.github/workflows/wheels.yml +0 -0
  13. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/.gitignore +0 -0
  14. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/.gitmodules +0 -0
  15. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/CLAUDE.md +0 -0
  16. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/LICENSE +0 -0
  18. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/README.md +0 -0
  19. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/bindings/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/bindings/ast_to_py.cpp +0 -0
  21. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/bindings/module.cpp +0 -0
  22. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/cmake/embed_font.cmake +0 -0
  23. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/examples/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/examples/eval_perf_benchmark.cpp +0 -0
  25. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/examples/manifold_cache_reuse.cpp +0 -0
  26. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/examples/minimal_debugger.cpp +0 -0
  27. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  28. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/.gitignore +0 -0
  29. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  30. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  31. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/README.md +0 -0
  32. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  33. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  34. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  35. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  36. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  37. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  38. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  39. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  40. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  41. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  42. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  43. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  44. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  45. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  46. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  47. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  48. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  49. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/api.cpp +0 -0
  50. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  51. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  52. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  53. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  54. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  55. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  56. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  57. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  58. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  59. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  60. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  61. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  62. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  63. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  64. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  65. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  66. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/position.cpp +0 -0
  67. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  68. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  69. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  70. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  71. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  72. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  73. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  74. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  75. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  76. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  77. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  78. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  79. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  80. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  81. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  82. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  83. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  84. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  85. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  86. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  87. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  88. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  89. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  90. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  91. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  92. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  93. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  94. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  95. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  96. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  97. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  98. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  99. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  100. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  101. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  102. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  103. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  104. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  105. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  106. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  107. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  108. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  109. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  110. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/export.hpp +0 -0
  111. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  112. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  113. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  114. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  115. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  116. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  117. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  118. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  119. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  120. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  121. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  122. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  123. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  124. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  125. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  126. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/value.hpp +0 -0
  127. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  128. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/python/openscad_cpp_evaluator/__init__.py +0 -0
  129. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/resources/fonts/LICENSE +0 -0
  130. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  131. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/CMakeLists.txt +0 -0
  132. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/booleans.cpp +0 -0
  133. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/builtins.hpp +0 -0
  134. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/call_args.cpp +0 -0
  135. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/color.cpp +0 -0
  136. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/control.cpp +0 -0
  137. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/dxf_dim.cpp +0 -0
  138. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/extrude.cpp +0 -0
  139. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/import.cpp +0 -0
  140. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/modifiers.cpp +0 -0
  141. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/primitives_2d.cpp +0 -0
  142. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/primitives_3d.cpp +0 -0
  143. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/registry.cpp +0 -0
  144. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/roof.cpp +0 -0
  145. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/segments.cpp +0 -0
  146. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/surface.cpp +0 -0
  147. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/text.cpp +0 -0
  148. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/topology.cpp +0 -0
  149. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/builtins/transforms.cpp +0 -0
  150. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/colored_body.cpp +0 -0
  151. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/csg_generate.cpp +0 -0
  152. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/csg_resolve.cpp +0 -0
  153. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/css_colors.cpp +0 -0
  154. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/debug/debug_repl.cpp +0 -0
  155. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/debug_profile.cpp +0 -0
  156. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/eval_context.cpp +0 -0
  157. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/eval_error.cpp +0 -0
  158. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/eval_use.cpp +0 -0
  159. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/export.cpp +0 -0
  160. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/expr_eval.cpp +0 -0
  161. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/format_closure.cpp +0 -0
  162. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/import/dxf_import.cpp +0 -0
  163. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/import/mesh_import.cpp +0 -0
  164. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/import/surface_load.cpp +0 -0
  165. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/import/svg_import.cpp +0 -0
  166. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/manifold_cache.cpp +0 -0
  167. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/measure_geometry.cpp +0 -0
  168. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/stmt_eval.cpp +0 -0
  169. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/text/font_match.cpp +0 -0
  170. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/text/freetype_font_provider.cpp +0 -0
  171. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/text/text_metrics.cpp +0 -0
  172. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/user_calls.cpp +0 -0
  173. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/utf8.cpp +0 -0
  174. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/value.cpp +0 -0
  175. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/src/zip_stored.cpp +0 -0
  176. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/CMakeLists.txt +0 -0
  177. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_booleans.cpp +0 -0
  178. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_children_separate.cpp +0 -0
  179. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_cli.cpp +0 -0
  180. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_control_flow.cpp +0 -0
  181. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_csg_tree.cpp +0 -0
  182. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_debug_hooks.cpp +0 -0
  183. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_dxf_svg_import.cpp +0 -0
  184. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_eval_context.cpp +0 -0
  185. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_export.cpp +0 -0
  186. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_expr_eval.cpp +0 -0
  187. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_extrude_roof.cpp +0 -0
  188. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_feature_detection.cpp +0 -0
  189. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_function_builtins.cpp +0 -0
  190. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_helpers.hpp +0 -0
  191. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_import_export.cpp +0 -0
  192. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_include_cache.cpp +0 -0
  193. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_manifold_cache.cpp +0 -0
  194. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_multi_color_merge.cpp +0 -0
  195. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_primitives.cpp +0 -0
  196. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_profiling.cpp +0 -0
  197. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_python_bindings.py +0 -0
  198. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_render_expr.cpp +0 -0
  199. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_scope_trail.cpp +0 -0
  200. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_scoping.cpp +0 -0
  201. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_surface.cpp +0 -0
  202. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_tail_calls.cpp +0 -0
  203. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_text.cpp +0 -0
  204. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_transforms.cpp +0 -0
  205. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_unexpected_args.cpp +0 -0
  206. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_use.cpp +0 -0
  207. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_value.cpp +0 -0
  208. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_viewport_params.cpp +0 -0
  209. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tests/test_zip_stored.cpp +0 -0
  210. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tools/cli/CMakeLists.txt +0 -0
  211. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tools/cli/cli_lib.cpp +0 -0
  212. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tools/cli/cli_lib.hpp +0 -0
  213. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/tools/cli/main.cpp +0 -0
  214. {openscad_cpp_evaluator-1.6.1 → openscad_cpp_evaluator-1.7.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.6.1
3
+ Version: 1.7.2
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -1,5 +1,6 @@
1
1
  #pragma once
2
2
 
3
+ #include "openscad_cpp_evaluator/function_builtins.hpp"
3
4
  #include "openscad_cpp_evaluator/value.hpp"
4
5
 
5
6
  #include "openscad_cpp_parser/ast/ast_node.hpp"
@@ -787,6 +788,49 @@ struct CompiledChunk {
787
788
  const oscad::FunctionDeclaration* decl = nullptr;
788
789
  std::vector<std::optional<std::string>> argNames;
789
790
  const oscad::ASTNode* callNode = nullptr;
791
+ // Everything below is `calleeName` already looked up, once, here at
792
+ // compile time. The VM used to redo all three on EVERY builtin call:
793
+ // two std::string hashes into static tables plus a string compare
794
+ // against "object", for answers that cannot change once this site is
795
+ // compiled. `declaredParams` points into a function-local static
796
+ // table with process lifetime, so holding the pointer is safe.
797
+ BuiltinFnId builtinId = BuiltinFnId::None;
798
+ const std::vector<std::string>* declaredParams = nullptr;
799
+ bool isObjectBuiltin = false;
800
+
801
+ // Where each argument of this call goes, worked out once at compile
802
+ // time against `decl`'s parameter list -- see ArgBind. Only filled in
803
+ // for a static user-function callee (`decl` set); a builtin, an
804
+ // import or a dynamic/closure callee has no compile-time parameter
805
+ // list to match against and keeps the name-matching path.
806
+ //
807
+ // The names on both sides of that matching are fixed the moment this
808
+ // site is compiled, but the VM was redoing it on every call, twice:
809
+ // buildBoundArgs linear-scanned a name-keyed BoundArgs (copying each
810
+ // name into it), then bindBoundArgsIntoFrame linear-scanned it again
811
+ // for every parameter, then a third nested loop compared every bound
812
+ // name against every parameter name. A two-argument call did roughly
813
+ // ten std::string comparisons and two string copies to reach an
814
+ // answer that was already known. Binding straight into the frame's
815
+ // slots also MOVES each argument instead of moving it into BoundArgs
816
+ // and copying it back out.
817
+ struct ArgBind {
818
+ int paramIndex = -1; // >=0: this argument binds that parameter
819
+ // Any FURTHER parameters of the same name. Normally empty, but a
820
+ // declaration may repeat a parameter name -- BOSL2's
821
+ // regular_prism() declares both `length` and `height` twice --
822
+ // and the name-matching path this replaced bound every one of
823
+ // them, because it walked the parameter list asking "is there a
824
+ // bound argument called this?" rather than the other way round.
825
+ // Binding only the first silently left the body reading undef
826
+ // from whichever slot the duplicate name finally resolved to.
827
+ std::vector<int> alsoParams;
828
+ bool toDyn = false; // instead bind by name into ctx.dyn ($-named, undeclared)
829
+ bool warnUnexpectedNamed = false;
830
+ bool warnTooManyPositional = false;
831
+ };
832
+ std::vector<ArgBind> argBinds;
833
+ bool hasArgPlan = false; // argBinds is authoritative (it is empty for a zero-argument call too)
790
834
  };
791
835
 
792
836
  // An upvalue read (see Op::LoadUpvalue): `targetDecl` is the enclosing
@@ -26,8 +26,35 @@ bool isBuiltinFunctionName(const std::string& name);
26
26
  // accept $-prefixed named-argument overrides). Mirrors the reference's
27
27
  // _math_fns dispatch table plus the textmetrics()/fontmetrics() special
28
28
  // cases (implemented on top of FontProvider, text_metrics.hpp).
29
+ // One id per name evalBuiltinFunction handles (NOT the same list as
30
+ // isBuiltinFunctionName -- "object" is deliberately absent, since it is
31
+ // special-cased to builtinObject() before this function is reached). Public
32
+ // so a CALL SITE can resolve its callee once, at compile time
33
+ // (CompiledChunk::CallSite::builtinId), instead of hashing the name string
34
+ // on every single call.
35
+ enum class BuiltinFnId {
36
+ None, // not a name evalBuiltinFunction handles
37
+ TextMetrics, FontMetrics, Abs, Sign, Ceil, Floor, Round, Sqrt, Ln, Log, Exp, Sin, Cos, Tan,
38
+ Asin, Acos, Atan, Atan2, Max, Min, Pow, Norm, Cross, Rands, Concat, Len, Str, Chr, Ord,
39
+ IsUndef, IsNum, IsBool, IsString, IsList, IsFunction, IsObject, Search, Lookup, HasKey,
40
+ Version, VersionNum, ParentModule, DxfDim, DxfCross, SupportedFeature, LinearSolve,
41
+ };
42
+
43
+ // `name` -> its id, or None. The one place that hashes the name; everything
44
+ // on a hot path should call this once at compile time and keep the answer.
45
+ BuiltinFnId builtinFnIdFor(const std::string& name);
46
+
29
47
  Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs& args, const oscad::ASTNode& node);
30
48
 
49
+ // The same dispatch with both of its name lookups already done: `id` from
50
+ // builtinFnIdFor and `declaredParams` from builtinParamNames (dispatch.hpp),
51
+ // which for all but a couple of builtins is null. `name` survives only
52
+ // because diagnostics quote it. The string-taking overload above is just
53
+ // this one with the two lookups performed first, and remains the entry point
54
+ // for the AST interpreter, where there is no call site to cache them on.
55
+ Value evalBuiltinFunctionResolved(Evaluator& ev, BuiltinFnId id, const std::vector<std::string>* declaredParams,
56
+ const std::string& name, const CallArgs& args, const oscad::ASTNode& node);
57
+
31
58
  // object(a=1, b=2, ...) -- an ordered string-keyed map merging positional
32
59
  // (an existing object's entries, or a list of [key,value] pairs) and named
33
60
  // arguments, later arguments winning over earlier ones for the same key --
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.6.1"
7
+ version = "1.7.2"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -1007,12 +1007,7 @@ namespace {
1007
1007
  // `if (name == "...")` string-compare chain; the switch below then
1008
1008
  // compiles to a jump table instead of sequential comparisons. No branch's
1009
1009
  // logic changed, only how it's reached.
1010
- enum class BuiltinFnId {
1011
- TextMetrics, FontMetrics, Abs, Sign, Ceil, Floor, Round, Sqrt, Ln, Log, Exp, Sin, Cos, Tan,
1012
- Asin, Acos, Atan, Atan2, Max, Min, Pow, Norm, Cross, Rands, Concat, Len, Str, Chr, Ord,
1013
- IsUndef, IsNum, IsBool, IsString, IsList, IsFunction, IsObject, Search, Lookup, HasKey,
1014
- Version, VersionNum, ParentModule, DxfDim, DxfCross, SupportedFeature, LinearSolve,
1015
- };
1010
+ // BuiltinFnId itself now lives in function_builtins.hpp -- see builtinFnIdFor.
1016
1011
 
1017
1012
  // supported_feature("name") -> the level at which this build implements that
1018
1013
  // feature, or 0 for one it does not implement (and for a name it has never
@@ -1242,18 +1237,26 @@ bool checkBuiltinArgs(Evaluator& ev, const std::string& name, BuiltinFnId id, co
1242
1237
 
1243
1238
  } // namespace
1244
1239
 
1240
+ BuiltinFnId builtinFnIdFor(const std::string& name) {
1241
+ const auto& ids = builtinFnIds();
1242
+ const auto it = ids.find(name);
1243
+ return it == ids.end() ? BuiltinFnId::None : it->second;
1244
+ }
1245
+
1245
1246
  Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs& args, const oscad::ASTNode& node) {
1247
+ return evalBuiltinFunctionResolved(ev, builtinFnIdFor(name), builtinParamNames(name), name, args, node);
1248
+ }
1246
1249
 
1247
- const auto& ids = builtinFnIds();
1248
- const auto idIt = ids.find(name);
1250
+ Value evalBuiltinFunctionResolved(Evaluator& ev, BuiltinFnId id, const std::vector<std::string>* declaredParams,
1251
+ const std::string& name, const CallArgs& args, const oscad::ASTNode& node) {
1249
1252
  // Not one of the names this function handles (e.g. "object", routed
1250
1253
  // elsewhere before reaching here) -- mirrors the old chain's fallthrough.
1251
- if (idIt == ids.end()) return Value{};
1254
+ if (id == BuiltinFnId::None) return Value{};
1252
1255
 
1253
1256
  // Only textmetrics/fontmetrics have an entry -- every other builtin
1254
1257
  // function reads its arguments positionally upstream and warns about
1255
1258
  // nothing. See builtinParamNames (registry.cpp).
1256
- if (const std::vector<std::string>* declared = builtinParamNames(name)) {
1259
+ if (const std::vector<std::string>* declared = declaredParams) {
1257
1260
  for (const auto& [argName, _] : args.named) {
1258
1261
  if (!isConfigVariable(argName) &&
1259
1262
  std::find(declared->begin(), declared->end(), argName) == declared->end()) {
@@ -1265,9 +1268,10 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
1265
1268
  // Arity and argument types, with the reference's own two diagnostics.
1266
1269
  // This replaced a silent version of the same gate (scalarNumericArity /
1267
1270
  // numericOnlyNames), which returned undef without ever saying why.
1268
- if (!checkBuiltinArgs(ev, name, idIt->second, args, &node.position())) return Value{};
1271
+ if (!checkBuiltinArgs(ev, name, id, args, &node.position())) return Value{};
1269
1272
 
1270
- switch (idIt->second) {
1273
+ switch (id) {
1274
+ case BuiltinFnId::None: return Value{}; // unreachable: handled above
1271
1275
  case BuiltinFnId::TextMetrics: return builtinTextmetrics(ev, args);
1272
1276
  case BuiltinFnId::FontMetrics: return builtinFontmetrics(ev, args);
1273
1277
  case BuiltinFnId::Abs: return Value{std::fabs(toDoubleLenient(getArg(args, 0, "x", Value{})))};
@@ -20,6 +20,59 @@
20
20
  namespace oscadeval {
21
21
 
22
22
  namespace {
23
+ // Records every parameter called `name`, not just the first. See
24
+ // CompiledChunk::CallSite::ArgBind::alsoParams for why there can be more
25
+ // than one.
26
+ void bindEveryParamNamed(CompiledChunk::CallSite::ArgBind& b,
27
+ const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params,
28
+ const std::string& name) {
29
+ for (size_t p = 0; p < params.size(); ++p) {
30
+ if (params[p]->name->name != name) continue;
31
+ if (b.paramIndex < 0) {
32
+ b.paramIndex = static_cast<int>(p);
33
+ } else {
34
+ b.alsoParams.push_back(static_cast<int>(p));
35
+ }
36
+ }
37
+ }
38
+
39
+ // Replays buildBoundArgs' own positional/named matching rule (bytecode_vm.cpp)
40
+ // against a parameter list known at compile time, recording the answer per
41
+ // argument instead of recomputing it per call. The rule, unchanged: a named
42
+ // argument binds the parameter of that name, warning when there is none and
43
+ // the name is not a $-config variable; a positional argument binds the
44
+ // parameter at its own positional index, warning once when it runs past the
45
+ // end; an undeclared $-name still reaches ctx.dyn; anything else is dropped.
46
+ // A later argument writing the same parameter still wins, because both bind
47
+ // the same slot in order.
48
+ void planCallSiteArgs(CompiledChunk::CallSite& site,
49
+ const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& params) {
50
+ const size_t nparams = params.size();
51
+ size_t positionalIdx = 0;
52
+ site.argBinds.resize(site.argNames.size());
53
+ for (size_t i = 0; i < site.argNames.size(); ++i) {
54
+ CompiledChunk::CallSite::ArgBind& b = site.argBinds[i];
55
+ if (site.argNames[i]) {
56
+ const std::string& name = *site.argNames[i];
57
+ bindEveryParamNamed(b, params, name);
58
+ const bool found = b.paramIndex >= 0;
59
+ b.warnUnexpectedNamed = !found && !isConfigVariable(name);
60
+ b.toDyn = !found && !name.empty() && name[0] == '$';
61
+ } else {
62
+ if (positionalIdx < nparams) {
63
+ // By NAME, not just by index: a positional argument landing on
64
+ // a repeated name filled every parameter of that name too,
65
+ // because the old path keyed its bound arguments by name.
66
+ bindEveryParamNamed(b, params, params[positionalIdx]->name->name);
67
+ } else if (positionalIdx == nparams) {
68
+ b.warnTooManyPositional = true;
69
+ }
70
+ ++positionalIdx;
71
+ }
72
+ }
73
+ site.hasArgPlan = true;
74
+ }
75
+
23
76
 
24
77
  // Thrown by compileExpr wherever it hits a construct this phase doesn't
25
78
  // compile yet -- tryCompileFunction catches it and returns nullopt (falls
@@ -601,6 +654,11 @@ public:
601
654
  // call-site interleaved order, not resolveArgs'
602
655
  // split positional/named CallArgs shape).
603
656
  site.isBuiltin = true;
657
+ // Resolve the callee here rather than per call; see
658
+ // CallSite's own doc comment (bytecode.hpp).
659
+ site.builtinId = builtinFnIdFor(calleeName);
660
+ site.declaredParams = builtinParamNames(calleeName);
661
+ site.isObjectBuiltin = (calleeName == "object");
604
662
  isStatic = true;
605
663
  }
606
664
  // Anything else stays dynamic: a function-literal value,
@@ -674,6 +732,9 @@ public:
674
732
  }
675
733
  }
676
734
  }
735
+ // Resolve every argument to its parameter now; see
736
+ // CallSite::argBinds (bytecode.hpp) for why.
737
+ if (site.decl) planCallSiteArgs(site, site.decl->parameters);
677
738
  int siteIdx = static_cast<int>(chunk_.callSites.size());
678
739
  chunk_.callSites.push_back(std::move(site));
679
740
  // Builtins and import() never trampoline (matching
@@ -49,8 +49,12 @@ CallArgs& buildCallArgs(const CompiledChunk::CallSite& site, Value* args, size_t
49
49
  // LIST is `paramNames` (in declared order -- a FunctionDeclaration's
50
50
  // parameters or a FunctionLiteral's). Shared by CallFn/CallFnTail (a
51
51
  // site.decl callee) and CallDynamic/CallDynamicTail (a closure callee).
52
+ // `emitWarnings=false` is for the one caller that has already emitted them
53
+ // through warnPlannedArgs and only needs the binding -- see Op::CallFnTail's
54
+ // no-hop fallback.
52
55
  BoundArgs buildBoundArgs(Evaluator& ev, const CompiledChunk::CallSite& site, Value* args, size_t argCount,
53
- const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& paramNames) {
56
+ const std::vector<std::unique_ptr<oscad::ParameterDeclaration>>& paramNames,
57
+ bool emitWarnings = true) {
54
58
  BoundArgs bound;
55
59
  bound.reserve(argCount);
56
60
  size_t positionalIdx = 0;
@@ -59,14 +63,14 @@ BoundArgs buildBoundArgs(Evaluator& ev, const CompiledChunk::CallSite& site, Val
59
63
  for (size_t i = 0; i < argCount; ++i) {
60
64
  if (site.argNames[i]) {
61
65
  const std::string& name = *site.argNames[i];
62
- if (!isConfigVariable(name) && !declaresParam(paramNames, name)) {
66
+ if (emitWarnings && !isConfigVariable(name) && !declaresParam(paramNames, name)) {
63
67
  warnUnexpectedNamedArg(ev, name, pos);
64
68
  }
65
69
  bound.set(name, std::move(args[i]));
66
70
  } else {
67
71
  if (positionalIdx < nparams) {
68
72
  bound.set(paramNames[positionalIdx]->name->name, std::move(args[i]));
69
- } else if (positionalIdx == nparams) {
73
+ } else if (emitWarnings && positionalIdx == nparams) {
70
74
  warnTooManyPositionalArgs(ev, pos);
71
75
  }
72
76
  ++positionalIdx;
@@ -108,6 +112,71 @@ void bindBoundArgsIntoFrame(const CompiledChunk& chunk, const BoundArgs& bound,
108
112
  }
109
113
  }
110
114
 
115
+ // Where a pushed frame's arguments come from: a call site's compile-time plan
116
+ // over the caller's own live operand stack (the hot path), or an
117
+ // already-name-matched BoundArgs (closures and dynamic callees, which have no
118
+ // compile-time parameter list to plan against).
119
+ struct FrameArgs {
120
+ const CompiledChunk::CallSite* site = nullptr;
121
+ Value* args = nullptr;
122
+ size_t argCount = 0;
123
+ const BoundArgs* bound = nullptr;
124
+ };
125
+
126
+ // The compile-time counterpart of bindBoundArgsIntoFrame: binds a call's
127
+ // arguments straight from the caller's operand stack into the callee frame,
128
+ // using the plan its call site worked out once (CallSite::argBinds). No name
129
+ // is compared and no BoundArgs is built, and each argument is MOVED into its
130
+ // slot rather than moved into a BoundArgs and copied back out. `args` must
131
+ // still be live -- the caller truncates its stack only after this returns.
132
+ // The argument diagnostics of a planned call, split out of the binder below
133
+ // because WHEN they fire is observable: their "TRACE: called by ..." line
134
+ // names whatever is on top of the call stack, and a tail hop has already
135
+ // replaced that with the CALLEE by the time it binds. buildBoundArgs warned
136
+ // before any of that happened, so this must too -- caught by
137
+ // UnexpectedArgs.NestedUserFunctionCallStillWarns, which compares the VM
138
+ // against the interpreter.
139
+ void warnPlannedArgs(Evaluator& ev, const CompiledChunk::CallSite& site, size_t argCount) {
140
+ const oscad::Position* pos = site.callNode ? &site.callNode->position() : nullptr;
141
+ for (size_t i = 0; i < argCount; ++i) {
142
+ const CompiledChunk::CallSite::ArgBind& b = site.argBinds[i];
143
+ if (b.warnUnexpectedNamed) warnUnexpectedNamedArg(ev, *site.argNames[i], pos);
144
+ if (b.warnTooManyPositional) warnTooManyPositionalArgs(ev, pos);
145
+ }
146
+ }
147
+
148
+ void bindPlannedArgsIntoFrame(const CompiledChunk& chunk, const CompiledChunk::CallSite& site, Value* args,
149
+ size_t argCount, VmFrame& frame) {
150
+ EvalContext& ctx = frame.ctxChain.back();
151
+ for (size_t i = 0; i < argCount; ++i) {
152
+ const CompiledChunk::CallSite::ArgBind& b = site.argBinds[i];
153
+ if (b.paramIndex >= 0) {
154
+ // The duplicate-name case (alsoParams) needs the value more than
155
+ // once, so it is copied there and only the last one moves.
156
+ for (const int extra : b.alsoParams) {
157
+ const CompiledChunk::Param& e = chunk.params[static_cast<size_t>(extra)];
158
+ if (e.isDyn) {
159
+ ctx.dyn->set(e.name, args[i]);
160
+ } else {
161
+ frame.slots[static_cast<size_t>(e.slot)] = args[i];
162
+ }
163
+ frame.bound[static_cast<size_t>(extra)] = true;
164
+ }
165
+ const CompiledChunk::Param& p = chunk.params[static_cast<size_t>(b.paramIndex)];
166
+ if (p.isDyn) {
167
+ ctx.dyn->set(p.name, std::move(args[i]));
168
+ } else {
169
+ frame.slots[static_cast<size_t>(p.slot)] = std::move(args[i]);
170
+ }
171
+ frame.bound[static_cast<size_t>(b.paramIndex)] = true;
172
+ } else if (b.toDyn) {
173
+ ctx.dyn->set(*site.argNames[i], std::move(args[i]));
174
+ }
175
+ // else: an undeclared plain name, or a positional past the end --
176
+ // dropped, exactly as the name-matching path drops it.
177
+ }
178
+ }
179
+
111
180
  // Same, but for a call site whose arguments are still raw AST (evaluated
112
181
  // here against `callerCtx`) -- only ever used by runCompiledFunction's own
113
182
  // entry point (a top-level call with raw AST arguments, mirroring
@@ -249,7 +318,7 @@ void pushBareFrame(Evaluator& ev, const CompiledChunk& chunk, const std::vector<
249
318
  // own parameters exactly (a FunctionDeclaration's decl/*decl.expr, or a
250
319
  // FunctionLiteral's funcNode/*funcNode.body).
251
320
  void pushBracketedCallFrame(Evaluator& ev, const CompiledChunk& chunk, const oscad::ASTNode& declNode,
252
- const oscad::Expression& bodyExpr, const std::string& name, BoundArgs bound,
321
+ const oscad::Expression& bodyExpr, const std::string& name, const FrameArgs& fargs,
253
322
  EvalContext& callerCtx, const oscad::Scope* fnScope,
254
323
  const std::shared_ptr<TrailView<Value>>& capturedLet, const oscad::Position* callPos) {
255
324
  if (ev.vmCallStack_.size() >= Evaluator::kMaxVmCallStackDepth) {
@@ -274,7 +343,11 @@ void pushBracketedCallFrame(Evaluator& ev, const CompiledChunk& chunk, const osc
274
343
  frame->tailHopGuard = 0;
275
344
  frame->logicalName = name;
276
345
  frame->hopEligible = true; // just pushed a fresh, correctly-named callStack_ entry
277
- bindBoundArgsIntoFrame(chunk, bound, *frame);
346
+ if (fargs.site) {
347
+ bindPlannedArgsIntoFrame(chunk, *fargs.site, fargs.args, fargs.argCount, *frame);
348
+ } else {
349
+ bindBoundArgsIntoFrame(chunk, *fargs.bound, *frame);
350
+ }
278
351
  applyCompiledDefaultsToFrame(ev, chunk, *frame);
279
352
 
280
353
  // enterUserCall's own bodyCtx must point at frame->ctxChain's OWN
@@ -802,7 +875,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
802
875
  f.stack.resize(argBase);
803
876
  f.stack.push_back(importAsValue(ev, callArgs, *site.callNode));
804
877
  ++f.pc;
805
- } else if (site.isBuiltin && site.calleeName == "object") {
878
+ } else if (site.isObjectBuiltin) {
806
879
  std::vector<std::pair<std::optional<std::string>, Value>> pairs;
807
880
  pairs.reserve(argCount);
808
881
  for (size_t i = 0; i < argCount; ++i) pairs.emplace_back(site.argNames[i], std::move(args[i]));
@@ -812,24 +885,39 @@ Value driveVm(Evaluator& ev, size_t floor) {
812
885
  } else if (site.isBuiltin) {
813
886
  CallArgs& callArgs = buildCallArgs(site, args, argCount, f.argScratch);
814
887
  f.stack.resize(argBase);
815
- f.stack.push_back(evalBuiltinFunction(ev, site.calleeName, callArgs, *site.callNode));
888
+ f.stack.push_back(evalBuiltinFunctionResolved(ev, site.builtinId, site.declaredParams,
889
+ site.calleeName, callArgs, *site.callNode));
816
890
  ++f.pc;
817
891
  } else {
818
- BoundArgs bound = buildBoundArgs(ev, site, args, argCount, site.decl->parameters);
819
- f.stack.resize(argBase);
820
892
  const CompiledChunk* calleeChunk = ev.useBytecodeVm() ? ev.lookupOrCompileChunk(*site.decl) : nullptr;
821
- if (calleeChunk) {
893
+ if (calleeChunk && site.hasArgPlan) {
894
+ warnPlannedArgs(ev, site, argCount);
895
+ // The hot path: bind straight off this frame's own
896
+ // stack using the site's compile-time plan, then
897
+ // truncate. Truncating FIRST would destroy the very
898
+ // values being bound.
822
899
  const oscad::Scope* fnScope = ctx.scopeOf(*site.decl) ? ctx.scopeOf(*site.decl) : ctx.scope;
823
900
  pushBracketedCallFrame(ev, *calleeChunk, *site.decl, *site.decl->expr, site.calleeName,
824
- std::move(bound), ctx, fnScope, nullptr, &site.callNode->position());
901
+ FrameArgs{&site, args, argCount, nullptr}, ctx, fnScope, nullptr,
902
+ &site.callNode->position());
903
+ f.stack.resize(argBase);
825
904
  // f.pc deliberately NOT advanced -- resumes when the
826
905
  // pushed frame completes (see driveVm's own
827
906
  // completion branch, above).
828
907
  } else {
829
- Value result = ev.evalUserFunctionFromBound(site.calleeName, *site.decl, std::move(bound), ctx,
830
- &site.callNode->position());
831
- f.stack.push_back(std::move(result));
832
- ++f.pc;
908
+ BoundArgs bound = buildBoundArgs(ev, site, args, argCount, site.decl->parameters);
909
+ f.stack.resize(argBase);
910
+ if (calleeChunk) {
911
+ const oscad::Scope* fnScope = ctx.scopeOf(*site.decl) ? ctx.scopeOf(*site.decl) : ctx.scope;
912
+ pushBracketedCallFrame(ev, *calleeChunk, *site.decl, *site.decl->expr, site.calleeName,
913
+ FrameArgs{nullptr, nullptr, 0, &bound}, ctx, fnScope, nullptr,
914
+ &site.callNode->position());
915
+ } else {
916
+ Value result = ev.evalUserFunctionFromBound(site.calleeName, *site.decl, std::move(bound),
917
+ ctx, &site.callNode->position());
918
+ f.stack.push_back(std::move(result));
919
+ ++f.pc;
920
+ }
833
921
  }
834
922
  }
835
923
  break;
@@ -849,7 +937,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
849
937
  if (calleeChunk) {
850
938
  const oscad::Scope* fnScope = ctx.scopeOf(funcNode) ? ctx.scopeOf(funcNode) : ctx.scope;
851
939
  pushBracketedCallFrame(ev, *calleeChunk, funcNode, *funcNode.body, "<function literal>",
852
- std::move(bound), ctx, fnScope, capturedLetTrail(closure), ins.pos);
940
+ FrameArgs{nullptr, nullptr, 0, &bound}, ctx, fnScope, capturedLetTrail(closure), ins.pos);
853
941
  } else {
854
942
  Value result = ev.evalFunctionLiteralFromBound(closure, std::move(bound), ctx, ins.pos);
855
943
  f.stack.push_back(std::move(result));
@@ -868,8 +956,17 @@ Value driveVm(Evaluator& ev, size_t floor) {
868
956
  const size_t argCount = static_cast<size_t>(ins.b);
869
957
  const size_t argBase = f.stack.size() - argCount;
870
958
  Value* args = f.stack.data() + argBase; // into f.stack; truncated below once consumed
871
- BoundArgs bound = buildBoundArgs(ev, site, args, argCount, site.decl->parameters);
872
- f.stack.resize(argBase);
959
+ // A planned site defers both of these: the arguments have
960
+ // to stay live on this frame's own stack until they are
961
+ // bound, and a tail HOP binds into this very frame.
962
+ const bool planned = site.hasArgPlan;
963
+ BoundArgs bound;
964
+ if (planned) {
965
+ warnPlannedArgs(ev, site, argCount); // before recordTailCallHop; see warnPlannedArgs
966
+ } else {
967
+ bound = buildBoundArgs(ev, site, args, argCount, site.decl->parameters);
968
+ f.stack.resize(argBase);
969
+ }
873
970
  // Tail-hop-in-place requires f.hopEligible -- a frame
874
971
  // that's call-boundary-free (statement expression,
875
972
  // assignment block, parameter default) has no
@@ -898,22 +995,37 @@ Value driveVm(Evaluator& ev, size_t floor) {
898
995
  f.setCode(calleeChunk->bodyCode);
899
996
  f.pc = 0;
900
997
  f.slots.assign(static_cast<size_t>(calleeChunk->numSlots), Value{});
901
- f.stack.clear();
902
998
  f.bound.assign(calleeChunk->params.size(), false);
903
999
  f.accumStack.clear();
904
1000
  f.iterLists.assign(static_cast<size_t>(calleeChunk->numIterLists), IterList{});
905
1001
  f.ctxChain.push_back(std::move(*hopCtx));
906
- bindBoundArgsIntoFrame(*calleeChunk, bound, f);
1002
+ // The stack is cleared AFTER binding on the planned
1003
+ // path: a hop rebinds this same frame, and `args`
1004
+ // still points into the stack it would have wiped.
1005
+ if (planned) {
1006
+ bindPlannedArgsIntoFrame(*calleeChunk, site, args, argCount, f);
1007
+ f.stack.clear();
1008
+ } else {
1009
+ f.stack.clear();
1010
+ bindBoundArgsIntoFrame(*calleeChunk, bound, f);
1011
+ }
907
1012
  applyCompiledDefaultsToFrame(ev, *calleeChunk, f);
908
1013
  // continue via the outer while -- re-fetches this
909
1014
  // same (now-mutated) frame from its new pc=0.
910
1015
  } else {
1016
+ if (planned) { // no hop after all -- fall back to name matching
1017
+ // warnPlannedArgs already emitted these above.
1018
+ bound = buildBoundArgs(ev, site, args, argCount, site.decl->parameters,
1019
+ /*emitWarnings=*/false);
1020
+ f.stack.resize(argBase);
1021
+ }
911
1022
  const CompiledChunk* fallbackChunk =
912
1023
  ev.useBytecodeVm() ? ev.lookupOrCompileChunk(*site.decl) : nullptr;
913
1024
  if (fallbackChunk) {
914
1025
  const oscad::Scope* fnScope = ctx.scopeOf(*site.decl) ? ctx.scopeOf(*site.decl) : ctx.scope;
915
1026
  pushBracketedCallFrame(ev, *fallbackChunk, *site.decl, *site.decl->expr, site.calleeName,
916
- std::move(bound), ctx, fnScope, nullptr, &site.callNode->position());
1027
+ FrameArgs{nullptr, nullptr, 0, &bound}, ctx, fnScope, nullptr,
1028
+ &site.callNode->position());
917
1029
  } else {
918
1030
  Value result = ev.evalUserFunctionFromBound(site.calleeName, *site.decl, std::move(bound), ctx,
919
1031
  &site.callNode->position());
@@ -960,7 +1072,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
960
1072
  if (fallbackChunk) {
961
1073
  const oscad::Scope* fnScope = ctx.scopeOf(funcNode) ? ctx.scopeOf(funcNode) : ctx.scope;
962
1074
  pushBracketedCallFrame(ev, *fallbackChunk, funcNode, *funcNode.body, "<function literal>",
963
- std::move(bound), ctx, fnScope, capturedLetTrail(closure), ins.pos);
1075
+ FrameArgs{nullptr, nullptr, 0, &bound}, ctx, fnScope, capturedLetTrail(closure), ins.pos);
964
1076
  } else {
965
1077
  Value result = ev.evalFunctionLiteralFromBound(closure, std::move(bound), ctx, ins.pos);
966
1078
  f.stack.push_back(std::move(result));