openscad-cpp-evaluator 0.16.0__tar.gz → 0.21.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/bindings/module.cpp +27 -3
  3. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +21 -0
  4. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/lexer.l +5 -1
  5. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +73 -0
  6. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/csg_node.hpp +16 -0
  7. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/eval_context.hpp +15 -0
  8. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/eval_error.hpp +12 -0
  9. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/evaluator.hpp +59 -2
  10. openscad_cpp_evaluator-0.21.0/include/openscad_cpp_evaluator/profile.hpp +81 -0
  11. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/pyproject.toml +1 -1
  12. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/python/openscad_cpp_evaluator/__init__.py +20 -2
  13. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/bytecode_vm.cpp +2 -0
  14. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/csg_generate.cpp +7 -0
  15. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/csg_resolve.cpp +6 -1
  16. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/debug_profile.cpp +103 -3
  17. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/eval_context.cpp +1 -0
  18. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/eval_error.cpp +44 -0
  19. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/expr_eval.cpp +19 -1
  20. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/user_calls.cpp +14 -1
  21. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_bytecode_compiler.cpp +11 -3
  22. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_cli.cpp +1 -1
  23. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_csg_tree.cpp +70 -0
  24. openscad_cpp_evaluator-0.21.0/tests/test_profiling.cpp +344 -0
  25. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/cli_lib.cpp +8 -5
  26. openscad_cpp_evaluator-0.16.0/include/openscad_cpp_evaluator/profile.hpp +0 -43
  27. openscad_cpp_evaluator-0.16.0/tests/test_profiling.cpp +0 -127
  28. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.github/workflows/ci.yml +0 -0
  29. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.github/workflows/wheels.yml +0 -0
  30. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.gitignore +0 -0
  31. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/.gitmodules +0 -0
  32. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/CLAUDE.md +0 -0
  33. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/CMakeLists.txt +0 -0
  34. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/LICENSE +0 -0
  35. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/README.md +0 -0
  36. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/bindings/CMakeLists.txt +0 -0
  37. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/bindings/ast_to_py.cpp +0 -0
  38. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/cmake/embed_font.cmake +0 -0
  39. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/CMakeLists.txt +0 -0
  40. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/eval_perf_benchmark.cpp +0 -0
  41. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/manifold_cache_reuse.cpp +0 -0
  42. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/examples/minimal_debugger.cpp +0 -0
  43. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  44. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/.gitignore +0 -0
  45. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  46. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  47. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/README.md +0 -0
  48. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  49. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  50. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  51. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  52. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  53. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  54. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  55. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  56. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  57. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  58. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  59. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  60. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  61. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  62. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  63. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  64. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  65. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  66. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  67. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  68. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  69. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  70. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  71. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  72. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  73. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  74. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  75. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  76. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  77. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  78. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  79. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  80. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  81. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  82. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  83. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  84. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  85. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  86. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  87. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  88. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  89. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  90. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  91. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  92. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  93. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  94. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  95. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  96. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  97. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  98. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  99. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  100. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  101. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  102. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  103. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  104. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  105. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  106. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  107. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  108. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  109. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  110. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  111. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  112. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  113. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  114. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  115. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  116. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  117. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  118. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  119. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  120. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  121. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  122. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  123. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  124. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  125. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  126. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  127. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  128. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  129. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  130. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  131. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  132. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/resources/fonts/LICENSE +0 -0
  133. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  134. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/CMakeLists.txt +0 -0
  135. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/booleans.cpp +0 -0
  136. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/builtins.hpp +0 -0
  137. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/call_args.cpp +0 -0
  138. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/color.cpp +0 -0
  139. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/control.cpp +0 -0
  140. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/extrude.cpp +0 -0
  141. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/function_builtins.cpp +0 -0
  142. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/import.cpp +0 -0
  143. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/modifiers.cpp +0 -0
  144. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/primitives_2d.cpp +0 -0
  145. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/primitives_3d.cpp +0 -0
  146. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/registry.cpp +0 -0
  147. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/roof.cpp +0 -0
  148. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/segments.cpp +0 -0
  149. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/surface.cpp +0 -0
  150. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/text.cpp +0 -0
  151. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/topology.cpp +0 -0
  152. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/builtins/transforms.cpp +0 -0
  153. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/bytecode_compiler.cpp +0 -0
  154. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/colored_body.cpp +0 -0
  155. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/css_colors.cpp +0 -0
  156. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/debug/debug_repl.cpp +0 -0
  157. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/eval_use.cpp +0 -0
  158. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/export.cpp +0 -0
  159. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/dxf_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/mesh_import.cpp +0 -0
  161. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/surface_load.cpp +0 -0
  162. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/import/svg_import.cpp +0 -0
  163. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/manifold_cache.cpp +0 -0
  164. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/stmt_eval.cpp +0 -0
  165. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/text/stb_font_provider.cpp +0 -0
  166. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/text/text_metrics.cpp +0 -0
  167. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/value.cpp +0 -0
  168. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/src/zip_stored.cpp +0 -0
  169. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/CMakeLists.txt +0 -0
  170. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_booleans.cpp +0 -0
  171. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_control_flow.cpp +0 -0
  172. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_debug_hooks.cpp +0 -0
  173. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_dxf_svg_import.cpp +0 -0
  174. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_eval_context.cpp +0 -0
  175. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_export.cpp +0 -0
  176. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_expr_eval.cpp +0 -0
  177. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_extrude_roof.cpp +0 -0
  178. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_function_builtins.cpp +0 -0
  179. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_helpers.hpp +0 -0
  180. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_import_export.cpp +0 -0
  181. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_manifold_cache.cpp +0 -0
  182. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_multi_color_merge.cpp +0 -0
  183. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_primitives.cpp +0 -0
  184. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_python_bindings.py +0 -0
  185. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_scope_trail.cpp +0 -0
  186. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_scoping.cpp +0 -0
  187. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_surface.cpp +0 -0
  188. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_tail_calls.cpp +0 -0
  189. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_text.cpp +0 -0
  190. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_transforms.cpp +0 -0
  191. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_use.cpp +0 -0
  192. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_value.cpp +0 -0
  193. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_viewport_params.cpp +0 -0
  194. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tests/test_zip_stored.cpp +0 -0
  195. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/CMakeLists.txt +0 -0
  196. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/cli_lib.hpp +0 -0
  197. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/tools/cli/main.cpp +0 -0
  198. {openscad_cpp_evaluator-0.16.0 → openscad_cpp_evaluator-0.21.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.16.0
3
+ Version: 0.21.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -246,10 +246,34 @@ nb::object profileResultToPy(const std::optional<oscadeval::ProfileResult>& pr)
246
246
  if (!pr) return nb::none();
247
247
  nb::list sites;
248
248
  for (const oscadeval::CallSiteProfile& s : pr->callSites) {
249
- sites.append(facadeAttr("CallSiteProfile")(s.kind, s.name, s.callerName, s.callOrigin, s.callLine, s.declOrigin,
250
- s.declLine, s.callCount, s.selfTime, s.cumulativeTime));
249
+ sites.append(facadeAttr("CallSiteProfile")(s.kind, s.name, s.callerName, s.callOrigin, s.callLine, s.callColumn,
250
+ s.declOrigin, s.declLine, s.callCount, s.selfTime, s.cumulativeTime));
251
251
  }
252
- return facadeAttr("ProfileResult")(sites, pr->resolveTime, pr->generateTime, pr->totalTime, pr->unattributedTime);
252
+ // The calling-context tree, as plain dicts. Parent/child INDICES, not
253
+ // nested objects: the C++ side is already a flat vector keyed that way,
254
+ // and a self-referential nesting would have to be rebuilt here for no
255
+ // gain -- a consumer walks it by index just as C++ does.
256
+ nb::list paths;
257
+ for (const oscadeval::ProfilePathNode& n : pr->paths) {
258
+ nb::dict d;
259
+ d["parent"] = n.parent;
260
+ nb::list kids;
261
+ for (int c : n.children) kids.append(c);
262
+ d["children"] = kids;
263
+ d["kind"] = n.kind;
264
+ d["name"] = n.name;
265
+ d["call_origin"] = n.callOrigin;
266
+ d["call_line"] = n.callLine;
267
+ d["call_column"] = n.callColumn;
268
+ d["decl_origin"] = n.declOrigin;
269
+ d["decl_line"] = n.declLine;
270
+ d["call_count"] = n.callCount;
271
+ d["self_time"] = n.selfTime;
272
+ d["cumulative_time"] = n.cumulativeTime;
273
+ paths.append(d);
274
+ }
275
+ return facadeAttr("ProfileResult")(sites, pr->resolveTime, pr->generateTime, pr->totalTime, pr->unattributedTime,
276
+ paths);
253
277
  }
