openscad-cpp-evaluator 1.5.0__tar.gz → 1.6.1__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.5.0 → openscad_cpp_evaluator-1.6.1}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +24 -1
  3. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/call_args.hpp +10 -0
  4. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/evaluator.hpp +20 -1
  5. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/scope_trail.hpp +125 -45
  6. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/pyproject.toml +1 -1
  7. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/call_args.cpp +12 -0
  8. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/function_builtins.cpp +13 -12
  9. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/bytecode_vm.cpp +46 -43
  10. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/eval_context.cpp +17 -3
  11. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/user_calls.cpp +11 -5
  12. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_bytecode_compiler.cpp +35 -0
  13. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_scope_trail.cpp +24 -0
  14. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/.github/workflows/ci.yml +0 -0
  15. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/.github/workflows/wheels.yml +0 -0
  16. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/.gitignore +0 -0
  17. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/.gitmodules +0 -0
  18. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/CLAUDE.md +0 -0
  19. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/LICENSE +0 -0
  21. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/README.md +0 -0
  22. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/bindings/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/bindings/ast_to_py.cpp +0 -0
  24. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/bindings/module.cpp +0 -0
  25. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/cmake/embed_font.cmake +0 -0
  26. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/examples/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/examples/eval_perf_benchmark.cpp +0 -0
  28. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/examples/manifold_cache_reuse.cpp +0 -0
  29. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/examples/minimal_debugger.cpp +0 -0
  30. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  31. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/.gitignore +0 -0
  32. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  33. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  34. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/README.md +0 -0
  35. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  36. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  37. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  39. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  40. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  41. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  42. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  43. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  44. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  45. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  46. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  47. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  48. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  49. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  50. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  51. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  52. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
  53. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  54. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  55. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  56. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  57. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  58. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  59. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  60. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  61. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  62. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  63. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  64. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  65. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  66. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  67. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  68. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  69. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
  70. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  71. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  72. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  73. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  74. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  75. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  76. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  77. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  78. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  79. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  80. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  81. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  82. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  83. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  84. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  85. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  86. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  87. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  88. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  89. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  90. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  91. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  92. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  93. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  94. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  95. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  96. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  97. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  98. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  99. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  100. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  101. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  102. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  103. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  104. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  105. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  106. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  107. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  108. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  109. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  110. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  111. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
  112. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  113. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  114. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  115. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  117. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  118. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  119. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  120. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  121. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  122. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  123. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  124. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  125. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  126. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  127. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
  128. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  129. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
  130. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/resources/fonts/LICENSE +0 -0
  131. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  132. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/CMakeLists.txt +0 -0
  133. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/booleans.cpp +0 -0
  134. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/builtins.hpp +0 -0
  135. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/color.cpp +0 -0
  136. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/control.cpp +0 -0
  137. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/dxf_dim.cpp +0 -0
  138. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/extrude.cpp +0 -0
  139. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/import.cpp +0 -0
  140. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/modifiers.cpp +0 -0
  141. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/primitives_2d.cpp +0 -0
  142. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/primitives_3d.cpp +0 -0
  143. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/registry.cpp +0 -0
  144. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/roof.cpp +0 -0
  145. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/segments.cpp +0 -0
  146. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/surface.cpp +0 -0
  147. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/text.cpp +0 -0
  148. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/topology.cpp +0 -0
  149. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/builtins/transforms.cpp +0 -0
  150. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/bytecode_compiler.cpp +0 -0
  151. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/colored_body.cpp +0 -0
  152. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/csg_generate.cpp +0 -0
  153. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/csg_resolve.cpp +0 -0
  154. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/css_colors.cpp +0 -0
  155. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/debug/debug_repl.cpp +0 -0
  156. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/debug_profile.cpp +0 -0
  157. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/eval_error.cpp +0 -0
  158. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/eval_use.cpp +0 -0
  159. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/export.cpp +0 -0
  160. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/expr_eval.cpp +0 -0
  161. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/format_closure.cpp +0 -0
  162. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/import/dxf_import.cpp +0 -0
  163. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/import/mesh_import.cpp +0 -0
  164. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/import/surface_load.cpp +0 -0
  165. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/import/svg_import.cpp +0 -0
  166. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/manifold_cache.cpp +0 -0
  167. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/measure_geometry.cpp +0 -0
  168. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/mesh_check.cpp +0 -0
  169. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/stmt_eval.cpp +0 -0
  170. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/text/font_match.cpp +0 -0
  171. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/text/freetype_font_provider.cpp +0 -0
  172. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/text/text_metrics.cpp +0 -0
  173. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/utf8.cpp +0 -0
  174. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/value.cpp +0 -0
  175. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/src/zip_stored.cpp +0 -0
  176. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/CMakeLists.txt +0 -0
  177. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_booleans.cpp +0 -0
  178. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_children_separate.cpp +0 -0
  179. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_cli.cpp +0 -0
  180. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_control_flow.cpp +0 -0
  181. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_csg_tree.cpp +0 -0
  182. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_debug_hooks.cpp +0 -0
  183. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_dxf_svg_import.cpp +0 -0
  184. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_eval_context.cpp +0 -0
  185. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_export.cpp +0 -0
  186. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_expr_eval.cpp +0 -0
  187. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_extrude_roof.cpp +0 -0
  188. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_feature_detection.cpp +0 -0
  189. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_function_builtins.cpp +0 -0
  190. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_helpers.hpp +0 -0
  191. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_import_export.cpp +0 -0
  192. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_include_cache.cpp +0 -0
  193. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_manifold_cache.cpp +0 -0
  194. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_mesh_check.cpp +0 -0
  195. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_multi_color_merge.cpp +0 -0
  196. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_primitives.cpp +0 -0
  197. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_profiling.cpp +0 -0
  198. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_python_bindings.py +0 -0
  199. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_render_expr.cpp +0 -0
  200. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_scoping.cpp +0 -0
  201. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_surface.cpp +0 -0
  202. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_tail_calls.cpp +0 -0
  203. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_text.cpp +0 -0
  204. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_transforms.cpp +0 -0
  205. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_unexpected_args.cpp +0 -0
  206. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_use.cpp +0 -0
  207. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_value.cpp +0 -0
  208. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_viewport_params.cpp +0 -0
  209. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tests/test_zip_stored.cpp +0 -0
  210. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tools/cli/CMakeLists.txt +0 -0
  211. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tools/cli/cli_lib.cpp +0 -0
  212. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tools/cli/cli_lib.hpp +0 -0
  213. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/tools/cli/main.cpp +0 -0
  214. {openscad_cpp_evaluator-1.5.0 → openscad_cpp_evaluator-1.6.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.5.0
3
+ Version: 1.6.1
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -152,7 +152,20 @@ struct IterList {
152
152
  // (pre-redesign) behavior exactly.
153
153
  struct VmFrame {
154
154
  const CompiledChunk* chunk = nullptr;
155
- const std::vector<Instruction>* code = nullptr;
155
+ // This frame's instruction stream, pre-decomposed into data()/size().
156
+ // driveVm's dispatch loop reads both once per INSTRUCTION; when this was a
157
+ // `const std::vector<Instruction>*`, that cost a pointer chase plus an
158
+ // end-minus-begin divided by sizeof(Instruction) (a multiply by a magic
159
+ // constant) on every opcode. ~11% of a whole Anklet.scad render sat in that
160
+ // loop preamble. A compiled chunk's code vector is immutable for as long as
161
+ // any frame runs it, so caching these is free -- but they must only ever be
162
+ // set together, hence setCode() rather than two public fields to forget.
163
+ const Instruction* code = nullptr;
164
+ size_t codeSize = 0;
165
+ void setCode(const std::vector<Instruction>& c) {
166
+ code = c.data();
167
+ codeSize = c.size();
168
+ }
156
169
  size_t pc = 0;
157
170
  std::vector<Value> slots;
158
171
  std::vector<Value> stack;
@@ -189,6 +202,16 @@ struct VmFrame {
189
202
  // brackets first", normally via matched Push/Pop, or via
190
203
  // teardownVmCallStackDownTo on the exception path) already covers it.
191
204
  std::vector<PendingCsgWrap> csgWrapStack;
205
+ // Scratch CallArgs for this frame's builtin/import call sites, reused
206
+ // across calls so its two vectors keep their capacity: building a fresh
207
+ // one per call was 1.99M of one Anklet.scad render's 10M allocations,
208
+ // second only to the argument vector this frame's operand stack now
209
+ // stands in for. One per FRAME rather than one per Evaluator because a
210
+ // builtin can re-enter the VM -- but only by pushing a new frame, which
211
+ // brings its own scratch, and a frame never has two call opcodes in
212
+ // flight at once. Frames themselves are pooled (acquireVmFrame), so the
213
+ // capacity survives the call that grew it.
214
+ CallArgs argScratch;
192
215
  // The ORIGINAL callee name at push time, used by
193
216
  // Evaluator::exitUserCallSuccess's own returnHook call when this frame
194
217
  // carries a bracket -- deliberately NOT updated by a later tail hop
@@ -97,4 +97,14 @@ const oscad::Expression* argExpr(const oscad::Argument& arg);
97
97
  // positional argument, not a fixed named slot.
98
98
  std::vector<Value> allPositional(const CallArgs& args);
99
99
 
100
+ // allPositional()'s size and element access, without building the vector.
101
+ // The argument CHECKERS (function_builtins.cpp) only ever ask how many
102
+ // positional slots there are and look at a couple of them, so they used to
103
+ // pay a heap allocation plus a Value copy per slot on every builtin call
104
+ // that has a check entry -- 1.97M of the 16.6M allocations in one
105
+ // Anklet.scad render. positionalAt() answers undef for a gap, exactly as
106
+ // allPositional() pads one.
107
+ size_t positionalCount(const CallArgs& args);
108
+ const Value& positionalAt(const CallArgs& args, size_t pos);
109
+
100
110
  } // namespace oscadeval
@@ -1812,7 +1812,26 @@ private:
1812
1812
  // its (possibly many) active occurrences is checked, checking each
1813
1813
  // DISTINCT X once is exactly equivalent to checking every occurrence
1814
1814
  // -- not an approximation.
1815
- std::unordered_map<const oscad::ASTNode*, int> activeDeclRefcount_;
1815
+ // A flat vector, not a map, for the reason the paragraph above already
1816
+ // establishes: the number of DISTINCT active declarations stays small and
1817
+ // roughly constant, so a linear scan beats hashing -- and an
1818
+ // unordered_map cost one node allocation per user call (595K in one
1819
+ // Anklet.scad render) where this costs none after warmup, while the
1820
+ // every-call containment scan above walks contiguous memory instead of a
1821
+ // hash table's buckets. Same flat-vector-over-small-map trade CallArgs and
1822
+ // BoundArgs already make. Entry order is not meaningful (the scan visits
1823
+ // all of them), so removal swaps with the back.
1824
+ std::vector<std::pair<const oscad::ASTNode*, int>> activeDeclRefcount_;
1825
+ // ++count for `decl`, appending an entry if it has none yet.
1826
+ void noteActiveDeclEnter(const oscad::ASTNode* declNode) {
1827
+ for (auto& entry : activeDeclRefcount_) {
1828
+ if (entry.first == declNode) {
1829
+ ++entry.second;
1830
+ return;
1831
+ }
1832
+ }
1833
+ activeDeclRefcount_.emplace_back(declNode, 1);
1834
+ }
1816
1835
 
1817
1836
  // Shared cap on callStack_'s own size, checked by both
1818
1837
  // evalUserFunctionCore (below) and evalUserModule (user_calls.cpp) --
@@ -12,37 +12,101 @@
12
12
 
13
13
  namespace oscadeval {
14
14
 
15
- // A "binding trail" (the classic Prolog/WAM undo-log pattern): each
16
- // variable name owns a stack of (value, level) entries. Setting a name
17
- // pushes; entering a new scope opens a fresh `level`; exiting a scope pops
18
- // every entry tagged with that level, in O(bindings actually made at that
19
- // level) rather than O(whole-map-size) -- replacing EvalContext's previous
20
- // copy-the-whole-map-on-every-derivation design. Safe here specifically
21
- // because this codebase has no escaping closures: a FunctionLiteral value
22
- // is a bare AST pointer with no captured environment, and callCtxFor()
23
- // re-derives every call's context from the LIVE call stack -- nothing ever
24
- // reads a binding after the scope that pushed it has already exited.
15
+ // Recycles the blocks that TrailView/IndexedTrailView's own shared_ptrs are
16
+ // made of. Deriving one EvalContext opens three trail levels -- dyn, let_,
17
+ // dynPositions -- and each was a fresh make_shared: ~2M allocations in one
18
+ // Anklet.scad render, all of them the same size, almost all of them freed in
19
+ // reverse order a few microseconds later. This hands the same block straight
20
+ // back out.
25
21
  //
26
- // Visibility is by TRUE ANCESTRY, not by level-number comparison: each
27
- // level records its own PARENT level (the level that was current when it
28
- // was opened, or 0 -- meaning "no parent" -- for an isolating derivation,
29
- // see TrailView::openChild). A view at level L can see an entry pushed at
30
- // level E iff E appears on L's own parent chain (L, parent(L), parent(
31
- // parent(L)), ... down to 0). This was NOT the original design here: an
32
- // earlier version used a single numeric "ceiling" (visible iff E <= L),
33
- // which is necessary but not sufficient -- it incorrectly treated any
34
- // chronologically-earlier-but-still-open level as an ancestor, even an
35
- // unrelated sibling/isolated scope that happens to still be on the call
36
- // stack. Caught on a real script (BOSL2's attachable()/trapezoid(), which
37
- // deep-forwards children() through several more calls while attachable()
38
- // itself -- an ISOLATED scope with its own same-named "path"/"h"
39
- // parameters, still open throughout -- sits on the stack): the ceiling-
40
- // only check found attachable()'s own unbound "path"/"h" (undef) instead
41
- // of trapezoid()'s real ones, since level-number order alone can't tell
42
- // "ancestor" from "unrelated open branch." Parent-chain walking fixes this
43
- // by construction. See eval_context.hpp for how each derivation method
44
- // picks its own level and (via `isolate`) whether the parent chain
45
- // continues to the caller or terminates there.
22
+ // It changes WHERE the memory comes from and nothing else: no lifetime, no
23
+ // visibility, no popLevel timing. That is the whole point -- the trail's
24
+ // escaping-closure rules are the most delicate thing in this file, and an
25
+ // allocator cannot get them wrong.
26
+ //
27
+ // One size class, because there is exactly one: every block is the control
28
+ // block plus one view object. Anything else falls through to the global
29
+ // allocator rather than being kept.
30
+ //
31
+ // Worth ~1% of an Anklet.scad render, for 2M allocations removed -- a modest
32
+ // return, and deliberately recorded here as such: macOS's own small-object
33
+ // allocator is already a free list, so swapping it for another one only pays
34
+ // for the bookkeeping either side of it. Removing an allocation that also
35
+ // removed real work (a vector's worth of Value moves, say) has paid far
36
+ // better everywhere it has been tried in this codebase.
37
+ class ViewBlockPool {
38
+ public:
39
+ ViewBlockPool() { free_.reserve(kMaxKept); }
40
+ ~ViewBlockPool() {
41
+ for (void* p : free_) ::operator delete(p);
42
+ }
43
+ ViewBlockPool(const ViewBlockPool&) = delete;
44
+ ViewBlockPool& operator=(const ViewBlockPool&) = delete;
45
+
46
+ void* take(std::size_t bytes) {
47
+ if (bytes == blockSize_ && !free_.empty()) {
48
+ void* p = free_.back();
49
+ free_.pop_back();
50
+ return p;
51
+ }
52
+ if (blockSize_ == 0) blockSize_ = bytes;
53
+ return ::operator new(bytes);
54
+ }
55
+ // noexcept is safe: free_ was reserved to kMaxKept up front and is never
56
+ // pushed past it, so this push_back cannot reallocate and cannot throw.
57
+ void give(void* p, std::size_t bytes) noexcept {
58
+ if (bytes != blockSize_ || free_.size() >= kMaxKept) {
59
+ ::operator delete(p);
60
+ return;
61
+ }
62
+ free_.push_back(p);
63
+ }
64
+
65
+ private:
66
+ static constexpr std::size_t kMaxKept = 512;
67
+ std::size_t blockSize_ = 0;
68
+ std::vector<void*> free_;
69
+ };
70
+
71
+ // A STATELESS allocator over one pool per view type (each type has exactly
72
+ // one block size, so each gets its own single-size-class pool). Stateless
73
+ // matters twice over: std::allocate_shared stores a copy of the allocator
74
+ // inside the very block it allocated, so a stateful one both grows every
75
+ // block and -- if it held a shared_ptr to keep the pool alive past the last
76
+ // view, which a pool owned by the storage would need -- pays an atomic
77
+ // refcount pair per allocation. That version was measured, and it was SLOWER
78
+ // than not pooling at all.
79
+ //
80
+ // thread_local, so no locking and no cross-thread sharing, and it outlives
81
+ // every view on its thread, which is what makes "keep the pool alive for the
82
+ // final deallocate" a non-problem rather than a lifetime puzzle.
83
+ template <typename T>
84
+ class ViewBlockAllocator {
85
+ public:
86
+ using value_type = T;
87
+ ViewBlockAllocator() = default;
88
+ template <typename U>
89
+ ViewBlockAllocator(const ViewBlockAllocator<U>&) {}
90
+
91
+ T* allocate(std::size_t n) { return static_cast<T*>(pool().take(n * sizeof(T))); }
92
+ void deallocate(T* p, std::size_t n) noexcept { pool().give(p, n * sizeof(T)); }
93
+
94
+ template <typename U>
95
+ bool operator==(const ViewBlockAllocator<U>&) const noexcept {
96
+ return true;
97
+ }
98
+ template <typename U>
99
+ bool operator!=(const ViewBlockAllocator<U>&) const noexcept {
100
+ return false;
101
+ }
102
+
103
+ private:
104
+ static ViewBlockPool& pool() {
105
+ static thread_local ViewBlockPool p;
106
+ return p;
107
+ }
108
+ };
109
+
46
110
  template <typename T>
47
111
  class ScopeTrailStorage {
48
112
  public:
@@ -50,7 +114,7 @@ public:
50
114
  // ancestry chain here (an isolating derivation -- see openChild).
51
115
  int openLevel(int parentLevel) {
52
116
  int level = ++nextLevel_;
53
- parent_[level] = parentLevel;
117
+ parent_.push_back(parentLevel); // index == level; see parent_'s own comment
54
118
  return level;
55
119
  }
56
120
 
@@ -122,7 +186,7 @@ public:
122
186
  }
123
187
  dirty_.erase(it);
124
188
  }
125
- parent_.erase(level);
189
+ parent_[static_cast<size_t>(level)] = 0; // dead: an ancestry walk stops here
126
190
  }
127
191
 
128
192
  // nullptr if `name` has no binding on `myLevel`'s own ancestry chain.
@@ -154,8 +218,7 @@ public:
154
218
  } else if (entryLevel == ancestor) {
155
219
  return &vec[static_cast<size_t>(idx)].value;
156
220
  } else {
157
- auto pit = parent_.find(ancestor);
158
- ancestor = pit != parent_.end() ? pit->second : 0;
221
+ ancestor = parent_[static_cast<size_t>(ancestor)];
159
222
  }
160
223
  }
