openscad-cpp-evaluator 0.14.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.14.0 → openscad_cpp_evaluator-0.16.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.14.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.14.0 → openscad_cpp_evaluator-0.16.0}/bindings/module.cpp +23 -3
  5. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/colored_body.hpp +21 -0
  6. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/evaluator.hpp +12 -0
  7. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/pyproject.toml +1 -1
  8. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/python/openscad_cpp_evaluator/__init__.py +36 -1
  9. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/booleans.cpp +15 -2
  10. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/builtins.hpp +13 -1
  11. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/import.cpp +33 -24
  12. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/primitives_3d.cpp +64 -0
  13. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/topology.cpp +2 -0
  14. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/csg_generate.cpp +23 -0
  15. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_import_export.cpp +8 -1
  16. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_primitives.cpp +92 -0
  17. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_python_bindings.py +116 -0
  18. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/.github/workflows/ci.yml +0 -0
  19. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/.github/workflows/wheels.yml +0 -0
  20. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/.gitignore +0 -0
  21. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/.gitmodules +0 -0
  22. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/CLAUDE.md +0 -0
  23. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/LICENSE +0 -0
  25. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/README.md +0 -0
  26. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/cmake/embed_font.cmake +0 -0
  27. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/examples/CMakeLists.txt +0 -0
  28. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/examples/eval_perf_benchmark.cpp +0 -0
  29. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/examples/manifold_cache_reuse.cpp +0 -0
  30. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/examples/minimal_debugger.cpp +0 -0
  31. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  32. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/.gitignore +0 -0
  33. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  34. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  35. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/README.md +0 -0
  36. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  37. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  38. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  39. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  40. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  41. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  42. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  43. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  44. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  45. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  46. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  47. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  48. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  49. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  50. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  51. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  52. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  53. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  54. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  55. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  56. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  57. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  58. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  59. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  60. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  61. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  62. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  63. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  64. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  65. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  66. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  67. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  68. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  69. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  70. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  71. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  72. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  73. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  74. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  75. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  76. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  77. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  78. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  79. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  80. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  81. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  82. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  83. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  84. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  85. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  86. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  87. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  88. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  89. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  90. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  91. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  92. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  93. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  94. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  95. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  96. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  97. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  98. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  99. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  100. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  101. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  102. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  103. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  104. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  105. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  106. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  107. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  108. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  109. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  110. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  111. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  112. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  113. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  114. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  115. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  116. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  117. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  118. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  119. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  120. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  121. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  122. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  123. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  124. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  125. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  126. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/resources/fonts/LICENSE +0 -0
  127. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  128. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/CMakeLists.txt +0 -0
  129. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/call_args.cpp +0 -0
  130. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/color.cpp +0 -0
  131. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/control.cpp +0 -0
  132. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/extrude.cpp +0 -0
  133. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/function_builtins.cpp +0 -0
  134. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/modifiers.cpp +0 -0
  135. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/primitives_2d.cpp +0 -0
  136. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/registry.cpp +0 -0
  137. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/roof.cpp +0 -0
  138. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/segments.cpp +0 -0
  139. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/surface.cpp +0 -0
  140. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/text.cpp +0 -0
  141. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/builtins/transforms.cpp +0 -0
  142. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/bytecode_compiler.cpp +0 -0
  143. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/bytecode_vm.cpp +0 -0
  144. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/colored_body.cpp +0 -0
  145. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/csg_resolve.cpp +0 -0
  146. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/css_colors.cpp +0 -0
  147. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/debug/debug_repl.cpp +0 -0
  148. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/debug_profile.cpp +0 -0
  149. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/eval_context.cpp +0 -0
  150. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/eval_error.cpp +0 -0
  151. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/eval_use.cpp +0 -0
  152. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/export.cpp +0 -0
  153. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/expr_eval.cpp +0 -0
  154. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/import/dxf_import.cpp +0 -0
  155. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/import/mesh_import.cpp +0 -0
  156. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/import/surface_load.cpp +0 -0
  157. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/import/svg_import.cpp +0 -0
  158. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/manifold_cache.cpp +0 -0
  159. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/stmt_eval.cpp +0 -0
  160. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/text/stb_font_provider.cpp +0 -0
  161. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/text/text_metrics.cpp +0 -0
  162. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/user_calls.cpp +0 -0
  163. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/value.cpp +0 -0
  164. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/src/zip_stored.cpp +0 -0
  165. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/CMakeLists.txt +0 -0
  166. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_booleans.cpp +0 -0
  167. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_bytecode_compiler.cpp +0 -0
  168. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_cli.cpp +0 -0
  169. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_control_flow.cpp +0 -0
  170. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_csg_tree.cpp +0 -0
  171. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_debug_hooks.cpp +0 -0
  172. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_dxf_svg_import.cpp +0 -0
  173. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_eval_context.cpp +0 -0
  174. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_export.cpp +0 -0
  175. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_expr_eval.cpp +0 -0
  176. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_extrude_roof.cpp +0 -0
  177. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_function_builtins.cpp +0 -0
  178. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_helpers.hpp +0 -0
  179. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_manifold_cache.cpp +0 -0
  180. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_multi_color_merge.cpp +0 -0
  181. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_profiling.cpp +0 -0
  182. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_scope_trail.cpp +0 -0
  183. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_scoping.cpp +0 -0
  184. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_surface.cpp +0 -0
  185. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_tail_calls.cpp +0 -0
  186. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_text.cpp +0 -0
  187. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_transforms.cpp +0 -0
  188. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_use.cpp +0 -0
  189. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_value.cpp +0 -0
  190. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_viewport_params.cpp +0 -0
  191. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tests/test_zip_stored.cpp +0 -0
  192. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/CMakeLists.txt +0 -0
  193. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/cli_lib.cpp +0 -0
  194. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/cli_lib.hpp +0 -0
  195. {openscad_cpp_evaluator-0.14.0 → openscad_cpp_evaluator-0.16.0}/tools/cli/main.cpp +0 -0
  196. {openscad_cpp_evaluator-0.14.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.14.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
@@ -129,7 +137,11 @@ const char* roleToString(oscadeval::BodyRole role) {
129
137
  // tri_verts (nTri, 3), run_original_id (nRun,), run_index (nRun+1,) -- the
130
138
  // same flat 3*triangle units manifold3d's own run_index uses).
131
139
  nb::dict bodyToDict(oscadeval::ColoredBody& cb) {
132
- manifold::MeshGL mesh = cb.body->GetMeshGL();
140
+ // A display-only body's Manifold is empty by construction, so its
141
+ // GetMeshGL() would hand back nothing -- take the raw triangle soup
142
+ // that failed to build instead. Everything downstream reads plain
143
+ // arrays, so the two cases are indistinguishable from here on.
144
+ manifold::MeshGL mesh = cb.isDisplayOnly() ? *cb.rawMesh : cb.body->GetMeshGL();
133
145
  const size_t numProp = mesh.numProp;
134
146
  const size_t numVert = numProp ? mesh.vertProperties.size() / numProp : 0;
135
147
  const size_t numTri = mesh.triVerts.size() / 3;
@@ -184,7 +196,9 @@ void collectIdSpans(const oscadeval::Evaluator& ev, std::vector<IdSpan>& out) {
184
196
  nb::list bodiesToList(std::vector<oscadeval::ColoredBody>& bodies) {
185
197
  nb::list out;
186
198
  for (oscadeval::ColoredBody& cb : bodies) {
187
- if (!cb.body || cb.body->IsEmpty()) continue;
199
+ // isDisplayOnly() bodies are ALWAYS IsEmpty() -- that's what made
200
+ // an open polyhedron() silently vanish here rather than render.
201
+ if (!cb.isDisplayOnly() && (!cb.body || cb.body->IsEmpty())) continue;
188
202
  out.append(bodyToDict(cb));
189
203
  }
190
204
  return out;
@@ -210,7 +224,7 @@ nb::object csgNodeToPy(const oscadeval::CSGNode& node) {
210
224
  for (const auto& [k, v] : node.params) params[nb::str(k.c_str())] = valueToPy(v);
211
225
  nb::list bodies;
212
226
  for (const oscadeval::ColoredBody& cb : node.bodies) {
213
- if (!cb.body || cb.body->IsEmpty()) continue;
227
+ if (!cb.isDisplayOnly() && (!cb.body || cb.body->IsEmpty())) continue;
214
228
  // ColoredBody here is const in this walk (tree ownership stays with
215
229
  // the caller) -- GetMeshGL() itself is logically read-only, so a
216
230
  // const_cast here is safe/local, mirroring bodyToDict's mutating
@@ -643,6 +657,12 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
643
657
  "Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit).");
644
658
  m.def("parse_decls", &parseDecls, nb::arg("path"),
645
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.");
646
666
  m.def("debug_evaluate", &debugEvaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("debug_hook"),
647
667
  nb::arg("error_break"), nb::arg("echo_fn"), nb::arg("manifold_cache") = nullptr,
648
668
  nb::arg("return_hook") = nb::none(), nb::arg("fast_continue_signal") = nullptr,
@@ -26,6 +26,27 @@ struct ColoredBody {
26
26
  bool flatPreview = false;
27
27
  BodyRole role = BodyRole::Normal;
28
28
  std::optional<std::vector<std::array<float, 4>>> triColors; // per-triangle RGBA, multi-color CSG merges only
29
+
30
+ // Set ONLY when a mesh could not be built into a valid Manifold -- in
31
+ // practice a polyhedron() whose faces don't close the surface (it has
32
+ // boundary edges), which Manifold reports by returning an *empty* body
33
+ // with Status() == NotManifold rather than by failing. Note the status
34
+ // name is misleading: a closed mesh with reversed winding, non-manifold
35
+ // vertices or self-intersections builds fine; only an OPEN one lands
36
+ // here.
37
+ //
38
+ // Carrying the raw triangle soup alongside lets the renderer still draw
39
+ // what the script described, which is far more use for spotting the
40
+ // missing face than an empty viewport. `body` is deliberately left set
41
+ // (and empty), so every existing `cb.body->...` path keeps working.
42
+ //
43
+ // Such a body is display-only: it can never take part in a CSG
44
+ // operation, since there is no Manifold for Manifold to operate on.
45
+ // splitByRole() pulls it aside much as it does a `%` background body,
46
+ // and the CSG/hull/minkowski generators re-join it unchanged.
47
+ std::optional<manifold::MeshGL> rawMesh;
48
+
49
+ bool isDisplayOnly() const { return rawMesh.has_value(); }
29
50
  };
30
51
 
31
52
  // `EvalContext::color`/CSGParams round-trip: a resolve function reads
@@ -236,6 +236,18 @@ public:
236
236
  // comment for why that reference exists at all).
237
237
  ColoredBody tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue);
238
238
 
239
+ // tagGenerated()'s counterpart for a mesh Manifold refused to build --
240
+ // an open polyhedron(). Returns a display-only ColoredBody (see
241
+ // ColoredBody::rawMesh) carrying the triangle soup as-is, so it can
242
+ // still be drawn even though no CSG operation can ever touch it.
243
+ //
244
+ // A failed Manifold has no run IDs of its own, so one is reserved here
245
+ // and written onto the raw mesh -- otherwise the body would be
246
+ // invisible to selection and to the originalID -> AST node mapping
247
+ // that drives click-to-source, which is exactly when a user most wants
248
+ // to click the broken thing and be shown the line that made it.
249
+ ColoredBody tagDisplayOnly(manifold::MeshGL mesh, const oscad::ASTNode& node, const Value& colorValue);
250
+
239
251
  // Raises an OpenSCAD-level runtime error: throws EvalError with the
240
252
  // formatted "ERROR: {msg}{TRACE lines}" message (see eval_error.hpp),
241
253
  // walking the real user function/module call stack for the TRACE lines
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.14.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")
@@ -27,8 +27,19 @@ RoleSplit splitByRole(const std::vector<ColoredBody>& bodies) {
27
27
  for (const ColoredBody& c : bodies) {
28
28
  if (c.role == BodyRole::Background) r.background.push_back(c);
29
29
  }
30
+ // Checked before the foreground sweep below and excluded from it: a
31
+ // display-only body has an empty Manifold, so letting it reach a
32
+ // boolean would silently zero the whole operation (the same failure
33
+ // mode invalid operands caused before they were filtered out).
30
34
  for (const ColoredBody& c : bodies) {
31
- if (c.role != BodyRole::Background && c.role != BodyRole::ShowOnly) r.foreground.push_back(c);
35
+ if (c.isDisplayOnly() && c.role != BodyRole::Background && c.role != BodyRole::ShowOnly) {
36
+ r.displayOnly.push_back(c);
37
+ }
38
+ }
39
+ for (const ColoredBody& c : bodies) {
40
+ if (c.role != BodyRole::Background && c.role != BodyRole::ShowOnly && !c.isDisplayOnly()) {
41
+ r.foreground.push_back(c);
42
+ }
32
43
  }
33
44
  for (const ColoredBody& c : r.foreground) {
34
45
  if (c.role == BodyRole::Highlight) r.highlight.push_back(c);
@@ -160,7 +171,7 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
160
171
  const std::string& op = std::get<std::string>(params.at("op"));
161
172
  const auto& groupSizes = std::get<ListPtr>(params.at("group_sizes"))->items;
162
173
 
163
- std::vector<ColoredBody> allBg, allHi, allSo;
174
+ std::vector<ColoredBody> allBg, allHi, allSo, allDo;
164
175
  std::optional<ColoredBody> csgResult;
165
176
  size_t idx = 0;
166
177
 
@@ -173,6 +184,7 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
173
184
  allBg.insert(allBg.end(), split.background.begin(), split.background.end());
174
185
  allHi.insert(allHi.end(), split.highlight.begin(), split.highlight.end());
175
186
  allSo.insert(allSo.end(), split.showOnly.begin(), split.showOnly.end());
187
+ allDo.insert(allDo.end(), split.displayOnly.begin(), split.displayOnly.end());
176
188
 
177
189
  std::vector<ColoredBody> bodies3d, sections2d;
178
190
  for (const ColoredBody& c : split.foreground) {
@@ -251,6 +263,7 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
251
263
  result.insert(result.end(), allBg.begin(), allBg.end());
252
264
  result.insert(result.end(), allHi.begin(), allHi.end());
253
265
  result.insert(result.end(), allSo.begin(), allSo.end());
266
+ result.insert(result.end(), allDo.begin(), allDo.end());
254
267
  return result;
255
268
  }
256
269
 
@@ -77,11 +77,23 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params,
77
77
  // splitting/2D-merge logic.
78
78
  struct RoleSplit {
79
79
  std::vector<ColoredBody> background;
80
- std::vector<ColoredBody> foreground; // role != Background && role != ShowOnly
80
+ std::vector<ColoredBody> foreground; // role != Background && role != ShowOnly, and not display-only
81
81
  std::vector<ColoredBody> highlight; // the subset of `foreground` that's role == Highlight
82
82
  std::vector<ColoredBody> showOnly;
83
+ // Bodies with no Manifold to operate on (an open polyhedron -- see
84
+ // ColoredBody::rawMesh). Split out for the same reason showOnly is:
85
+ // they must survive a CSG/hull/minkowski step untouched rather than be
86
+ // merged into it, since there is nothing to merge. Kept separate from
87
+ // `background` because they render as ordinary geometry, not as a
88
+ // translucent ghost.
89
+ std::vector<ColoredBody> displayOnly;
83
90
  };
84
91
  RoleSplit splitByRole(const std::vector<ColoredBody>& bodies);
92
+
93
+ // manifold::ToString(Manifold::Error) only exists under MANIFOLD_DEBUG --
94
+ // not enabled in this build -- so this mirrors it. Shared by import.cpp and
95
+ // primitives_3d.cpp, both of which report why a mesh wouldn't build.
96
+ std::string manifoldErrorName(manifold::Manifold::Error e);
85
97
  std::optional<manifold::CrossSection> toCrossSection(const std::vector<ColoredBody>& bodies);
86
98
 
87
99
  // Merges a statement's bodies into one (3D union, else 2D union, else an