254
278
 
255
279
  // ctx.dyn / ctx.dynExplicit -> (dict[str, Any], set[str]) -- every currently-
@@ -49,6 +49,18 @@ public:
49
49
  LexPos tokenEnd;
50
50
  std::string stringBuffer; // accumulates a STRING token's content while in the %x STR lexer state
51
51
 
52
+ // Where the opening quote of the STRING currently being lexed began.
53
+ //
54
+ // Needed because YY_USER_ACTION resets tokenStart on EVERY rule match,
55
+ // and a string is matched by several rules (opening quote, content
56
+ // runs, escapes, closing quote). currentTokenLoc() at the closing
57
+ // quote therefore describes just that one character, which made every
58
+ // StringLiteral's source span a single `"` -- and dragged the span of
59
+ // anything wrapping it (a PositionalArgument, a vector element) along
60
+ // with it. Recorded when the opening quote is matched and used by
61
+ // stringTokenLoc() below.
62
+ LexPos stringStart;
63
+
52
64
  Position toPosition(const OscadLocation& loc) const {
53
65
  return Position{origin_, loc.first_line, loc.first_column, loc.first_offset, loc.last_offset};
54
66
  }
@@ -58,6 +70,15 @@ public:
58
70
  tokenEnd.column, tokenStart.offset, tokenEnd.offset};