161
224
  return nullptr;
@@ -194,8 +257,21 @@ private:
194
257
  };
195
258
  std::unordered_map<std::string, std::vector<Entry>> stacks_;
196
259
  std::unordered_map<int, std::vector<std::string>> dirty_;
197
- std::unordered_map<int, int> parent_; // level -> parent level (0 = chain terminates here)
260
+ // level -> parent level (0 = chain terminates here, which is also what a
261
+ // popped level reads back as). Levels come from `++nextLevel_`, so they are
262
+ // dense and monotonic and this is a plain vector indexed BY level, with
263
+ // element 0 the unused stand-in for "no parent" -- push_back in openLevel()
264
+ // keeps index == level. It used to be an unordered_map<int,int>, which cost
265
+ // one hash-node allocation per scope derivation (3 of the 10 allocations a
266
+ // user function call makes, ~2M of them in one Anklet.scad render) and made
267
+ // lookup()'s ancestry walk -- run on every variable read -- a hash probe per
268
+ // hop instead of an array index. It never shrinks: 4 bytes per level ever
269
+ // opened (~2.6MB for Anklet.scad), traded knowingly for the allocation.
270
+ // Levels are NEVER renumbered or reused -- lookup()'s sorted-merge walk
271
+ // depends on level numbers being globally monotonic.
272
+ std::vector<int> parent_{0};
198
273
  int nextLevel_ = 0;
