openscad-cpp-evaluator 0.15.0__tar.gz → 0.16.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 (196) hide show
  1. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/bindings/CMakeLists.txt +1 -0
  3. openscad_cpp_evaluator-0.16.0/bindings/ast_to_py.cpp +326 -0
  4. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/bindings/module.cpp +14 -0
  5. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/pyproject.toml +1 -1
  6. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/python/openscad_cpp_evaluator/__init__.py +36 -1
  7. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_python_bindings.py +116 -0
  8. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/.github/workflows/ci.yml +0 -0
  9. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/.github/workflows/wheels.yml +0 -0
  10. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/.gitignore +0 -0
  11. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/.gitmodules +0 -0
  12. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/CLAUDE.md +0 -0
  13. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/CMakeLists.txt +0 -0
  14. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/LICENSE +0 -0
  15. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/README.md +0 -0
  16. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/cmake/embed_font.cmake +0 -0
  17. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/examples/CMakeLists.txt +0 -0
  18. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/examples/eval_perf_benchmark.cpp +0 -0
  19. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/examples/manifold_cache_reuse.cpp +0 -0
  20. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/examples/minimal_debugger.cpp +0 -0
  21. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  22. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/.gitignore +0 -0
  23. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  24. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  25. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/README.md +0 -0
  26. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  27. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  28. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  29. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  30. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  31. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  32. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  33. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  34. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  35. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  36. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  37. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  38. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  39. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  40. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  41. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  42. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  43. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  44. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  45. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  46. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  47. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  48. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  49. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  50. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  51. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  52. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  53. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  54. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  55. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  56. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  57. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  58. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  59. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  60. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  61. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  62. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  63. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  64. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  65. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  66. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  67. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  68. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  69. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  70. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  71. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  72. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  73. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  74. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  75. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  76. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  77. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  78. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  79. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  80. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  81. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  82. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  83. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  84. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  85. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  86. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  87. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  88. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  89. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  90. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  91. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  92. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  93. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  94. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  95. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  96. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  97. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  98. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  99. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  100. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  101. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  102. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  103. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  104. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  105. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  106. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  107. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  108. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  109. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  110. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  111. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  112. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  113. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  114. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  115. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  116. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  117. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  118. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/resources/fonts/LICENSE +0 -0
  119. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  120. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/CMakeLists.txt +0 -0
  121. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/booleans.cpp +0 -0
  122. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/builtins.hpp +0 -0
  123. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/call_args.cpp +0 -0
  124. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/color.cpp +0 -0
  125. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/control.cpp +0 -0
  126. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/extrude.cpp +0 -0
  127. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/function_builtins.cpp +0 -0
  128. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/import.cpp +0 -0
  129. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/modifiers.cpp +0 -0
  130. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/primitives_2d.cpp +0 -0
  131. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/primitives_3d.cpp +0 -0
  132. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/registry.cpp +0 -0
  133. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/roof.cpp +0 -0
  134. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/segments.cpp +0 -0
  135. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/surface.cpp +0 -0
  136. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/text.cpp +0 -0
  137. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/topology.cpp +0 -0
  138. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/transforms.cpp +0 -0
  139. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/bytecode_compiler.cpp +0 -0
  140. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/bytecode_vm.cpp +0 -0
  141. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/colored_body.cpp +0 -0
  142. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/csg_generate.cpp +0 -0
  143. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/csg_resolve.cpp +0 -0
  144. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/css_colors.cpp +0 -0
  145. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/debug/debug_repl.cpp +0 -0
  146. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/debug_profile.cpp +0 -0
  147. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/eval_context.cpp +0 -0
  148. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/eval_error.cpp +0 -0
  149. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/eval_use.cpp +0 -0
  150. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/export.cpp +0 -0
  151. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/expr_eval.cpp +0 -0
  152. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/import/dxf_import.cpp +0 -0
  153. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/import/mesh_import.cpp +0 -0
  154. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/import/surface_load.cpp +0 -0
  155. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/import/svg_import.cpp +0 -0
  156. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/manifold_cache.cpp +0 -0
  157. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/stmt_eval.cpp +0 -0
  158. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/text/stb_font_provider.cpp +0 -0
  159. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/text/text_metrics.cpp +0 -0
  160. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/user_calls.cpp +0 -0
  161. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/value.cpp +0 -0
  162. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/src/zip_stored.cpp +0 -0
  163. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/CMakeLists.txt +0 -0
  164. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_booleans.cpp +0 -0
  165. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_bytecode_compiler.cpp +0 -0
  166. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_cli.cpp +0 -0
  167. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_control_flow.cpp +0 -0
  168. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_csg_tree.cpp +0 -0
  169. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_debug_hooks.cpp +0 -0
  170. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_dxf_svg_import.cpp +0 -0
  171. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_eval_context.cpp +0 -0
  172. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_export.cpp +0 -0
  173. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_expr_eval.cpp +0 -0
  174. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_extrude_roof.cpp +0 -0
  175. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_function_builtins.cpp +0 -0
  176. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_helpers.hpp +0 -0
  177. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_import_export.cpp +0 -0
  178. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_manifold_cache.cpp +0 -0
  179. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_multi_color_merge.cpp +0 -0
  180. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_primitives.cpp +0 -0
  181. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_profiling.cpp +0 -0
  182. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_scope_trail.cpp +0 -0
  183. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_scoping.cpp +0 -0
  184. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_surface.cpp +0 -0
  185. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_tail_calls.cpp +0 -0
  186. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_text.cpp +0 -0
  187. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_transforms.cpp +0 -0
  188. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_use.cpp +0 -0
  189. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_value.cpp +0 -0
  190. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_viewport_params.cpp +0 -0
  191. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tests/test_zip_stored.cpp +0 -0
  192. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/CMakeLists.txt +0 -0
  193. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/cli_lib.cpp +0 -0
  194. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/cli_lib.hpp +0 -0
  195. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/main.cpp +0 -0
  196. {openscad_cpp_evaluator-0.15.0 → openscad_cpp_evaluator-0.16.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.15.0
3
+ Version: 0.16.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -15,6 +15,7 @@ find_package(nanobind CONFIG REQUIRED)
15
15
  # from-source Manifold/Boost rebuild per interpreter in CI.
16
16
  nanobind_add_module(_openscad_cpp_evaluator NB_STATIC STABLE_ABI
17
17
  module.cpp
18
+ ast_to_py.cpp
18
19
  )
19
20
 
20
21
  # The static evaluator lib PUBLIC-links parser + manifold + Boost::polygon,
@@ -0,0 +1,326 @@
1
+ // Snapshots the parser's AST into plain Python dicts.
2
+ //
3
+ // Deliberately a COPY rather than wrappers around live ASTNode pointers.
4
+ // The tree is owned by a `std::vector<std::unique_ptr<ASTNode>>` local to
5
+ // whichever binding parsed it, and dies when that call returns; handing
6
+ // Python raw pointers into it would be a use-after-free the moment anything
7
+ // outlived the call, and this project has already shipped one dangling
8
+ // pointer of exactly that shape (the bodyCtx bug fixed in v0.13.1). It also
9
+ // sidesteps the threading problem entirely -- a render worker thread can
10
+ // parse, hand the snapshot to the GUI thread, and exit, with nothing shared.
11
+ //
12
+ // The cost is one walk plus allocation per parse. That is affordable
13
+ // because callers ask for this explicitly (parse_ast()), not on the render
14
+ // path, which never needs it.
15
+ //
16
+ // Every node dict carries at least:
17
+ // kind -- str, the NodeKind name ("NumberLiteral", "ModularCall", ...)
18
+ // position -- dict(origin, line, column, start_offset, end_offset)
19
+ // plus that kind's own fields, named in snake_case. Child nodes are nested
20
+ // dicts; child lists are lists of dicts; an absent optional child is None.
21
+ //
22
+ // `position.start_offset`/`end_offset` are the point of the whole exercise:
23
+ // they let a caller recover the ORIGINAL source text of any subexpression
24
+ // and reuse it verbatim, rather than re-serialising a parsed value and
25
+ // losing how the author wrote it (1.500 -> 1.5, 1e3 -> 1000).
26
+
27
+ #include "openscad_cpp_parser/api.hpp"
28
+ #include "openscad_cpp_parser/ast/ast_node.hpp"
29
+ #include "openscad_cpp_parser/ast/comments.hpp"
30
+ #include "openscad_cpp_parser/ast/declarations.hpp"
31
+ #include "openscad_cpp_parser/ast/expression.hpp"
32
+ #include "openscad_cpp_parser/ast/module_instantiation.hpp"
33
+ #include "openscad_cpp_parser/ast/vector_element.hpp"
34
+
35
+ #include <nanobind/nanobind.h>
36
+ #include <nanobind/stl/string.h>
37
+
38
+ #include <memory>
39
+ #include <vector>
40
+
41
+ namespace nb = nanobind;
42
+
43
+ namespace oscadbind {
44
+
45
+ namespace {
46
+
47
+ nb::dict positionToPy(const oscad::Position& p) {
48
+ nb::dict d;
49
+ // origin is "" for the main file (matching how the evaluator reports
50
+ // it); left as-is rather than mapped to None, so a caller can compare
51
+ // it against a path without a null check.
52
+ d["origin"] = p.origin;
53
+ d["line"] = p.line;
54
+ d["column"] = p.column;
55
+ d["start_offset"] = p.start_offset;
56
+ d["end_offset"] = p.end_offset;
57
+ return d;
58
+ }
59
+
60
+ nb::object nodeToPy(const oscad::ASTNode* n);
61
+
62
+ // A null unique_ptr child (e.g. a parameter with no default) becomes None
63
+ // rather than being omitted, so a consumer can index the key unconditionally.
64
+ template <typename T>
65
+ nb::object childToPy(const std::unique_ptr<T>& p) {
66
+ return p ? nodeToPy(p.get()) : nb::none();
67
+ }
68
+
69
+ template <typename T>
70
+ nb::list listToPy(const std::vector<std::unique_ptr<T>>& v) {
71
+ nb::list out;
72
+ for (const std::unique_ptr<T>& item : v) out.append(nodeToPy(item.get()));
73
+ return out;
74
+ }
75
+
76
+ nb::dict baseDict(const oscad::ASTNode* n) {
77
+ nb::dict d;
78
+ d["kind"] = oscad::nodeKindName(n->kind());
79
+ d["position"] = positionToPy(n->position());
80
+ return d;
81
+ }
82
+
83
+ nb::object nodeToPy(const oscad::ASTNode* n) {
84
+ if (n == nullptr) return nb::none();
85
+ nb::dict d = baseDict(n);
86
+
87
+ // kind() is authoritative about the concrete type (every leaf class sets
88
+ // its own), so static_cast is safe here and avoids a dynamic_cast per
89
+ // node on what is already an allocation-heavy walk.
90
+ #define OSC_NODE(KindName, Type) case oscad::NodeKind::KindName: { const auto* x = static_cast<const oscad::Type*>(n); (void)x;
91
+ #define OSC_END break; }
92
+
93
+ switch (n->kind()) {
94
+ // -- comments ---------------------------------------------------
95
+ OSC_NODE(CommentLine, CommentLine) d["text"] = x->text; OSC_END
96
+ OSC_NODE(BlankLine, BlankLine) OSC_END
97
+ OSC_NODE(CommentSpan, CommentSpan) d["text"] = x->text; OSC_END
98
+ OSC_NODE(CommentedExpr, CommentedExpr)
99
+ d["leading_comments"] = listToPy(x->leadingComments);
100
+ d["trailing_comments"] = listToPy(x->trailingComments);
101
+ d["expr"] = childToPy(x->expr);
102
+ OSC_END
103
+
104
+ // -- primaries / literals ---------------------------------------
105
+ OSC_NODE(Identifier, Identifier) d["name"] = x->name; OSC_END
106
+ // `val` is the DECODED string minus its quotes; escape sequences
107
+ // are kept raw (a source \n stays two characters). Use the span to
108
+ // recover exactly what was written.
109
+ OSC_NODE(StringLiteral, StringLiteral) d["val"] = x->val; OSC_END
110
+ // Parsed to double, so the source spelling (1.500, 1e3, 0x10) is
111
+ // NOT recoverable from here -- that is what the span is for.
112
+ OSC_NODE(NumberLiteral, NumberLiteral) d["val"] = x->val; OSC_END
113
+ OSC_NODE(BooleanLiteral, BooleanLiteral) d["val"] = x->val; OSC_END
114
+ OSC_NODE(UndefinedLiteral, UndefinedLiteral) OSC_END
115
+ OSC_NODE(RangeLiteral, RangeLiteral)
116
+ d["start"] = childToPy(x->start);
117
+ d["end"] = childToPy(x->end);
118
+ // Never null: the parser defaults an omitted step to 1.
119
+ d["step"] = childToPy(x->step);
120
+ OSC_END
121
+
122
+ // -- declarations / arguments -----------------------------------
123
+ OSC_NODE(ParameterDeclaration, ParameterDeclaration)
124
+ d["name"] = childToPy(x->name);
125
+ d["default_value"] = childToPy(x->defaultValue); // nullable
126
+ d["leading_comments"] = listToPy(x->leadingComments);
127
+ d["trailing_comments"] = listToPy(x->trailingComments);
128
+ OSC_END
129
+ OSC_NODE(PositionalArgument, PositionalArgument) d["expr"] = childToPy(x->expr); OSC_END
130
+ OSC_NODE(NamedArgument, NamedArgument)
131
+ d["name"] = childToPy(x->name);
132
+ d["expr"] = childToPy(x->expr);
133
+ OSC_END
134
+ OSC_NODE(Assignment, Assignment)
135
+ d["name"] = childToPy(x->name);
136
+ d["expr"] = childToPy(x->expr);
137
+ OSC_END
138
+
139
+ // -- prefix expression forms ------------------------------------
140
+ OSC_NODE(LetOp, LetOp)
141
+ d["assignments"] = listToPy(x->assignments);
142
+ d["body"] = childToPy(x->body);
143
+ OSC_END
144
+ OSC_NODE(EchoOp, EchoOp)
145
+ d["arguments"] = listToPy(x->arguments);
146
+ d["body"] = childToPy(x->body);
147
+ OSC_END
148
+ OSC_NODE(AssertOp, AssertOp)
149
+ d["arguments"] = listToPy(x->arguments);
150
+ d["body"] = childToPy(x->body);
151
+ OSC_END
152
+ OSC_NODE(FunctionLiteral, FunctionLiteral)
153
+ d["parameters"] = listToPy(x->parameters);
154
+ d["body"] = childToPy(x->body);
155
+ OSC_END
156
+
157
+ // -- unary operators --------------------------------------------
158
+ #define OSC_UNARY(KindName) OSC_NODE(KindName, KindName) d["expr"] = childToPy(x->expr); OSC_END
159
+ OSC_UNARY(UnaryMinusOp)
160
+ OSC_UNARY(LogicalNotOp)
161
+ OSC_UNARY(BitwiseNotOp)
162
+ #undef OSC_UNARY
163
+
164
+ // -- binary operators -------------------------------------------
165
+ #define OSC_BINARY(KindName) \
166
+ OSC_NODE(KindName, KindName) d["left"] = childToPy(x->left); d["right"] = childToPy(x->right); OSC_END
167
+ OSC_BINARY(AdditionOp)
168
+ OSC_BINARY(SubtractionOp)
169
+ OSC_BINARY(MultiplicationOp)
170
+ OSC_BINARY(DivisionOp)
171
+ OSC_BINARY(ModuloOp)
172
+ OSC_BINARY(ExponentOp)
173
+ OSC_BINARY(BitwiseAndOp)
174
+ OSC_BINARY(BitwiseOrOp)
175
+ OSC_BINARY(BitwiseShiftLeftOp)
176
+ OSC_BINARY(BitwiseShiftRightOp)
177
+ OSC_BINARY(LogicalAndOp)
178
+ OSC_BINARY(LogicalOrOp)
179
+ OSC_BINARY(EqualityOp)
180
+ OSC_BINARY(InequalityOp)
181
+ OSC_BINARY(GreaterThanOp)
182
+ OSC_BINARY(GreaterThanOrEqualOp)
183
+ OSC_BINARY(LessThanOp)
184
+ OSC_BINARY(LessThanOrEqualOp)
185
+ #undef OSC_BINARY
186
+
187
+ OSC_NODE(TernaryOp, TernaryOp)
188
+ d["condition"] = childToPy(x->condition);
189
+ d["true_expr"] = childToPy(x->trueExpr);
190
+ d["false_expr"] = childToPy(x->falseExpr);
191
+ OSC_END
192
+
193
+ // -- postfix ----------------------------------------------------
194
+ OSC_NODE(PrimaryCall, PrimaryCall)
195
+ d["left"] = childToPy(x->left);
196
+ d["arguments"] = listToPy(x->arguments);
197
+ OSC_END
198
+ OSC_NODE(PrimaryIndex, PrimaryIndex)
199
+ d["left"] = childToPy(x->left);
200
+ d["index"] = childToPy(x->index);
201
+ OSC_END
202
+ OSC_NODE(PrimaryMember, PrimaryMember)
203
+ d["left"] = childToPy(x->left);
204
+ d["member"] = childToPy(x->member);
205
+ OSC_END
206
+
207
+ // -- list comprehension clauses ---------------------------------
208
+ OSC_NODE(ListCompLet, ListCompLet)
209
+ d["assignments"] = listToPy(x->assignments);
210
+ d["body"] = childToPy(x->body);
211
+ OSC_END
212
+ OSC_NODE(ListCompEach, ListCompEach) d["body"] = childToPy(x->body); OSC_END
213
+ OSC_NODE(ListCompFor, ListCompFor)
214
+ d["assignments"] = listToPy(x->assignments);
215
+ d["body"] = childToPy(x->body);
216
+ OSC_END
217
+ OSC_NODE(ListCompCFor, ListCompCFor)
218
+ d["inits"] = listToPy(x->inits);
219
+ d["condition"] = childToPy(x->condition);
220
+ d["incrs"] = listToPy(x->incrs);
221
+ d["body"] = childToPy(x->body);
222
+ OSC_END
223
+ OSC_NODE(ListCompIf, ListCompIf)
224
+ d["condition"] = childToPy(x->condition);
225
+ d["true_expr"] = childToPy(x->trueExpr);
226
+ OSC_END
227
+ OSC_NODE(ListCompIfElse, ListCompIfElse)
228
+ d["condition"] = childToPy(x->condition);
229
+ d["true_expr"] = childToPy(x->trueExpr);
230
+ d["false_expr"] = childToPy(x->falseExpr);
231
+ OSC_END
232
+ // Also how a plain vector literal ([1,2,3]) is represented --
233
+ // elements mix comprehension clauses and bare expressions.
234
+ OSC_NODE(ListComprehension, ListComprehension) d["elements"] = listToPy(x->elements); OSC_END
235
+
236
+ // -- module instantiation ---------------------------------------
237
+ OSC_NODE(ModularCall, ModularCall)
238
+ d["name"] = childToPy(x->name);
239
+ d["arguments"] = listToPy(x->arguments);
240
+ d["children"] = listToPy(x->children);
241
+ OSC_END
242
+ OSC_NODE(ModularFor, ModularFor)
243
+ d["assignments"] = listToPy(x->assignments);
244
+ d["body"] = listToPy(x->body);
245
+ OSC_END
246
+ OSC_NODE(ModularIntersectionFor, ModularIntersectionFor)
247
+ d["assignments"] = listToPy(x->assignments);
248
+ d["body"] = listToPy(x->body);
249
+ OSC_END
250
+ OSC_NODE(ModularLet, ModularLet)
251
+ d["assignments"] = listToPy(x->assignments);
252
+ d["children"] = listToPy(x->children);
253
+ OSC_END
254
+ OSC_NODE(ModularEcho, ModularEcho)
255
+ d["arguments"] = listToPy(x->arguments);
256
+ d["children"] = listToPy(x->children);
257
+ OSC_END
258
+ OSC_NODE(ModularAssert, ModularAssert)
259
+ d["arguments"] = listToPy(x->arguments);
260
+ d["children"] = listToPy(x->children);
261
+ OSC_END
262
+ OSC_NODE(ModularIf, ModularIf)
263
+ d["condition"] = childToPy(x->condition);
264
+ d["true_branch"] = listToPy(x->trueBranch);
265
+ OSC_END
266
+ OSC_NODE(ModularIfElse, ModularIfElse)
267
+ d["condition"] = childToPy(x->condition);
268
+ d["true_branch"] = listToPy(x->trueBranch);
269
+ d["false_branch"] = listToPy(x->falseBranch);
270
+ OSC_END
271
+
272
+ // -- modifier wrappers (! # % *) --------------------------------
273
+ #define OSC_MODIFIER(KindName) OSC_NODE(KindName, KindName) d["child"] = childToPy(x->child); OSC_END
274
+ OSC_MODIFIER(ModularModifierShowOnly)
275
+ OSC_MODIFIER(ModularModifierHighlight)
276
+ OSC_MODIFIER(ModularModifierBackground)
277
+ OSC_MODIFIER(ModularModifierDisable)
278
+ #undef OSC_MODIFIER
279
+
280
+ // -- top-level declarations -------------------------------------
281
+ OSC_NODE(ModuleDeclaration, ModuleDeclaration)
282
+ d["name"] = childToPy(x->name);
283
+ d["parameters"] = listToPy(x->parameters);
284
+ d["children"] = listToPy(x->children);
285
+ d["pre_name_comments"] = listToPy(x->preNameComments);
286
+ d["post_name_comments"] = listToPy(x->postNameComments);
287
+ d["post_params_comments"] = listToPy(x->postParamsComments);
288
+ OSC_END
289
+ OSC_NODE(FunctionDeclaration, FunctionDeclaration)
290
+ d["name"] = childToPy(x->name);
291
+ d["parameters"] = listToPy(x->parameters);
292
+ d["expr"] = childToPy(x->expr);
293
+ d["pre_name_comments"] = listToPy(x->preNameComments);
294
+ d["post_name_comments"] = listToPy(x->postNameComments);
295
+ d["post_params_comments"] = listToPy(x->postParamsComments);
296
+ OSC_END
297
+ OSC_NODE(UseStatement, UseStatement) d["filepath"] = childToPy(x->filepath); OSC_END
298
+ OSC_NODE(IncludeStatement, IncludeStatement) d["filepath"] = childToPy(x->filepath); OSC_END
299
+ }
300
+
301
+ #undef OSC_NODE
302
+ #undef OSC_END
303
+
304
+ // No default: above -- the switch is exhaustive over NodeKind, so
305
+ // adding a node kind to the parser without handling it here is a
306
+ // compiler warning rather than a silently field-less dict.
307
+ return d;
308
+ }
309
+
310
+ } // namespace
311
+
312
+ nb::list astToPy(const std::vector<std::unique_ptr<oscad::ASTNode>>& ast) {
313
+ nb::list out;
314
+ for (const std::unique_ptr<oscad::ASTNode>& n : ast) out.append(nodeToPy(n.get()));
315
+ return out;
316
+ }
317
+
318
+ nb::list parseAstFromFile(const std::string& path, bool includeComments) {
319
+ return astToPy(oscad::getASTFromFile(path, includeComments));
320
+ }
321
+
322
+ nb::list parseAstFromString(const std::string& code, bool includeComments) {
323
+ return astToPy(oscad::getASTFromString(code, includeComments));
324
+ }
325
+
326
+ } // namespace oscadbind
@@ -40,6 +40,14 @@
40
40
 
41
41
  namespace nb = nanobind;
42
42
 
43
+ // Defined in ast_to_py.cpp -- snapshots the parser's AST into plain Python
44
+ // dicts. Declared here rather than in a header since these two entry points
45
+ // are the whole interface and only this file calls them.
46
+ namespace oscadbind {
47
+ nb::list parseAstFromFile(const std::string& path, bool includeComments);
48
+ nb::list parseAstFromString(const std::string& code, bool includeComments);
49
+ } // namespace oscadbind
50
+
43
51
  namespace {
44
52
 
45
53
  // Moves a std::vector's storage into a NumPy array that owns it via a
@@ -649,6 +657,12 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
649
657
  "Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit).");
650
658
  m.def("parse_decls", &parseDecls, nb::arg("path"),
651
659
  "Parse a .scad file; return top-level declaration (namespace, name, start, end, line, column, origin) tuples.");
660
+ m.def("parse_ast", &oscadbind::parseAstFromFile, nb::arg("path"), nb::arg("include_comments") = false,
661
+ "Parse a .scad file; return the AST as a list of plain nested dicts "
662
+ "(kind, position, plus that kind's own fields). A snapshot, not a view -- "
663
+ "safe to keep, share across threads, and outlive the parse.");
664
+ m.def("parse_ast_string", &oscadbind::parseAstFromString, nb::arg("code"), nb::arg("include_comments") = false,
665
+ "As parse_ast(), but parses a source string instead of a file.");
652
666
  m.def("debug_evaluate", &debugEvaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("debug_hook"),
653
667
  nb::arg("error_break"), nb::arg("echo_fn"), nb::arg("manifold_cache") = nullptr,
654
668
  nb::arg("return_hook") = nb::none(), nb::arg("fast_continue_signal") = nullptr,
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.15.0"
7
+ version = "0.16.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -18,7 +18,7 @@ from ._openscad_cpp_evaluator import FastContinueSignal, ManifoldCache
18
18
  __all__ = [
19
19
  "Evaluator", "ColoredBody", "EvalError", "ParseError", "OscObject", "parse", "to_renderable_bodies",
20
20
  "ManifoldCache", "CallSiteProfile", "ProfileResult", "format_csg_tree", "bodies_from_dicts",
21
- "FastContinueSignal",
21
+ "FastContinueSignal", "parse_ast", "parse_ast_string",
22
22
  ]
23
23
 
24
24
 
@@ -256,6 +256,41 @@ def parse(path: str) -> RootScope:
256
256
  return RootScope(decls)
257
257
 
258
258
 
259
+ def parse_ast(path: str, include_comments: bool = False) -> list:
260
+ """Parse a .scad file; return its AST as a list of plain nested dicts.
261
+
262
+ Each node is `{"kind": str, "position": {...}, **fields}`, where fields
263
+ are that kind's own (e.g. NumberLiteral has "val"; ModularCall has
264
+ "name"/"arguments"/"children"). Child nodes nest as dicts, child lists
265
+ as lists, and an absent optional child is None.
266
+
267
+ `position` carries `start_offset`/`end_offset`, which is the point of
268
+ this API: they let a caller slice the ORIGINAL source text of any
269
+ subexpression and reuse it verbatim, instead of re-serialising a parsed
270
+ value and losing how it was written (`1.500` -> `1.5`, `1e3` -> `1000`).
271
+
272
+ This is a SNAPSHOT, not a view of live parser memory: it is safe to
273
+ keep, mutate, share across threads, and outlive the parse. That costs
274
+ one walk per call, which is why it is a separate entry point rather
275
+ than something evaluate() always pays for.
276
+
277
+ Raises ParseError on a syntax error.
278
+ """
279
+ try:
280
+ return _ext.parse_ast(path, include_comments)
281
+ except Exception as e:
282
+ raise ParseError(str(e)) from e
283
+
284
+
285
+ def parse_ast_string(code: str, include_comments: bool = False) -> list:
286
+ """As `parse_ast`, but parses a source string. `position.origin` is
287
+ "<string>" for every node. Raises ParseError on a syntax error."""
288
+ try:
289
+ return _ext.parse_ast_string(code, include_comments)
290
+ except Exception as e:
291
+ raise ParseError(str(e)) from e
292
+
293
+
259
294
  class _MeshShim:
260
295
  """Duck-types a manifold3d mesh: the four arrays the renderer reads."""
261
296
  __slots__ = ("vert_properties", "tri_verts", "run_original_id", "run_index", "num_prop")
@@ -232,6 +232,122 @@ def test_role_defaults_to_normal_and_reflects_modifiers():
232
232
  assert roles == ["highlight", "show_only"]
233
233
 
234
234
 
235
+ # -- parse_ast: AST snapshot ---------------------------------------------
236
+
237
+
238
+ def test_parse_ast_spans_recover_original_number_text():
239
+ # The whole point of exposing spans: a parsed NumberLiteral is a double,
240
+ # so the source spelling is gone from `val` -- but the span still points
241
+ # at it, letting a rewriter reuse the author's own text verbatim.
242
+ from openscad_cpp_evaluator import parse_ast_string
243
+ src = "translate([1.500, 2.250, 1e3]) cube(2);"
244
+ vec = parse_ast_string(src)[0]["arguments"][0]["expr"]
245
+ spellings = [src[e["position"]["start_offset"]:e["position"]["end_offset"]] for e in vec["elements"]]
246
+ assert spellings == ["1.500", "2.250", "1e3"], spellings
247
+ assert [e["val"] for e in vec["elements"]] == [1.5, 2.25, 1000.0]
248
+
249
+
250
+ def test_parse_ast_node_shapes():
251
+ from openscad_cpp_evaluator import parse_ast_string
252
+ ast = parse_ast_string("module m(a, b=2) { cube(a); } m(1, b=3) sphere(2);")
253
+ decl, call = ast
254
+ assert decl["kind"] == "ModuleDeclaration"
255
+ assert decl["name"]["name"] == "m"
256
+ assert [p["name"]["name"] for p in decl["parameters"]] == ["a", "b"]
257
+ # An absent optional child is None, not a missing key.
258
+ assert decl["parameters"][0]["default_value"] is None
259
+ assert decl["parameters"][1]["default_value"]["val"] == 2.0
260
+ assert call["kind"] == "ModularCall"
261
+ assert [a["kind"] for a in call["arguments"]] == ["PositionalArgument", "NamedArgument"]
262
+ assert call["arguments"][1]["name"]["name"] == "b"
263
+ assert [c["kind"] for c in call["children"]] == ["ModularCall"]
264
+
265
+
266
+ def test_parse_ast_covers_every_node_kind_reachable_from_source():
267
+ # Guards the exhaustive switch in ast_to_py.cpp: a node kind added to
268
+ # the parser without a case here would produce a dict with only
269
+ # kind/position, silently losing its fields.
270
+ from openscad_cpp_evaluator import parse_ast_string
271
+ src = """
272
+ include <x.scad>
273
+ use <y.scad>
274
+ A = 1; B = "s"; C = true; D = undef; E = [0:2:10]; F = -A; G = !C; H = ~1;
275
+ I = A+1-2*3/4%5^2; J = A<1 && A>2 || A<=3 && A>=4 && A==5 && A!=6;
276
+ K = A&1 | 2 << 3 >> 4;
277
+ L = C ? A : B; M = [1,2,3][0]; N = let(x=1) x; O = echo("e") 1;
278
+ P = assert(true) 2; Q = function(a) a; R = [for (i=[0:3]) i*2];
279
+ S = [for (i=[0:3]) if (i%2==0) i else -i];
280
+ S2 = [for (i=[0:3]) if (i%2==0) i]; // bare if -> ListCompIf
281
+ T = [for (i=0; i<3; i=i+1) i]; U = [for (v=[[1,2]]) each v];
282
+ V = [let(z=2) for (i=[0:1]) z*i]; W = object(a=1).a;
283
+ function fn(p) = p; module mod(m) { cube(m); children(); }
284
+ mod(1) { sphere(2); }
285
+ for (i=[0:1]) cube(1);
286
+ intersection_for (i=[0:1]) cube(2);
287
+ let (t=1) cube(t); echo("hi") cube(1); assert(true) cube(1);
288
+ if (A>0) cube(1); else sphere(1);
289
+ if (A>0) cube(1); // bare if -> ModularIf
290
+ !cube(1); #cube(1); %cube(1); *cube(1);
291
+ """
292
+ seen = set()
293
+
294
+ def walk(n):
295
+ if isinstance(n, dict) and "kind" in n:
296
+ seen.add(n["kind"])
297
+ pos = n["position"]
298
+ assert pos["end_offset"] >= pos["start_offset"], n["kind"]
299
+ for k, v in n.items():
300
+ if k not in ("kind", "position"):
301
+ walk(v)
302
+ elif isinstance(n, list):
303
+ for x in n:
304
+ walk(x)
305
+
306
+ for node in parse_ast_string(src, True):
307
+ walk(node)
308
+ # Every kind above, minus the three only produced by comment
309
+ # preservation on a file with comments in the right places.
310
+ expected = {
311
+ "IncludeStatement", "UseStatement", "Assignment", "Identifier", "NumberLiteral", "StringLiteral",
312
+ "BooleanLiteral", "UndefinedLiteral", "RangeLiteral", "UnaryMinusOp", "LogicalNotOp", "BitwiseNotOp",
313
+ "AdditionOp", "SubtractionOp", "MultiplicationOp", "DivisionOp", "ModuloOp", "ExponentOp",
314
+ "BitwiseAndOp", "BitwiseOrOp", "BitwiseShiftLeftOp", "BitwiseShiftRightOp", "LogicalAndOp",
315
+ "LogicalOrOp", "EqualityOp", "InequalityOp", "GreaterThanOp", "GreaterThanOrEqualOp", "LessThanOp",
316
+ "LessThanOrEqualOp", "TernaryOp", "PrimaryCall", "PrimaryIndex", "PrimaryMember", "LetOp", "EchoOp",
317
+ "AssertOp", "FunctionLiteral", "ListComprehension", "ListCompFor", "ListCompIf", "ListCompIfElse",
318
+ "ListCompCFor", "ListCompEach", "ListCompLet", "FunctionDeclaration", "ModuleDeclaration",
319
+ "ParameterDeclaration", "PositionalArgument", "NamedArgument", "ModularCall", "ModularFor",
320
+ "ModularIntersectionFor", "ModularLet", "ModularEcho", "ModularAssert", "ModularIf", "ModularIfElse",
321
+ "ModularModifierShowOnly", "ModularModifierHighlight", "ModularModifierBackground",
322
+ "ModularModifierDisable",
323
+ }
324
+ missing = expected - seen
325
+ assert not missing, f"node kinds not emitted: {sorted(missing)}"
326
+
327
+
328
+ def test_parse_ast_snapshot_outlives_the_parse():
329
+ # It is a copy, not a view into parser memory -- the point of the
330
+ # snapshot design. Holding it after many further parses (which reuse
331
+ # and free the same arenas) must not corrupt it.
332
+ from openscad_cpp_evaluator import parse_ast_string
333
+ held = parse_ast_string("a = 1.500;")
334
+ for i in range(200):
335
+ parse_ast_string(f"b{i} = [for (j=[0:20]) j*{i}];")
336
+ assert held[0]["expr"]["val"] == 1.5
337
+ assert held[0]["name"]["name"] == "a"
338
+ held[0]["expr"]["val"] = 99.0 # plain dicts: mutable, owned by the caller
339
+ assert held[0]["expr"]["val"] == 99.0
340
+
341
+
342
+ def test_parse_ast_raises_parse_error_on_bad_syntax():
343
+ from openscad_cpp_evaluator import ParseError, parse_ast_string
344
+ try:
345
+ parse_ast_string("cube(")
346
+ except ParseError:
347
+ return
348
+ raise AssertionError("expected ParseError")
349
+
350
+
235
351
  def main():
236
352
  tests = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)]
237
353
  failures = []