59
71
  }
60
72
 
73
+ // As currentTokenLoc(), but spanning from the string's OPENING quote
74
+ // (stringStart) to the current token's end -- i.e. the whole literal
75
+ // including both quotes, which is what a caller slicing the source by
76
+ // this span expects to get back.
77
+ OscadLocation stringTokenLoc() const {
78
+ return OscadLocation{stringStart.line, stringStart.column, tokenEnd.line,
79
+ tokenEnd.column, stringStart.offset, tokenEnd.offset};
80
+ }
81
+
61
82
  void reportError(const OscadLocation& loc, const std::string& message);
62
83
  };
63
84
 
@@ -93,13 +93,17 @@ NUMLIT 0[xX][0-9A-Fa-f]+|[0-9]+([.][0-9]*)?([eE][+-]?[0-9]+)?|[.][0-9]+([eE][+-
93
93
 
94
94
  \" {
95
95
  driver.stringBuffer.clear();
96
+ // YY_USER_ACTION has just set tokenStart to this opening quote; keep
97
+ // it, because the rules that follow will overwrite it (see
98
+ // ParserDriver::stringStart).
99
+ driver.stringStart = driver.tokenStart;
96
100
  BEGIN(STR);
97
101
  }
98
102
  <STR>\\. { driver.stringBuffer.append(yytext, yyleng); }
99
103
  <STR>[^"\\]+ { driver.stringBuffer.append(yytext, yyleng); }
100
104
  <STR>\" {
101
105
  BEGIN(INITIAL);
102
- return yy::parser::make_STRING(driver.stringBuffer, driver.currentTokenLoc());
106
+ return yy::parser::make_STRING(driver.stringBuffer, driver.stringTokenLoc());
103
107
  }
104
108
 
105
109
  <INCFILE>[ \t\r\n]+ { /* skip whitespace before < */ }
@@ -363,3 +363,76 @@ TEST(LexicalIdentifiers, IdentifierStr) {
363
363
  std::vector<std::unique_ptr<ASTNode>> ast;
364
364
  EXPECT_EQ(exprSrc("foo", ast)->toString(), "foo");
365
365
  }
366
+
367
+ // -- Source spans ---------------------------------------------------------
368
+
369
+ namespace {
370
+ // The source text a node's own position spans -- what a consumer slicing
371
+ // by start/end_offset actually gets back.
372
+ std::string spanned(const std::string& src, const oscad::ASTNode& n) {
373
+ const auto& p = n.position();
374
+ return src.substr(static_cast<size_t>(p.start_offset),
375
+ static_cast<size_t>(p.end_offset - p.start_offset));
376
+ }
377
+ } // namespace
378
+
379
+ // A string is matched by SEVERAL lexer rules (opening quote, content runs,
380
+ // escapes, closing quote), and YY_USER_ACTION resets tokenStart on every
381
+ // one of them -- so building the token's location from currentTokenLoc() at
382
+ // the closing quote described just that one character. Every StringLiteral
383
+ // spanned a bare `"`, and so did anything wrapping it, which silently
384
+ // corrupted a downstream consumer that sliced source by those offsets.
385
+ TEST(SourceSpans, StringLiteralSpansTheWholeLiteral) {
386
+ struct Case { std::string src; std::string want; };
387
+ const Case cases[] = {
388
+ {"a = \"txt\";", "\"txt\""},
389
+ {"a = \"\";", "\"\""}, // empty
390
+ {"a = \"with \\\"quote\\\" in it\";", "\"with \\\"quote\\\" in it\""},
391
+ {"a = \"esc \\n seq\";", "\"esc \\n seq\""},
392
+ {"a = \"comma,inside\";", "\"comma,inside\""},
393
+ };
394
+ for (const Case& c : cases) {
395
+ auto ast = oscad::parseSrc(c.src);
396
+ ASSERT_EQ(ast.size(), 1u) << c.src;
397
+ auto* assign = dynamic_cast<oscad::Assignment*>(ast[0].get());
398
+ ASSERT_NE(assign, nullptr) << c.src;
399
+ EXPECT_EQ(spanned(c.src, *assign->expr), c.want) << c.src;
400
+ }
401
+ }
402
+
403
+ // The enclosing node's span has to be right too -- that is how the bug
404
+ // actually did damage, by dragging an argument's end_offset into the
405
+ // middle of the string.
406
+ TEST(SourceSpans, NodesWrappingAStringSpanCorrectly) {
407
+ const std::string src = "f(\"x,y\", b);";
408
+ auto ast = oscad::parseSrc(src);
409
+ auto* call = dynamic_cast<oscad::ModularCall*>(ast[0].get());
410
+ ASSERT_NE(call, nullptr);
411
+ ASSERT_EQ(call->arguments.size(), 2u);
412
+ EXPECT_EQ(spanned(src, *call->arguments[0]), "\"x,y\"");
413
+ EXPECT_EQ(spanned(src, *call->arguments[1]), "b");
414
+ }
415
+
416
+ TEST(SourceSpans, StringsInAVectorSpanCorrectly) {
417
+ const std::string src = "x = [\"a,b\",\"c\"];";
418
+ auto ast = oscad::parseSrc(src);
419
+ auto* assign = dynamic_cast<oscad::Assignment*>(ast[0].get());
420
+ ASSERT_NE(assign, nullptr);
421
+ auto* vec = dynamic_cast<oscad::ListComprehension*>(assign->expr.get());
422
+ ASSERT_NE(vec, nullptr);
423
+ ASSERT_EQ(vec->elements.size(), 2u);
424
+ EXPECT_EQ(spanned(src, *vec->elements[0]), "\"a,b\"");
425
+ EXPECT_EQ(spanned(src, *vec->elements[1]), "\"c\"");
426
+ }
427
+
428
+ // Multi-line strings must still report the OPENING quote's line/column, not
429
+ // the closing one's.
430
+ TEST(SourceSpans, MultiLineStringReportsItsStartingLine) {
431
+ const std::string src = "a = 1;\nb = \"one\ntwo\";\n";
432
+ auto ast = oscad::parseSrc(src);
433
+ ASSERT_EQ(ast.size(), 2u);
434
+ auto* assign = dynamic_cast<oscad::Assignment*>(ast[1].get());
435
+ ASSERT_NE(assign, nullptr);
436
+ EXPECT_EQ(assign->expr->position().line, 2);
437
+ EXPECT_EQ(spanned(src, *assign->expr), "\"one\ntwo\"");
438
+ }
@@ -34,6 +34,22 @@ struct CSGNode {
34
34
  CSGParams params; // resolve step's plain-data output
35
35
  bool uncacheable = false; // set by a later phase (ManifoldCache, Phase 8)
36
36
 
37
+ // The call site that entered this node's call chain from the top
38
+ // level, captured at RESOLVE time -- non-owning, AST-lifetime-bound
39
+ // like `node`. nullptr for a node resolved at top level.
40
+ //
41
+ // Needed because a GenerateFn runs long after resolve has unwound the
42
+ // call stack, so a warning raised there (an open polyhedron(), a
43
+ // non-manifold import()) has no stack left to walk and could only ever
44
+ // name the library's own line. One pointer per node, deliberately, so
45
+ // this stays affordable on a BOSL2-scale tree; the full frame list a
46
+ // TRACE would need is not worth the per-node cost.
47
+ //
48
+ // Not part of cacheKey() (an explicit allowlist of kind/isBuiltin/
49
+ // params/children), so two identical subtrees reached from different
50
+ // call sites still share a cache entry.
51
+ const oscad::Position* warnEntry = nullptr;
52
+
37
53
  // 1 + the deepest child's own treeDepth (1 for a leaf) -- set once,
38
54
  // at construction, by whichever csg_resolve.cpp site finalizes this
39
55
  // node's own `children` (buildTreeNode, evalModularCall's non-splice
@@ -59,6 +59,21 @@ struct EvalContext {
59
59
  std::shared_ptr<const ChildrenNodeList> childrenNodes;
60
60
  const EvalContext* childrenCallerCtx = nullptr;
61
61
 
62
+ // True while evaluating statements that a children() forwarded, so a
63
+ // module call reached this way profiles as kind "child" rather than
64
+ // "module". `foo() foo();` and `module foo() { foo(); }` both produce a
65
+ // foo->foo edge, but only the second is recursion, and a profile that
66
+ // cannot tell them apart is misleading.
67
+ //
68
+ // Propagates through withScope/childCtx (a forwarded `translate()
69
+ // foo()` is still child-passing) but NOT through callCtx, which resets
70
+ // it by omission -- entering a body ends the forwarding. The one
71
+ // deliberate exception is buildModuleChildCtx, which copies the
72
+ // caller's flag onto the body ctx so enterUserCall can read it there;
73
+ // enterUserCall then clears it, so the call it describes is the only
74
+ // one that sees it.
75
+ bool viaChildren = false;
76
+
62
77
  // The one genuinely fresh construction: seeds `dyn` with OpenSCAD's
63
78
  // built-in $-variable defaults ($fn=0, $fa=12, $fs=2, $t=0,
64
79
  // $parent_modules=0). Every other EvalContext in a run is derived
@@ -117,4 +117,16 @@ std::string formatError(const std::string& msg, const oscad::Position* nodePosit
117
117
  const std::vector<CallStackFrame>& callStack,
118
118
  const std::string& innermostFrame = "");
119
119
 
120
+ // "WARNING: {msg}{locSuffix(nodePosition)}, from {entry file}, line {N}"
121
+ // followed by traceLines(). The "from" clause names the OUTERMOST frame's
122
+ // call site -- the user's own line that entered the call chain -- because
123
+ // a warning raised inside a library otherwise reports only the library's
124
+ // own line, which the reader can neither act on nor trace back.
125
+ //
126
+ // Omitted when the stack is empty (a top-level warning, the common case,
127
+ // which stays a single line) or when it would merely repeat the headline.
128
+ // Deliberately richer than real OpenSCAD, which prints neither.
129
+ std::string formatWarning(const std::string& msg, const oscad::Position* nodePosition,
130
+ const std::vector<CallStackFrame>& callStack);
131
+
120
132
  } // namespace oscadeval
@@ -234,6 +234,24 @@ public:
234
234
  // GenerateFn is a free function, not a method -- it needs to call this
235
235
  // on the Evaluator& it's handed (see dispatch.hpp's GenerateFn doc
236
236
  // comment for why that reference exists at all).
237
+ // The call site that entered the current call chain from the top
238
+ // level (callStack_ is outermost-first), or nullptr at top level.
239
+ // Captured into each CSGNode at resolve time so a warning raised
240
+ // during generate -- after the stack has unwound -- can still name the
241
+ // user's own line. See CSGNode::warnEntry.
242
+ const oscad::Position* currentWarnEntry() const {
243
+ return callStack_.empty() ? nullptr : callStack_.front().callPosition;
244
+ }
245
+
246
+ // Set by generateTreeImpl() to the CSGNode currently being generated,
247
+ // so warn() can name the user's own call site during a phase where
248
+ // callStack_ is necessarily empty. Public for the same reason
249
+ // tagGenerated() is: a GenerateFn is a free function holding only an
250
+ // Evaluator&. Read only by warn(); a plain scoped save/restore rather
251
+ // than anything reentrancy-aware, since generateTreeImpl recurses
252
+ // depth-first on one thread.
253
+ const oscad::Position* generateWarnEntry = nullptr;
254
+
237
255
  ColoredBody tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue);
238
256
 
239
257
  // tagGenerated()'s counterpart for a mesh Manifold refused to build --
@@ -1216,12 +1234,16 @@ private:
1216
1234
 
1217
1235
  // -- Profiling (Phase 9) --------------------------------------------
1218
1236
 
1219
- // (kind, name, callOrigin, callLine) -- the exact call-site identity a
1237
+ // (kind, name, callOrigin, callLine, callColumn) -- the exact call-site
1238
+ // identity a
1220
1239
  // CallSiteProfile aggregates by. std::map (not unordered_map): a plain
1221
1240
  // tuple of comparable fields already gets a free operator< from the
1222
1241
  // standard library, so this needs no custom hash function for a table
1223
1242
  // that's never more than a few thousand entries even on a large script.
1224
- using ProfileSiteKey = std::tuple<std::string, std::string, std::string, int>;
1243
+ // Column is part of the key, not decoration: `foo() foo();` and
1244
+ // `f(g(x))` put two distinct call sites on one line, and without it
1245
+ // they aggregate into one entry whose times belong to neither.
1246
+ using ProfileSiteKey = std::tuple<std::string, std::string, std::string, int, int>;
1225
1247
 
1226
1248
  // Pushes profiling state for a user module/function call about to
1227
1249
  // start -- shared by evalUserModule/evalUserFunction/
@@ -1234,6 +1256,13 @@ private:
1234
1256
  ProfileSiteKey key;
1235
1257
  bool recursiveReentry;
1236
1258
  std::chrono::steady_clock::time_point start;
1259
+ // Node this call occupies in the calling-context tree, and the one
1260
+ // to restore on exit. `pathRecursive` is the per-PATH equivalent of
1261
+ // recursiveReentry: true when this entry folded back onto a node
1262
+ // already on the path, so only the outermost entry adds cumulative
1263
+ // time -- the same rule callSites uses, applied per node.
1264
+ int pathNode = -1;
1265
+ int pathPrev = -1;
1237
1266
  };