274
+
199
275
  };
200
276
 
201
277
  // Per-EvalContext handle onto a shared ScopeTrailStorage<T>: this view's
@@ -224,7 +300,8 @@ public:
224
300
  static std::shared_ptr<TrailView<T>> makeRoot() {
225
301
  auto storage = std::make_shared<ScopeTrailStorage<T>>();
226
302
  int level = storage->openLevel(0);
227
- return std::make_shared<TrailView<T>>(storage, level);
303
+ return std::allocate_shared<TrailView<T>>(ViewBlockAllocator<TrailView<T>>(), storage,
304
+ level);
228
305
  }
229
306
 
230
307
  // Open a fresh level for writes. `isolate=true` terminates the new
@@ -249,7 +326,7 @@ public:
249
326
  // call returns; ~TrailView() pops ITS bindings from ScopeTrailStorage
250
327
  // (erasing the very data the still-alive child's ancestry walk needs)
251
328
  // even though the walk can still numerically reach that ancestor level
252
- // via ScopeTrailStorage::parent_ (that map entry is untouched -- only
329
+ // via ScopeTrailStorage::parent_ (that entry is untouched -- only
253
330
  // the DATA is gone). Caught by a closure created inside a let()
254
331
  // nested one level inside its own enclosing call reading undef for a
