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