openscad-cpp-evaluator 1.3.1__tar.gz → 1.4.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 (216) hide show
  1. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/bindings/module.cpp +6 -6
  3. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +43 -12
  4. openscad_cpp_evaluator-1.4.0/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +213 -0
  5. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +1 -0
  6. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +25 -0
  7. openscad_cpp_evaluator-1.4.0/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +58 -0
  8. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/api.cpp +121 -1
  9. openscad_cpp_evaluator-1.4.0/external/openscad_cpp_parser/src/scope.cpp +75 -0
  10. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +14 -8
  11. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_scope.cpp +91 -91
  12. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +6 -6
  13. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +8 -3
  14. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/eval_context.hpp +16 -1
  15. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/eval_use.hpp +9 -0
  16. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/evaluator.hpp +11 -0
  17. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/value.hpp +9 -0
  18. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/pyproject.toml +1 -1
  19. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/topology.cpp +1 -1
  20. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/bytecode_compiler.cpp +35 -18
  21. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/bytecode_vm.cpp +7 -7
  22. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/csg_resolve.cpp +6 -0
  23. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/eval_context.cpp +16 -1
  24. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/eval_use.cpp +21 -5
  25. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/expr_eval.cpp +1 -1
  26. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/stmt_eval.cpp +1 -1
  27. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/user_calls.cpp +12 -12
  28. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/CMakeLists.txt +1 -0
  29. openscad_cpp_evaluator-1.4.0/tests/test_include_cache.cpp +132 -0
  30. openscad_cpp_evaluator-1.3.1/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -143
  31. openscad_cpp_evaluator-1.3.1/external/openscad_cpp_parser/src/scope.cpp +0 -25
  32. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/.github/workflows/ci.yml +0 -0
  33. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/.github/workflows/wheels.yml +0 -0
  34. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/.gitignore +0 -0
  35. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/.gitmodules +0 -0
  36. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/CLAUDE.md +0 -0
  37. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/CMakeLists.txt +0 -0
  38. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/LICENSE +0 -0
  39. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/README.md +0 -0
  40. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/bindings/CMakeLists.txt +0 -0
  41. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/bindings/ast_to_py.cpp +0 -0
  42. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/cmake/embed_font.cmake +0 -0
  43. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/examples/CMakeLists.txt +0 -0
  44. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/examples/eval_perf_benchmark.cpp +0 -0
  45. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/examples/manifold_cache_reuse.cpp +0 -0
  46. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/examples/minimal_debugger.cpp +0 -0
  47. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  48. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/.gitignore +0 -0
  49. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  50. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  51. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/README.md +0 -0
  52. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  53. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  54. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  55. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  56. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  57. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  58. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  59. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  60. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  61. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  62. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  63. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  64. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  65. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  66. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  67. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  68. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  69. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  70. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  71. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  72. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  73. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  74. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  75. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  76. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  77. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  78. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  79. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  80. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  81. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  82. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  83. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  84. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  85. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  86. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  87. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  88. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  89. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  90. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  91. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  92. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  93. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  94. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  95. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  96. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  97. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  98. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  99. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  100. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  101. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  102. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  103. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  104. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  105. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  106. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  107. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  108. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  109. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  110. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  111. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  112. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  113. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  114. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  115. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  116. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  117. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  118. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  119. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  120. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  121. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  122. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  123. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  124. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  125. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  126. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  127. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  128. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  129. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  130. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  131. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  132. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  133. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  134. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  135. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  136. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/resources/fonts/LICENSE +0 -0
  137. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  138. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/CMakeLists.txt +0 -0
  139. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/booleans.cpp +0 -0
  140. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/builtins.hpp +0 -0
  141. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/call_args.cpp +0 -0
  142. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/color.cpp +0 -0
  143. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/control.cpp +0 -0
  144. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/dxf_dim.cpp +0 -0
  145. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/extrude.cpp +0 -0
  146. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/function_builtins.cpp +0 -0
  147. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/import.cpp +0 -0
  148. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/modifiers.cpp +0 -0
  149. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/primitives_2d.cpp +0 -0
  150. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/primitives_3d.cpp +0 -0
  151. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/registry.cpp +0 -0
  152. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/roof.cpp +0 -0
  153. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/segments.cpp +0 -0
  154. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/surface.cpp +0 -0
  155. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/text.cpp +0 -0
  156. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/builtins/transforms.cpp +0 -0
  157. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/colored_body.cpp +0 -0
  158. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/csg_generate.cpp +0 -0
  159. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/css_colors.cpp +0 -0
  160. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/debug/debug_repl.cpp +0 -0
  161. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/debug_profile.cpp +0 -0
  162. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/eval_error.cpp +0 -0
  163. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/export.cpp +0 -0
  164. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/format_closure.cpp +0 -0
  165. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/import/dxf_import.cpp +0 -0
  166. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/import/mesh_import.cpp +0 -0
  167. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/import/surface_load.cpp +0 -0
  168. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/import/svg_import.cpp +0 -0
  169. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/manifold_cache.cpp +0 -0
  170. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/measure_geometry.cpp +0 -0
  171. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/mesh_check.cpp +0 -0
  172. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/text/font_match.cpp +0 -0
  173. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/text/freetype_font_provider.cpp +0 -0
  174. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/text/text_metrics.cpp +0 -0
  175. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/utf8.cpp +0 -0
  176. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/value.cpp +0 -0
  177. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/src/zip_stored.cpp +0 -0
  178. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_booleans.cpp +0 -0
  179. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_bytecode_compiler.cpp +0 -0
  180. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_children_separate.cpp +0 -0
  181. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_cli.cpp +0 -0
  182. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_control_flow.cpp +0 -0
  183. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_csg_tree.cpp +0 -0
  184. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_debug_hooks.cpp +0 -0
  185. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_dxf_svg_import.cpp +0 -0
  186. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_eval_context.cpp +0 -0
  187. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_export.cpp +0 -0
  188. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_expr_eval.cpp +0 -0
  189. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_extrude_roof.cpp +0 -0
  190. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_feature_detection.cpp +0 -0
  191. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_function_builtins.cpp +0 -0
  192. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_helpers.hpp +0 -0
  193. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_import_export.cpp +0 -0
  194. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_manifold_cache.cpp +0 -0
  195. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_mesh_check.cpp +0 -0
  196. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_multi_color_merge.cpp +0 -0
  197. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_primitives.cpp +0 -0
  198. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_profiling.cpp +0 -0
  199. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_python_bindings.py +0 -0
  200. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_render_expr.cpp +0 -0
  201. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_scope_trail.cpp +0 -0
  202. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_scoping.cpp +0 -0
  203. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_surface.cpp +0 -0
  204. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_tail_calls.cpp +0 -0
  205. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_text.cpp +0 -0
  206. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_transforms.cpp +0 -0
  207. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_unexpected_args.cpp +0 -0
  208. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_use.cpp +0 -0
  209. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_value.cpp +0 -0
  210. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_viewport_params.cpp +0 -0
  211. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tests/test_zip_stored.cpp +0 -0
  212. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tools/cli/CMakeLists.txt +0 -0
  213. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tools/cli/cli_lib.cpp +0 -0
  214. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tools/cli/cli_lib.hpp +0 -0
  215. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/tools/cli/main.cpp +0 -0
  216. {openscad_cpp_evaluator-1.3.1 → openscad_cpp_evaluator-1.4.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.3.1
3
+ Version: 1.4.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -388,8 +388,8 @@ nb::object evaluate(const std::string& path, nb::dict viewportParams,
388
388
  nb::gil_scoped_release rel;
389
389
  auto logFn = [&echoes](const std::string& m) { echoes.push_back(m); };
390
390
  try {
391
- std::vector<std::unique_ptr<oscad::ASTNode>> ast = oscad::getASTFromFile(path);
392
- oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(ast, path, logFn);
391
+ oscad::ParsedProgram program = oscad::getProgramFromFile(path);
392
+ oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(program.nodes, path, logFn);
393
393
  oscadeval::Evaluator ev(logFn, nullptr, manifoldCache, oscadeval::DebugHooks{}, profile);
394
394
  oscadeval::EvalContext ctx = oscadeval::EvalContext::makeRoot(used.rootScope.get());
395
395
  bodies = oscadeval::toRenderableBodies(ev.evaluate(used.processedNodes, ctx, vp, generate));
@@ -693,8 +693,8 @@ nb::object debugEvaluate(const std::string& path, nb::dict viewportParams, nb::c
693
693
  };
694
694
  }
695
695
  try {
696
- std::vector<std::unique_ptr<oscad::ASTNode>> ast = oscad::getASTFromFile(path);
697
- oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(ast, path, echoCpp);
696
+ oscad::ParsedProgram program = oscad::getProgramFromFile(path);
697
+ oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(program.nodes, path, echoCpp);
698
698
  oscadeval::Evaluator ev(echoCpp, nullptr, manifoldCache, hooks, false);
699
699
  evPtr = &ev;
700
700
  if (fastContinueSignal) ev.setFastContinueInterruptFlag(fastContinueSignal->flag());
@@ -729,8 +729,8 @@ nb::list parseDecls(const std::string& path) {
729
729
  std::vector<Decl> decls;
730
730
  {
731
731
  nb::gil_scoped_release rel;
732
- std::vector<std::unique_ptr<oscad::ASTNode>> ast = oscad::getASTFromFile(path);
733
- oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(ast, path, [](const std::string&) {});
732
+ oscad::ParsedProgram program = oscad::getProgramFromFile(path);
733
+ oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(program.nodes, path, [](const std::string&) {});
734
734
  for (const oscad::ASTNode* n : used.processedNodes) {
735
735
  const oscad::Position& p = n->position();
736
736
  const char* ns = nullptr;
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "openscad_cpp_parser/ast.hpp"
4
+ #include "openscad_cpp_parser/scope_table.hpp"
4
5
  #include "openscad_cpp_parser/source_map.hpp"
5
6
 
6
7
  #include <memory>
@@ -68,15 +69,10 @@ std::vector<std::unique_ptr<ASTNode>> getASTFromString(const std::string& code,
68
69
  // can't be found/read, or ParseError (see getASTFromString) on a syntax
69
70
  // error in any of the involved files.
70
71
  //
71
- // ponytail: unlike the Python reference, this does NOT cache parsed ASTs
72
- // (in-memory or on-disk) across calls. With unique_ptr ownership, "return
73
- // the same cached tree to every caller" isn't representable (each caller
74
- // needs exclusive ownership), and a clone-based cache would need a deep
75
- // clone() for all 66 node kinds just to support an unmeasured perf
76
- // optimization. Every call re-parses. Upgrade path: add a per-node
77
- // clone() (mirroring toJson/toString's per-kind dispatch) and cache
78
- // serialized snapshots keyed by (path, mtime) if repeated-parse cost of a
79
- // real workload is shown to matter.
72
+ // This does NOT share or cache anything: every call re-parses the file and
73
+ // every file it includes, and the caller owns all of it. See
74
+ // getProgramFromFile() for the shared, cached form -- the one an evaluator
75
+ // that re-renders the same script should use.
80
76
  std::vector<std::unique_ptr<ASTNode>> getASTFromFile(const std::string& file, bool includeComments = false,
81
77
  bool processIncludes = true);
82
78
 
@@ -95,9 +91,44 @@ LibraryFileResult getASTFromLibraryFile(const std::string& currFile, const std::
95
91
  // platform default library dir.
96
92
  std::optional<std::string> findLibraryFile(const std::string& currFile, const std::string& libFile);
97
93
 
98
- // No-op: kept for API-shape parity with the Python reference's
99
- // clear_ast_cache(). See the ponytail note on getASTFromFile() -- this
100
- // port doesn't cache, so there's nothing to clear.
94
+ // A file's statements with its `include <...>` directives resolved, where
95
+ // each included file's AST is SHARED with every other file that includes
96
+ // it rather than re-parsed.
97
+ //
98
+ // That sharing is the point. Parsing `include <BOSL2/std.scad>` costs
99
+ // ~55ms and is ~82% of evaluating a small BOSL2 script; the library does
100
+ // not change between renders, so re-parsing it every time is the single
101
+ // largest cost in a re-render or a docs build (which renders ~1000
102
+ // examples, each including the same library).
103
+ //
104
+ // Only the per-FILE parses are cached, not a whole resolved program: a
105
+ // file is spliced in at most once per resolution (`visited`), so caching
106
+ // std.scad already-resolved would double its statements in a script that
107
+ // also includes something else depending on it. Re-running the resolution
108
+ // is only pointer pushes.
109
+ struct ParsedProgram {
110
+ // The flattened statement list, in source order, includes spliced in
111
+ // where their directives stood. Non-owning: see keepAlive.
112
+ std::vector<const ASTNode*> nodes;
113
+ // Holds every AST the above points into -- this file's own parse and
114
+ // each shared include -- alive for as long as this object. Nodes are
115
+ // borrowed, never owned, which is what lets two scripts (or two
116
+ // threads) use the same parsed library at once.
117
+ std::vector<std::shared_ptr<const std::vector<std::unique_ptr<ASTNode>>>> keepAlive;
118
+ };
119
+
120
+ // Parses `file` and resolves its includes against the cache, which is keyed
121
+ // by (path, mtime, size) so an edited file re-parses on its own.
122
+ // Thread-safe. Throws exactly as getASTFromFile does.
123
+ ParsedProgram getProgramFromFile(const std::string& file, bool includeComments = false);
124
+
125
+ // Drops every cached file parse. Nothing already handed out is
126
+ // invalidated -- a ParsedProgram keeps what it borrowed alive.
101
127
  void clearAstCache();
102
128
 
129
+ // How many file parses the cache is holding. For tests that need to prove
130
+ // the cache replaces an edited file's entry rather than accumulating one
131
+ // per save.
132
+ size_t astCacheSize();
133
+
103
134
  } // namespace oscad
@@ -0,0 +1,213 @@
1
+ #pragma once
2
+
3
+ #include "openscad_cpp_parser/position.hpp"
4
+
5
+ #include <cstdint>
6
+ #include <string>
7
+
8
+ namespace oscad {
9
+
10
+ class Scope;
11
+
12
+ // One value per instantiable (leaf) AST node class. Abstract bases
13
+ // (Expression, Primary, Argument, VectorElement, ModuleInstantiation) have
14
+ // no NodeKind of their own, since they are never directly constructed.
15
+ enum class NodeKind {
16
+ // Comments
17
+ CommentLine,
18
+ BlankLine,
19
+ CommentSpan,
20
+ CommentedExpr,
21
+ // Primary / literals
22
+ Identifier,
23
+ StringLiteral,
24
+ NumberLiteral,
25
+ BooleanLiteral,
26
+ UndefinedLiteral,
27
+ RangeLiteral,
28
+ // Declarations / arguments
29
+ ParameterDeclaration,
30
+ PositionalArgument,
31
+ NamedArgument,
32
+ Assignment,
33
+ // Prefix expression forms
34
+ LetOp,
35
+ EchoOp,
36
+ AssertOp,
37
+ FunctionLiteral,
38
+ // Operators
39
+ UnaryMinusOp,
40
+ AdditionOp,
41
+ SubtractionOp,
42
+ MultiplicationOp,
43
+ DivisionOp,
44
+ ModuloOp,
45
+ ExponentOp,
46
+ BitwiseAndOp,
47
+ BitwiseOrOp,
48
+ BitwiseNotOp,
49
+ BitwiseShiftLeftOp,
50
+ BitwiseShiftRightOp,
51
+ LogicalAndOp,
52
+ LogicalOrOp,
53
+ LogicalNotOp,
54
+ TernaryOp,
55
+ EqualityOp,
56
+ InequalityOp,
57
+ GreaterThanOp,
58
+ GreaterThanOrEqualOp,
59
+ LessThanOp,
60
+ LessThanOrEqualOp,
61
+ // Postfix
62
+ PrimaryCall,
63
+ PrimaryIndex,
64
+ PrimaryMember,
65
+ // render() in EXPRESSION position -- evaluates its children as geometry
66
+ // and yields an object() of measurements. The STATEMENT form of render()
67
+ // is a plain ModularCall named "render"; only this one is new.
68
+ RenderExpression,
69
+ // List comprehension
70
+ ListCompLet,
71
+ ListCompEach,
72
+ ListCompFor,
73
+ ListCompCFor,
74
+ ListCompIf,
75
+ ListCompIfElse,
76
+ ListComprehension,
77
+ // Module instantiation
78
+ ModularCall,
79
+ ModularFor,
80
+ ModularIntersectionFor,
81
+ ModularLet,
82
+ ModularEcho,
83
+ ModularAssert,
84
+ ModularIf,
85
+ ModularIfElse,
86
+ ModularModifierShowOnly,
87
+ ModularModifierHighlight,
88
+ ModularModifierBackground,
89
+ ModularModifierDisable,
90
+ // Top-level declarations
91
+ ModuleDeclaration,
92
+ FunctionDeclaration,
93
+ UseStatement,
94
+ IncludeStatement,
95
+ };
96
+
97
+ const char* nodeKindName(NodeKind kind);
98
+
99
+ // Base class for all AST nodes. Mirrors openscad_lalr_parser.nodes.ASTNode:
100
+ // every node carries its source Position and (once buildScope() has run) a
101
+ // non-owning pointer to the Scope visible at that point in the tree.
102
+ // Numbering context for the nodes one parse creates: every node built
103
+ // while it is installed gets that parse's treeId and a slot dense within
104
+ // it -- see ASTNode::slot().
105
+ struct NodeNumbering {
106
+ uint32_t treeId = 0;
107
+ uint32_t next = 0;
108
+ };
109
+
110
+ // Installs a numbering for a whole parse, INCLUDING any pass that builds
111
+ // more nodes from its result (attachComments wraps expressions in
112
+ // CommentedExpr nodes after the parser proper has finished). Nested use
113
+ // reuses the outer numbering rather than starting a second one, so a tree
114
+ // stays a tree however many passes contribute to it.
115
+ //
116
+ // Thread-local: two threads may parse at once and must number
117
+ // independently.
118
+ class ParseNumberingScope {
119
+ public:
120
+ ParseNumberingScope();
121
+ ~ParseNumberingScope();
122
+ ParseNumberingScope(const ParseNumberingScope&) = delete;
123
+ ParseNumberingScope& operator=(const ParseNumberingScope&) = delete;
124
+
125
+ private:
126
+ NodeNumbering numbering_;
127
+ NodeNumbering* previous_;
128
+ bool installed_;
129
+ };
130
+
131
+ class ScopeTable;
132
+
133
+ // The table setScope() writes into. buildScopes() installs one for its
134
+ // walk; thread-local, so two evaluations can build scopes at once over the
135
+ // same shared tree -- the whole point of sharing it.
136
+ class ScopeTableScope {
137
+ public:
138
+ explicit ScopeTableScope(ScopeTable& table);
139
+ ~ScopeTableScope();
140
+ ScopeTableScope(const ScopeTableScope&) = delete;
141
+ ScopeTableScope& operator=(const ScopeTableScope&) = delete;
142
+
143
+ private:
144
+ ScopeTable* previous_;
145
+ };
146
+
147
+ NodeNumbering* currentNodeNumbering();
148
+ // Slot for a node built with no parse active -- a test constructing nodes
149
+ // by hand, or astFromJson. Those all land in treeId 0 with process-unique
150
+ // slots, so they never collide with each other.
151
+ uint32_t nextLooseSlot();
152
+
153
+ class ASTNode {
154
+ public:
155
+ ASTNode(NodeKind kind, Position position) : kind_(kind), position_(std::move(position)) {
156
+ if (NodeNumbering* n = currentNodeNumbering()) {
157
+ treeId_ = n->treeId;
158
+ slot_ = n->next++;
159
+ } else {
160
+ slot_ = nextLooseSlot();
161
+ }
162
+ }
163
+ virtual ~ASTNode() = default;
164
+
165
+ ASTNode(const ASTNode&) = delete;
166
+ ASTNode& operator=(const ASTNode&) = delete;
167
+
168
+ NodeKind kind() const { return kind_; }
169
+ const Position& position() const { return position_; }
170
+
171
+ // Identity of the parse that built this node, and this node's dense
172
+ // index within it. Together they address the node's Scope in a
173
+ // ScopeTable, which is what lets one parsed tree be shared by several
174
+ // evaluations at once: the scope a node sits in depends on the file
175
+ // that included it, so it cannot live in the node itself. Both are 0
176
+ // for a node built outside a parse (a test constructing nodes by
177
+ // hand), which is a valid single tree of its own.
178
+ uint32_t treeId() const { return treeId_; }
179
+ uint32_t slot() const { return slot_; }
180
+
181
+ // A node's Scope is NOT stored here -- it lives in the render's
182
+ // ScopeTable, addressed by (treeId, slot). It has to: one parsed tree
183
+ // is shared by every script that includes it, and `include` means the
184
+ // included nodes sit in the INCLUDER's scope, so the same node is in a
185
+ // different scope in each. Read it with ScopeTable::get(node).
186
+
187
+ // Mirrors Python's __str__: every leaf node overrides this.
188
+ virtual std::string toString() const = 0;
189
+
190
+ // Mirrors Python's build_scope(parent_scope): the default (leaf) case
191
+ // just records parent_scope; nodes that introduce bindings or new
192
+ // scopes override this.
193
+ virtual void buildScope(Scope& parentScope) { setScope(parentScope); }
194
+
195
+ protected:
196
+ // For buildScope() overrides: records the scope visible at this node,
197
+ // mirroring Python's `self.scope = parent_scope` (which is often a
198
+ // *different* scope than what gets passed to this node's children).
199
+ //
200
+ // Writes into the ScopeTable buildScopes() installed for the duration
201
+ // of its walk, rather than taking one as a parameter: that keeps all
202
+ // 37 buildScope() overrides on their existing signature, and the walk
203
+ // is a single scoped pass with nothing else running inside it.
204
+ void setScope(Scope& s);
205
+
206
+ private:
207
+ NodeKind kind_;
208
+ Position position_;
209
+ uint32_t treeId_ = 0;
210
+ uint32_t slot_ = 0;
211
+ };
212
+
213
+ } // namespace oscad
@@ -7,6 +7,7 @@
7
7
  #include "openscad_cpp_parser/ast/expression.hpp"
8
8
  #include "openscad_cpp_parser/ast/module_instantiation.hpp"
9
9
  #include "openscad_cpp_parser/ast/scope_builder.hpp"
10
+ #include "openscad_cpp_parser/scope_table.hpp"
10
11
  #include "openscad_cpp_parser/ast/vector_element.hpp"
11
12
  #include "openscad_cpp_parser/position.hpp"
12
13
  #include "openscad_cpp_parser/scope.hpp"
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+ #include "openscad_cpp_parser/scope_table.hpp"
4
+
3
5
  #include <memory>
4
6
  #include <string>
5
7
  #include <unordered_map>
@@ -83,6 +85,19 @@ public:
83
85
  return *children_.back();
84
86
  }
85
87
 
88
+ // The ScopeTable holding every node's scope for this scope tree.
89
+ // buildScopes() attaches it to the root it returns, so the table lives
90
+ // exactly as long as the scopes it points at -- which is the ownership
91
+ // contract callers already keep. Null on a non-root scope; use
92
+ // rootTable() to reach it from anywhere in the tree.
93
+ void adoptTable(std::unique_ptr<ScopeTable> table);
94
+ const ScopeTable* table() const { return ownedTable_.get(); }
95
+ const ScopeTable* rootTable() const {
96
+ const Scope* s = this;
97
+ while (s->parent_) s = s->parent_;
98
+ return s->ownedTable_.get();
99
+ }
100
+
86
101
  private:
87
102
  static ASTNode* find(const std::unordered_map<std::string, ASTNode*>& table, const std::string& name) {
88
103
  auto it = table.find(name);
@@ -94,6 +109,16 @@ private:
94
109
  std::unordered_map<std::string, ASTNode*> functions_;
95
110
  std::unordered_map<std::string, ASTNode*> modules_;
96
111
  std::vector<std::unique_ptr<Scope>> children_;
112
+ std::unique_ptr<ScopeTable> ownedTable_; // root only
97
113
  };
98
114
 
115
+ // The lexical Scope recorded for `node` when `anyScopeInTree`'s tree was
116
+ // built -- the replacement for the ASTNode::scope() field that used to
117
+ // live in the node. Any scope in the tree will do; it walks up to the root
118
+ // that owns the table.
119
+ inline const Scope* scopeOf(const Scope& anyScopeInTree, const ASTNode& node) {
120
+ const ScopeTable* table = anyScopeInTree.rootTable();
121
+ return table ? table->get(node) : nullptr;
122
+ }
123
+
99
124
  } // namespace oscad
@@ -0,0 +1,58 @@
1
+ #pragma once
2
+
3
+ #include "openscad_cpp_parser/ast/ast_node.hpp"
4
+
5
+ #include <cstdint>
6
+ #include <vector>
7
+
8
+ namespace oscad {
9
+
10
+ class Scope;
11
+
12
+ // Where a node's Scope lives, now that it cannot live in the node.
13
+ //
14
+ // A parsed tree is shared: `include <BOSL2/std.scad>` is parsed once and
15
+ // then handed to every script that includes it, which is what makes a
16
+ // render cheap (parsing BOSL2 is ~55ms and dwarfs everything else). But
17
+ // `include` means "share my scope", so the very same BOSL2 node sits in a
18
+ // different scope in every script that includes it. A `Scope*` field in
19
+ // ASTNode could only ever hold one of them, and whichever render wrote it
20
+ // last would corrupt the others.
21
+ //
22
+ // So the pointer moves out here, into a table one render owns. Addressing
23
+ // is (treeId, slot), both stamped on the node when it was parsed, so a
24
+ // lookup is two loads and no hashing -- it sits on the VM's call path.
25
+ class ScopeTable {
26
+ public:
27
+ // Null until set: buildScopes() fills in every node it walks, and a
28
+ // reader treats null as "no scope recorded", exactly as the old
29
+ // nullptr-initialised field did.
30
+ const Scope* get(const ASTNode& node) const {
31
+ const uint32_t tree = node.treeId();
32
+ if (tree >= trees_.size()) return nullptr;
33
+ const std::vector<Scope*>& slots = trees_[tree];
34
+ const uint32_t slot = node.slot();
35
+ return slot < slots.size() ? slots[slot] : nullptr;
36
+ }
37
+ Scope* get(const ASTNode& node) {
38
+ return const_cast<Scope*>(static_cast<const ScopeTable*>(this)->get(node));
39
+ }
40
+
41
+ void set(const ASTNode& node, Scope* scope) {
42
+ const uint32_t tree = node.treeId();
43
+ if (tree >= trees_.size()) trees_.resize(tree + 1);
44
+ std::vector<Scope*>& slots = trees_[tree];
45
+ const uint32_t slot = node.slot();
46
+ if (slot >= slots.size()) slots.resize(slot + 1, nullptr);
47
+ slots[slot] = scope;
48
+ }
49
+
50
+ private:
51
+ // Indexed by treeId. Sparse in principle -- treeIds are handed out
52
+ // process-wide and a render only involves a few trees -- but the empty
53
+ // outer entries are 24 bytes each and never allocate, so the waste is
54
+ // a few tens of KB against a 55ms saving.
55
+ std::vector<std::vector<Scope*>> trees_;
56
+ };
57
+
58
+ } // namespace oscad
@@ -9,6 +9,10 @@
9
9
  #include <filesystem>
10
10
  #include <fstream>
11
11
  #include <set>
12
+ #include <system_error>
13
+ #include <cstdint>
14
+ #include <unordered_map>
15
+ #include <mutex>
12
16
  #include <sstream>
13
17
 
14
18
  namespace oscad {
@@ -86,6 +90,11 @@ std::string formatSyntaxError(const ParserDriver& driver, const std::string& cod
86
90
  } // namespace
87
91
 
88
92
  std::vector<std::unique_ptr<ASTNode>> parseAst(const std::string& code, const std::string& origin, SourceMap* sourceMap) {
93
+ // Stamps every node this parse builds with one treeId and a dense
94
+ // slot, so a ScopeTable can address it without the node carrying a
95
+ // Scope pointer of its own -- see ASTNode::slot().
96
+ ParseNumberingScope numbering;
97
+
89
98
  ParserDriver driver(origin);
90
99
  lexerBeginString(code);
91
100
  yy::parser parser(driver);
@@ -100,6 +109,9 @@ std::vector<std::unique_ptr<ASTNode>> parseAst(const std::string& code, const st
100
109
 
101
110
  std::vector<std::unique_ptr<ASTNode>> getASTFromString(const std::string& code, bool includeComments,
102
111
  const std::string& origin) {
112
+ // Spans attachComments too: it builds CommentedExpr wrappers, and they
113
+ // belong to the same tree as what they wrap.
114
+ ParseNumberingScope numbering;
103
115
  auto ast = parseAst(code, origin); // propagates ParseError with the full diagnostic
104
116
  if (includeComments) {
105
117
  ast = attachComments(std::move(ast), code, origin);
@@ -121,6 +133,7 @@ std::string readFile(const std::string& path) {
121
133
 
122
134
  std::vector<std::unique_ptr<ASTNode>> parseSingleFile(const std::string& filePath, bool includeComments) {
123
135
  std::string code = readFile(filePath);
136
+ ParseNumberingScope numbering; // spans attachComments -- see getASTFromString
124
137
  auto ast = parseAst(code, filePath);
125
138
  if (includeComments) {
126
139
  ast = attachComments(std::move(ast), code, filePath);
@@ -238,8 +251,115 @@ LibraryFileResult getASTFromLibraryFile(const std::string& currFile, const std::
238
251
  return LibraryFileResult{std::move(ast), *found};
239
252
  }
240
253
 
254
+
255
+
256
+ namespace {
257
+
258
+ using FileAst = std::vector<std::unique_ptr<ASTNode>>;
259
+ using FileAstPtr = std::shared_ptr<const FileAst>;
260
+
261
+ // One entry per (file, comments) -- keyed by PATH, with the content stamp
262
+ // stored beside the tree rather than in the key. A stale stamp REPLACES the
263
+ // entry instead of adding a second one: an editor re-renders on every save,
264
+ // and a stamp-in-the-key cache would keep a full copy of every version the
265
+ // file ever had.
266
+ struct CacheKey {
267
+ std::string path;
268
+ bool comments;
269
+ bool operator==(const CacheKey& o) const { return comments == o.comments && path == o.path; }
270
+ };
271
+ struct CacheKeyHash {
272
+ size_t operator()(const CacheKey& k) const {
273
+ return std::hash<std::string>{}(k.path) ^ (k.comments ? 0x5bf03635U : 0U);
274
+ }
275
+ };
276
+ struct CacheEntry {
277
+ std::uintmax_t size = 0;
278
+ std::int64_t mtime = 0;
279
+ FileAstPtr ast;
280
+ };
281
+
282
+ std::mutex g_astCacheMutex;
283
+ std::unordered_map<CacheKey, CacheEntry, CacheKeyHash> g_astCache;
284
+
285
+ FileAstPtr parseFileShared(const std::string& absPath, bool includeComments) {
286
+ std::error_code ec;
287
+ const auto size = fs::file_size(absPath, ec);
288
+ const std::uintmax_t stampSize = ec ? 0 : size;
289
+ ec.clear();
290
+ const auto written = fs::last_write_time(absPath, ec);
291
+ const std::int64_t stampMtime = ec ? 0 : static_cast<std::int64_t>(written.time_since_epoch().count());
292
+
293
+ const CacheKey key{absPath, includeComments};
294
+ {
295
+ std::lock_guard<std::mutex> lock(g_astCacheMutex);
296
+ auto it = g_astCache.find(key);
297
+ if (it != g_astCache.end() && it->second.size == stampSize && it->second.mtime == stampMtime)
298
+ return it->second.ast;
299
+ }
300
+
301
+ // Parsed OUTSIDE the lock: parsing a library takes tens of
302
+ // milliseconds, and holding a global lock across it would serialise
303
+ // every thread. Two threads racing the same file both parse and one
304
+ // result is dropped -- wasteful once, never wrong, and far cheaper than
305
+ // the alternative.
306
+ auto parsed = std::make_shared<const FileAst>(parseSingleFile(absPath, includeComments));
307
+ std::lock_guard<std::mutex> lock(g_astCacheMutex);
308
+ CacheEntry& entry = g_astCache[key];
309
+ // Whoever writes last wins; the loser's tree stays alive in whatever
310
+ // ParsedProgram already borrowed it.
311
+ entry.size = stampSize;
312
+ entry.mtime = stampMtime;
313
+ entry.ast = parsed;
314
+ return parsed;
315
+ }
316
+
317
+ // Splices includes into a flat statement list of BORROWED nodes, collecting
318
+ // what must stay alive. Mirrors resolveIncludes' walk exactly, including
319
+ // the per-resolution `visited` set that makes a file contribute at most
320
+ // once.
321
+ void collectProgram(const FileAst& nodes, const std::string& currentFile, bool includeComments,
322
+ std::set<std::string>& visited, ParsedProgram& out) {
323
+ for (const auto& node : nodes) {
324
+ if (node->kind() == NodeKind::IncludeStatement) {
325
+ const auto& inc = static_cast<const IncludeStatement&>(*node);
326
+ const std::string& filename = inc.filepath->val;
327
+ auto libFile = findLibraryFile(currentFile, filename);
328
+ if (!libFile) {
329
+ throw std::runtime_error("Included file '" + filename + "' not found. Searched relative to: " +
330
+ (currentFile.empty() ? "current directory" : currentFile));
331
+ }
332
+ std::string absLib = fs::absolute(*libFile).string();
333
+ if (!visited.insert(absLib).second) continue;
334
+ FileAstPtr included = parseFileShared(absLib, includeComments);
335
+ out.keepAlive.push_back(included);
336
+ collectProgram(*included, absLib, includeComments, visited, out);
337
+ } else {
338
+ out.nodes.push_back(node.get());
339
+ }
340
+ }
341
+ }
342
+
343
+ } // namespace
344
+
345
+ ParsedProgram getProgramFromFile(const std::string& file, bool includeComments) {
346
+ const std::string abs = fs::absolute(file).string();
347
+ ParsedProgram out;
348
+ FileAstPtr own = parseFileShared(abs, includeComments);
349
+ out.keepAlive.push_back(own);
350
+ std::set<std::string> visited{abs};
351
+ collectProgram(*own, abs, includeComments, visited, out);
352
+ return out;
353
+ }
354
+
241
355
  void clearAstCache() {
242
- // No-op -- see the ponytail note on getASTFromFile() in api.hpp.
356
+ std::lock_guard<std::mutex> lock(g_astCacheMutex);
357
+ g_astCache.clear();
358
+ }
359
+
360
+ size_t astCacheSize() {
361
+ std::lock_guard<std::mutex> lock(g_astCacheMutex);
362
+ return g_astCache.size();
243
363
  }
244
364
 
245
365
  } // namespace oscad
@@ -0,0 +1,75 @@
1
+ #include "openscad_cpp_parser/api.hpp"
2
+
3
+ #include <atomic>
4
+
5
+ #include "openscad_cpp_parser/scope_table.hpp"
6
+
7
+ #include "openscad_cpp_parser/ast/scope_builder.hpp"
8
+
9
+ namespace oscad {
10
+
11
+ std::unique_ptr<Scope> buildScopes(const std::vector<std::unique_ptr<ASTNode>>& ast) {
12
+ auto owned = std::make_unique<ScopeTable>();
13
+ ScopeTableScope recording(*owned);
14
+ auto root = std::make_unique<Scope>();
15
+ collectHoistedDeclarations(ast, *root);
16
+ for (auto& node : ast) {
17
+ node->buildScope(*root);
18
+ }
19
+ root->adoptTable(std::move(owned));
20
+ return root;
21
+ }
22
+
23
+ std::unique_ptr<Scope> buildScopes(const std::vector<ASTNode*>& ast) {
24
+ auto owned = std::make_unique<ScopeTable>();
25
+ ScopeTableScope recording(*owned);
26
+ auto root = std::make_unique<Scope>();
27
+ collectHoistedDeclarations(ast, *root);
28
+ for (ASTNode* node : ast) {
29
+ node->buildScope(*root);
30
+ }
31
+ root->adoptTable(std::move(owned));
32
+ return root;
33
+ }
34
+
35
+
36
+ namespace {
37
+ // One per thread: a ParseNumberingScope installs a numbering for its
38
+ // duration, so a node's constructor can stamp itself without every node
39
+ // kind having to cooperate -- which matters because there is no generic
40
+ // child walker to number a finished tree with.
41
+ thread_local NodeNumbering* g_numbering = nullptr;
42
+ thread_local ScopeTable* g_scopeTable = nullptr;
43
+ std::atomic<uint32_t> g_nextTreeId{1}; // 0 is reserved for loose nodes
44
+ std::atomic<uint32_t> g_nextLooseSlot{0};
45
+ } // namespace
46
+
47
+ NodeNumbering* currentNodeNumbering() { return g_numbering; }
48
+ uint32_t nextLooseSlot() { return g_nextLooseSlot.fetch_add(1, std::memory_order_relaxed); }
49
+
50
+ ParseNumberingScope::ParseNumberingScope()
51
+ : numbering_{g_nextTreeId.fetch_add(1, std::memory_order_relaxed), 0},
52
+ previous_(g_numbering),
53
+ installed_(g_numbering == nullptr) {
54
+ // Only the outermost scope installs: a nested one keeps the outer
55
+ // numbering so a parse plus its comment-attach pass stay one tree.
56
+ if (installed_) g_numbering = &numbering_;
57
+ }
58
+
59
+ ParseNumberingScope::~ParseNumberingScope() {
60
+ if (installed_) g_numbering = previous_;
61
+ }
62
+
63
+ ScopeTableScope::ScopeTableScope(ScopeTable& table) : previous_(g_scopeTable) { g_scopeTable = &table; }
64
+ ScopeTableScope::~ScopeTableScope() { g_scopeTable = previous_; }
65
+
66
+ void Scope::adoptTable(std::unique_ptr<ScopeTable> table) { ownedTable_ = std::move(table); }
67
+
68
+ void ASTNode::setScope(Scope& s) {
69
+ // No table installed means nobody is recording scopes -- a parser-only
70
+ // caller walking a tree for its own reasons. Dropping the write keeps
71
+ // that a no-op rather than a crash.
72
+ if (g_scopeTable) g_scopeTable->set(*this, &s);
73
+ }
74
+
75
+ } // namespace oscad