255
332
  // variable bound at the call's own (now-popped) level, not the let()'s
@@ -261,7 +338,8 @@ public:
261
338
  // changes what happens when something holds an EXTRA reference.
262
339
  std::shared_ptr<TrailView<T>> openChild(bool isolate) const {
263
340
  int level = storage_->openLevel(isolate ? 0 : level_);
264
- auto child = std::make_shared<TrailView<T>>(storage_, level);
341
+ auto child = std::allocate_shared<TrailView<T>>(ViewBlockAllocator<TrailView<T>>(),
342
+ storage_, level);
265
343
  if (!isolate) child->parentView_ = this->shared_from_this();
266
344
  return child;
267
345
  }
@@ -344,7 +422,7 @@ public:
344
422
 
345
423
  int openLevel(int parentLevel) {
346
424
  int level = ++nextLevel_;
347
- parent_[level] = parentLevel;
425
+ parent_.push_back(parentLevel); // index == level; see parent_'s own comment
348
426
  return level;
349
427
  }
350
428
 
@@ -395,7 +473,7 @@ public:
395
473
  }
396
474
  dirty_.erase(it);
397
475
  }
398
- parent_.erase(level);
476
+ parent_[static_cast<size_t>(level)] = 0; // dead: an ancestry walk stops here
399
477
  }
