From 4cd05ea173a4c955325e94b03289b2cd26eacdd4 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sun, 6 Sep 2026 10:40:56 -0700 Subject: [PATCH] Add buildScopesInto for a scope table shared across files A resolution can span several files: `use ` gives each used file its own root Scope, and a used file may itself `use` another. One evaluation reads all of them back, so every node's scope has to land in ONE table. buildScopes() creates a table and attaches it to the root it returns, which is right for a single tree and wrong for that case -- each level built its own table and only the outermost survived, so the evaluation saw no scope at all for a used file's nodes and `use` silently resolved to undef. buildScopesInto() records into a table the caller owns and does not attach it, so a multi-file resolution can thread one through and hand it to the outermost root at the end. buildScopes() is now that plus the allocation. Co-Authored-By: Claude Opus 5 (1M context) --- include/openscad_cpp_parser/api.hpp | 12 ++++++++++++ src/scope.cpp | 22 ++++++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/openscad_cpp_parser/api.hpp b/include/openscad_cpp_parser/api.hpp index c41b4ca..070a736 100644 --- a/include/openscad_cpp_parser/api.hpp +++ b/include/openscad_cpp_parser/api.hpp @@ -48,6 +48,18 @@ std::unique_ptr buildScopes(const std::vector>& // why this exists. Never takes ownership. std::unique_ptr buildScopes(const std::vector& ast); +// Same, but recording into a table the CALLER owns, and not attaching it to +// the returned root. +// +// For a resolution that spans several files: `use ` builds a separate +// root Scope per used file, but all of them are read back through one +// evaluation, so their nodes' scopes have to land in ONE table. A per-root +// table would leave the outer evaluation unable to see anything the nested +// resolutions recorded -- every node of a used file would read back as +// having no scope. +std::unique_ptr buildScopesInto(const std::vector>& ast, ScopeTable& table); +std::unique_ptr buildScopesInto(const std::vector& ast, ScopeTable& table); + // Parses `code`. Throws ParseError (with the full caret diagnostic) on a // syntax error. // diff --git a/src/scope.cpp b/src/scope.cpp index 2b669e8..07a1a72 100644 --- a/src/scope.cpp +++ b/src/scope.cpp @@ -8,26 +8,36 @@ namespace oscad { -std::unique_ptr buildScopes(const std::vector>& ast) { - auto owned = std::make_unique(); - ScopeTableScope recording(*owned); +std::unique_ptr buildScopesInto(const std::vector>& ast, ScopeTable& table) { + ScopeTableScope recording(table); auto root = std::make_unique(); collectHoistedDeclarations(ast, *root); for (auto& node : ast) { node->buildScope(*root); } - root->adoptTable(std::move(owned)); return root; } -std::unique_ptr buildScopes(const std::vector& ast) { +std::unique_ptr buildScopes(const std::vector>& ast) { auto owned = std::make_unique(); - ScopeTableScope recording(*owned); + auto root = buildScopesInto(ast, *owned); + root->adoptTable(std::move(owned)); + return root; +} + +std::unique_ptr buildScopesInto(const std::vector& ast, ScopeTable& table) { + ScopeTableScope recording(table); auto root = std::make_unique(); collectHoistedDeclarations(ast, *root); for (ASTNode* node : ast) { node->buildScope(*root); } + return root; +} + +std::unique_ptr buildScopes(const std::vector& ast) { + auto owned = std::make_unique(); + auto root = buildScopesInto(ast, *owned); root->adoptTable(std::move(owned)); return root; }