1238
1267
  std::optional<ProfileHandle> profileEnter(const std::string& kind, const std::string& name,
1239
1268
  const oscad::Position* callPos, const oscad::Position* declPos);
@@ -1694,6 +1723,34 @@ private:
1694
1723
  std::map<ProfileSiteKey, CallSiteProfile> profileSites_;
1695
1724
  std::set<ProfileSiteKey> profileActive_; // site keys with a call currently on callStack_
1696
1725
  std::vector<double> profileChildTime_; // parallel aux stack to callStack_
1726
+
1727
+ // Calling-context tree (see ProfilePathNode). profilePaths_[0] is the
1728
+ // <toplevel> root, created lazily on the first profiled call;
1729
+ // profileCurrentPath_ is the node the running call belongs to, so a
1730
+ // profileEnter knows which parent to hang its node off.
1731
+ std::vector<ProfilePathNode> profilePaths_;
1732
+ int profileCurrentPath_ = -1;
1733
+
1734
+ // Safety valve. The tree is bounded by distinct ACYCLIC paths (see
1735
+ // ProfilePathNode on recursion folding), which is finite but can still
1736
+ // be large for a deeply-layered library. Past this, no new nodes are
1737
+ // created and further calls fold onto their parent -- the report stays
1738
+ // truthful about totals and simply stops subdividing, rather than the
1739
+ // profiler becoming the thing that runs out of memory.
1740
+ static constexpr size_t kMaxProfilePathNodes = 200000;
1741
+
1742
+ // Finds-or-creates the child of profileCurrentPath_ for this call site
1743
+ // and returns its index. Sets `folded` when the returned node was
1744
+ // already on the current path (recursion, or the node cap) rather than
1745
+ // a fresh or sibling child -- the caller must then skip cumulative
1746
+ // time for this entry, exactly as recursiveReentry does for callSites.
1747
+ int profilePathEnter(const std::string& kind, const std::string& name, const std::string& callOrigin,
1748
+ int callLine, int callColumn, const oscad::Position* declPos, bool& folded);
1749
+
1750
+ // Fills in every node's cumulativeTime as selfTime + the cumulative of
1751
+ // its children, bottom-up. See profileExit for why this is derived
1752
+ // rather than measured.
1753
+ void finalizeProfilePaths();
1697
1754
  };