400
478
 
401
479
  // Same sorted-merge ancestry walk as ScopeTrailStorage::lookup -- see
@@ -414,8 +492,7 @@ public:
414
492
  } else if (entryLevel == ancestor) {
415
493
  return &vec[static_cast<size_t>(idx)].value;
416
494
  } else {
417
- auto pit = parent_.find(ancestor);
418
- ancestor = pit != parent_.end() ? pit->second : 0;
495
+ ancestor = parent_[static_cast<size_t>(ancestor)];
419
496
  }
420
497
  }
421
498
  return nullptr;
@@ -450,8 +527,9 @@ private:
450
527
  std::shared_ptr<DynNameIntern> intern_;
451
528
  std::vector<std::vector<Entry>> stacks_;
452
529
  std::unordered_map<int, std::vector<int>> dirty_;
453
- std::unordered_map<int, int> parent_;
530
+ std::vector<int> parent_{0}; // see ScopeTrailStorage::parent_
454
531
  int nextLevel_ = 0;
532
+
455
533
  };
456
534
 
457
535
  // Per-EvalContext handle onto a shared IndexedScopeTrailStorage<T> -- the
@@ -464,12 +542,14 @@ public:
464
542
  static std::shared_ptr<IndexedTrailView<T>> makeRoot(std::shared_ptr<DynNameIntern> intern) {
465
543
  auto storage = std::make_shared<IndexedScopeTrailStorage<T>>(std::move(intern));
466
544
  int level = storage->openLevel(0);
467
- return std::make_shared<IndexedTrailView<T>>(storage, level);
545
+ return std::allocate_shared<IndexedTrailView<T>>(
546
+ ViewBlockAllocator<IndexedTrailView<T>>(), storage, level);
468
547
  }