1698
1755
 
1699
1756
  } // namespace oscadeval
@@ -0,0 +1,81 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+ #include <vector>
5
+
6
+ namespace oscadeval {
7
+
8
+ // Aggregated profiling data for one *call site* -- a specific source
9
+ // location that calls a specific user module/function, not one
10
+ // declaration. Two different calls to the same function get separate
11
+ // entries; the same call expression re-executed many times (a loop body,
12
+ // recursion) aggregates into one entry with callCount > 1, since the AST
13
+ // node (and thus its position) is identical across those invocations.
14
+ // Mirrors the reference's CallSiteProfile.
15
+ struct CallSiteProfile {
16
+ std::string kind; // "module" | "child" (forwarded via children()) | "function"
17
+ std::string name;
18
+ std::string callerName; // enclosing module/function's own name, or "<toplevel>"
19
+ std::string callOrigin;
20
+ int callLine = 0;
21
+ int callColumn = 0; // distinguishes two calls sharing one line
22
+ std::string declOrigin;
23
+ int declLine = 0;
24
+ int callCount = 0;
25
+ double selfTime = 0.0; // seconds, own code only, never double-counted
26
+ double cumulativeTime = 0.0; // seconds, includes children; recursion-guarded
27
+ };
28
+
29
+ // One node of the calling-context tree: a call site reached by ONE
30
+ // specific path from <toplevel>. Where CallSiteProfile aggregates a site
31
+ // over every path that reached it, this keeps them separate -- so
32
+ // `cuboid` called from `bracket` and `cuboid` called from `rail` are two
33
+ // nodes with their own times, and a consumer can show what a particular
34
+ // path actually cost rather than a total across all callers.
35
+ //
36
+ // Flat vector with parent/child indices rather than pointers: it survives
37
+ // the vector reallocating as nodes are appended, and crosses the Python
38
+ // binding as plain data with no ownership question.
39
+ //
40
+ // Recursion is folded rather than unrolled. Re-entering a call site
41
+ // already on the current path reuses that node instead of appending a new
42
+ // child, so `fib` calling itself 400 deep is one node with callCount 400,
43
+ // not a 400-node chain. That bounds the tree by distinct ACYCLIC paths and
44
+ // keeps the recursion-guarded cumulative-time rule (only the outermost
45
+ // entry contributes) meaningful per node.
46
+ struct ProfilePathNode {
47
+ int parent = -1; // index into ProfileResult::paths; -1 for the root
48
+ std::vector<int> children; // indices, in first-call order
49
+ std::string kind; // "module" | "child" | "function"; empty for the root
50
+ std::string name; // callee; "<toplevel>" for the root
51
+ std::string callOrigin;
52
+ int callLine = 0;
53
+ int callColumn = 0; // distinguishes two calls sharing one line
54
+ std::string declOrigin;
55
+ int declLine = 0;
56
+ int callCount = 0;
57
+ double selfTime = 0.0; // seconds, this node's own code on this path
58
+ double cumulativeTime = 0.0; // seconds, this node and everything under it
59
+ };
60
+
61
+ // Whole-evaluate() profiling summary, built when Evaluator is constructed
62
+ // with profiling=true. unattributedTime covers top-level script code and
63
+ // anything else not inside a user module/function call (native builtins'
64
+ // own resolve work, mostly) -- resolveTime always equals
65
+ // sum(s.selfTime for callSites) + unattributedTime, so a UI can show
66
+ // percentages that honestly add to 100%. Mirrors the reference's
67
+ // ProfileResult.
68
+ struct ProfileResult {
69
+ std::vector<CallSiteProfile> callSites;
70
+ // The calling-context tree; paths[0] is always the <toplevel> root.
71
+ // Empty only if profiling was off. callSites stays exactly as it was --
72
+ // the flat per-site view is still the right answer to "what is
73
+ // expensive overall", and is cheaper to scan than walking this.
74
+ std::vector<ProfilePathNode> paths;
75
+ double resolveTime = 0.0;
76
+ double generateTime = 0.0;
77
+ double totalTime = 0.0;
78
+ double unattributedTime = 0.0;
79
+ };
80
+
81
+ } // namespace oscadeval
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.16.0"
7
+ version = "0.21.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -9,7 +9,7 @@ like a manifold3d body (`.is_empty()` / `.to_mesh()`), so the renderer reads
9
9
  `cb.body.to_mesh().vert_properties/tri_verts/run_original_id/run_index`
10
10
  exactly as before -- no cross-module Manifold objects are ever exchanged.
11
11
  """
12
- from dataclasses import dataclass
12
+ from dataclasses import dataclass, field
13
13
  from typing import Optional
14
14
 
15
15
  from . import _openscad_cpp_evaluator as _ext
@@ -142,11 +142,12 @@ class _CSGNode:
142
142
  class CallSiteProfile:
143
143
  """Aggregated profiling data for one call site. Mirrors the reference's
144
144
  CallSiteProfile -- see ProfileResult's own docstring."""
145
- kind: str # "module" | "function"
145
+ kind: str # "module" | "child" | "function"
146
146
  name: str
147
147
  caller_name: str
148
148
  call_origin: str
149
149
  call_line: int
150
+ call_column: int
150
151
  decl_origin: str
151
152
  decl_line: int
152
153
  call_count: int = 0
@@ -165,6 +166,23 @@ class ProfileResult:
165
166
  generate_time: float
166
167
  total_time: float
167
168
  unattributed_time: float
169
+ # Calling-context tree: a flat list of dicts, paths[0] the <toplevel>
170
+ # root, linked by `parent`/`children` INDICES into this same list.
171
+ #
172
+ # Where call_sites aggregates a site over every path that reached it,
173
+ # each node here is one call site on ONE path -- so `cuboid` called
174
+ # from `bracket` and from `rail` are separate nodes with their own
175
+ # times, and a report can say what a particular path cost rather than
176
+ # only what a name cost in total.
177
+ #
178
+ # Keys: parent, children, kind, name, call_origin, call_line,
179
+ # decl_origin, decl_line, call_count, self_time, cumulative_time.
180
+ #
181
+ # Recursion is folded: re-entering a site already on the path reuses
182
+ # that node (call_count rises) instead of unrolling a node per level.
183
+ # Defaults to empty so a ProfileResult built by older code still
184
+ # constructs.
185
+ paths: list = field(default_factory=list)
168
186
 