469
548
 
470
549
  std::shared_ptr<IndexedTrailView<T>> openChild(bool isolate) const {
471
550
  int level = storage_->openLevel(isolate ? 0 : level_);
472
- return std::make_shared<IndexedTrailView<T>>(storage_, level);
551
+ return std::allocate_shared<IndexedTrailView<T>>(
552
+ ViewBlockAllocator<IndexedTrailView<T>>(), storage_, level);
473
553
  }
474
554
 
475
555
  IndexedTrailView(std::shared_ptr<IndexedScopeTrailStorage<T>> storage, int level)
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.5.0"
7
+ version = "1.6.1"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -106,6 +106,18 @@ const oscad::Expression* argExpr(const oscad::Argument& arg) {
106
106
  return static_cast<const oscad::PositionalArgument&>(arg).expr.get();
107
107
  }
108
108
 
109
+ size_t positionalCount(const CallArgs& args) {
110
+ int maxPos = -1;
111
+ for (const auto& [idx, v] : args.positional) maxPos = std::max(maxPos, idx);
112
+ return static_cast<size_t>(maxPos + 1);
113
+ }
114
+
115
+ const Value& positionalAt(const CallArgs& args, size_t pos) {
116
+ static const Value kUndef;
117
+ const Value* v = args.findPositional(static_cast<int>(pos));
118
+ return v ? *v : kUndef;
119
+ }
120
+
109
121
  std::vector<Value> allPositional(const CallArgs& args) {
110
122
  int maxPos = -1;
111
123
  for (const auto& [idx, v] : args.positional) maxPos = std::max(maxPos, idx);
@@ -1179,15 +1179,15 @@ void warnConversion(Evaluator& ev, const std::string& name, const std::string& w
1179
1179
  // numbers, with a distinct "at least 1 vector element" arity text for the
1180
1180
  // empty-vector case.
1181
1181
  bool checkMinMax(Evaluator& ev, const std::string& name, const CallArgs& args, const oscad::Position* pos) {
1182
- const std::vector<Value> positional = allPositional(args);
1183
- const size_t count = positional.size() + args.named.size();
1182
+ const size_t nPos = positionalCount(args);
1183
+ const size_t count = nPos + args.named.size();
1184
1184
  if (count < 1) {
1185
1185
  warnArity(ev, name, "at least 1", count, pos);
1186
1186
  return false;
1187
1187
  }
1188
- if (positional.size() == 1 && std::holds_alternative<ListPtr>(positional[0])) {
1188
+ if (nPos == 1 && std::holds_alternative<ListPtr>(positionalAt(args, 0))) {
1189
1189
  static const std::vector<Value> kEmptyItems;
1190
- const ListPtr& l = std::get<ListPtr>(positional[0]);
1190
+ const ListPtr& l = std::get<ListPtr>(positionalAt(args, 0));
1191
1191
  const std::vector<Value>& items = l ? l->items : kEmptyItems;
1192
1192
  if (items.empty()) {
1193
1193
  warnArity(ev, name, "at least 1 vector element", 0, pos);
@@ -1201,9 +1201,9 @@ bool checkMinMax(Evaluator& ev, const std::string& name, const CallArgs& args, c
1201
1201
  }
1202
1202
  return true;
1203
1203
  }
1204
- for (size_t i = 0; i < positional.size(); ++i) {
1205
- if (!std::holds_alternative<double>(positional[i])) {
1206
- warnConversion(ev, name, "argument " + std::to_string(i), "number", positional[i], pos);
1204
+ for (size_t i = 0; i < nPos; ++i) {
1205
+ if (!std::holds_alternative<double>(positionalAt(args, i))) {
1206
+ warnConversion(ev, name, "argument " + std::to_string(i), "number", positionalAt(args, i), pos);
1207
1207
  return false;
1208
1208
  }
1209
1209
  }
@@ -1219,8 +1219,8 @@ bool checkBuiltinArgs(Evaluator& ev, const std::string& name, BuiltinFnId id, co
1219
1219
  if (it == checks.end()) return true;
1220
1220
  const BuiltinCheck& c = it->second;
1221
1221
 
1222
- const std::vector<Value> positional = allPositional(args);
1223
- const size_t count = positional.size() + args.named.size();
1222
+ const size_t nPos = positionalCount(args);
1223
+ const size_t count = nPos + args.named.size();
1224
1224
  if (c.arityText && (static_cast<int>(count) < c.minArgs || (c.maxArgs >= 0 && static_cast<int>(count) > c.maxArgs))) {
1225
1225
  warnArity(ev, name, c.arityText, count, pos);
1226
1226
  return false;
@@ -1229,9 +1229,10 @@ bool checkBuiltinArgs(Evaluator& ev, const std::string& name, BuiltinFnId id, co
1229
1229
  // flat argument list can't be reconstructed from our split
1230
1230
  // positional/named form, and warning on the wrong index would be worse
1231
1231
  // than staying quiet on a spelling nobody uses for these builtins.
1232
- for (size_t i = 0; i < c.types.size() && i < positional.size(); ++i) {
1233
- if (!(typeBit(positional[i]) & c.types[i].allowed)) {
1234
- warnConversion(ev, name, "argument " + std::to_string(i), c.types[i].expected, positional[i], pos);
1232
+ for (size_t i = 0; i < c.types.size() && i < nPos; ++i) {
1233
+ const Value& v = positionalAt(args, i);
1234
+ if (!(typeBit(v) & c.types[i].allowed)) {
1235
+ warnConversion(ev, name, "argument " + std::to_string(i), c.types[i].expected, v, pos);
1235
1236
  return false;
1236
1237
  }
1237
1238
  }