169
187
 
170
188
  def _summarize_param(value, max_items: int = 6, max_len: int = 40) -> str:
@@ -1168,6 +1168,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
1168
1168
  treeNode->kind = site.tagName;
1169
1169
  treeNode->node = site.node;
1170
1170
  treeNode->isBuiltin = true;
1171
+ treeNode->warnEntry = ev.currentWarnEntry();
1171
1172
  treeNode->children = std::move(children);
1172
1173
  treeNode->params = std::move(pending.params);
1173
1174
  treeNode->uncacheable = uncacheable;
@@ -1244,6 +1245,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
1244
1245
  treeNode->kind = site.op;
1245
1246
  treeNode->node = site.node;
1246
1247
  treeNode->isBuiltin = true;
1248
+ treeNode->warnEntry = ev.currentWarnEntry();
1247
1249
  treeNode->children = std::move(children);
1248
1250
  treeNode->params = std::move(params);
1249
1251
  treeNode->uncacheable = uncacheable;
@@ -59,7 +59,14 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
59
59
 
60
60
  auto it = dispatch.find(node.kind);
61
61
  if (node.isBuiltin && it != dispatch.end()) {
62
+ // Resolve captured which user-level call reached this node;
63
+ // republish it so a warning raised inside the GenerateFn
64
+ // can name that line. Restored after (rather than left set)
65
+ // so a sibling generated at top level doesn't inherit it.
66
+ const oscad::Position* savedWarnEntry = generateWarnEntry;
67
+ generateWarnEntry = node.warnEntry;
62
68
  node.bodies = it->second(*this, node.params, node.children, *node.node);
69
+ generateWarnEntry = savedWarnEntry;
63
70
  } else {
64
71
  // No registered GenerateFn for this kind (a builtin with
65
72
  // display-only semantics like render(), or a non-builtin
@@ -66,6 +66,7 @@ void Evaluator::buildTreeNode(const std::string& kind, const oscad::ASTNode& nod
66
66
  treeNode->kind = kind;
67
67
  treeNode->node = &node;
68
68
  treeNode->isBuiltin = true;
69
+ treeNode->warnEntry = currentWarnEntry();
69
70
  treeNode->children = std::move(children);
70
71
  treeNode->params = std::move(params);
71
72
  treeNode->uncacheable = uncacheable;
@@ -135,6 +136,7 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
135
136
  treeNode->kind = name;
136
137
  treeNode->node = &node;
137
138
  treeNode->isBuiltin = true;
139
+ treeNode->warnEntry = currentWarnEntry();
138
140
  treeNode->uncacheable = uncacheable;
139
141
  treeNode->children = std::move(children);
140
142
  treeNode->params = std::move(params);
@@ -159,6 +161,7 @@ void Evaluator::spliceModuleChildren(std::vector<std::unique_ptr<CSGNode>> child
159
161
  unionNode->kind = "union";
160
162
  unionNode->node = &callNode;
161
163
  unionNode->isBuiltin = false;
164
+ unionNode->warnEntry = currentWarnEntry();
162
165
  unionNode->uncacheable = std::any_of(children.begin(), children.end(), [](const auto& c) { return c->uncacheable; });
163
166
  unionNode->children = std::move(children);
164
167
  setTreeDepthOrThrow(*unionNode, callNode);
@@ -266,8 +269,10 @@ std::vector<ColoredBody> Evaluator::evaluateImpl(const NodeList& nodes, EvalCont
266
269
  selfSum += site.selfTime;
267
270
  sites.push_back(site);
268
271
  }
272
+ finalizeProfilePaths();
269
273
  profileResult = ProfileResult{
270
- std::move(sites), resolveTime, generateTime, resolveTime + generateTime, std::max(0.0, resolveTime - selfSum),
274
+ std::move(sites), profilePaths_, resolveTime, generateTime, resolveTime + generateTime,
275
+ std::max(0.0, resolveTime - selfSum),
271
276
  };
272
277
  }
273
278