1 /// Copyright: Copyright (c) 2021 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 module vox.fe.ast.decl.package_; 5 6 import vox.all; 7 8 /// Does not correspond to any token in the source code 9 /// All packages and modules have parent package. Top-level package is root package (CommonAstNodes.node_root_package) 10 @(AstType.decl_package) 11 struct PackageDeclNode { 12 mixin AstNodeData!(AstType.decl_package, 0, AstNodeState.ir_gen_done); 13 AstNodeMap declarations; // nested modules/packages. Key is self name (non-fqn) 14 Identifier fqn; 15 /// Points to PackageDeclNode 16 AstIndex parentPackage = CommonAstNodes.node_root_package; 17 18 bool isTopLevel() { return parentPackage == CommonAstNodes.node_root_package; } 19 20 void addModule(TokenIndex loc, Identifier modId, AstIndex modIndex, ref AstIndex conflictingModPack, CompilationContext* c) { 21 bool wasCreated; 22 c.assertf(modIndex.astType(c) == AstType.decl_module, "Must be module"); 23 AstIndex value = *declarations.getOrCreate(c.arrayArena, modId.getSelf(c), wasCreated, modIndex); 24 if (!wasCreated) { 25 if (conflictingModPack.isUndefined) conflictingModPack = value; 26 } 27 } 28 29 AstIndex lookup(Identifier subpackageId, CompilationContext* c) { 30 AstIndex index = declarations.get(subpackageId, AstIndex.init); 31 assert(index.isModOrPack(c)); 32 return index; 33 } 34 35 AstIndex getOrCreateSubpackage(TokenIndex loc, Identifier subpackageId, ref AstIndex conflictingModule, CompilationContext* c) { 36 AstIndex index = declarations.get(subpackageId.getSelf(c), AstIndex.init); 37 38 void makePack() { 39 index = c.appendAst!PackageDeclNode(TokenIndex.init, AstNodeMap.init, subpackageId, c.getAstNodeIndex(&this)); 40 c.modules.put(c.arrayArena, subpackageId, index); 41 declarations.put(c.arrayArena, subpackageId.getSelf(c), index); 42 } 43 44 if (index.isUndefined) makePack(); // create new package 45 46 AstNode* node = index.get_node(c); 47 48 if (node.astType == AstType.decl_module) { 49 if (conflictingModule.isUndefined) conflictingModule = index; 50 // Create new package and replace existing, so that we can finish parsing module declaration 51 // Calling code will check conflictingModule in the end, and generate an error 52 makePack(); 53 } 54 return index; 55 } 56 57 // Needed to report file name that defines this package 58 void visitModules(scope void delegate(ref AstIndex mod) visitor, CompilationContext* c) { 59 foreach(i, ref AstIndex sub; declarations) { 60 auto node = sub.get_node(c); 61 if (node.astType == AstType.decl_module) visitor(sub); 62 else node.as!PackageDeclNode(c).visitModules(visitor, c); 63 } 64 } 65 } 66 67 68 struct PackageFilesPrinter 69 { 70 PackageDeclNode* pack; 71 CompilationContext* c; 72 void toString(scope void delegate(const(char)[]) sink) { 73 uint filesVisited = 0; 74 enum FILE_PRINT_LIMIT = 2; 75 76 // TODO: need to gather names in array, sort it and then print. Otherwise tests depend on hashmap order 77 void onModule(ref AstIndex modIndex) { 78 if (filesVisited < FILE_PRINT_LIMIT || c.verboseErrors) { 79 if (filesVisited > 0) sink(", "); 80 sink(modIndex.get!ModuleDeclNode(c).fileName(c)); 81 } 82 ++filesVisited; 83 } 84 85 pack.visitModules(&onModule, c); 86 if (filesVisited > FILE_PRINT_LIMIT && c.conciseErrors) sink.formattedWrite(" and %s more", filesVisited - FILE_PRINT_LIMIT); 87 } 88 } 89 90 void print_package(PackageDeclNode* node, ref AstPrintState state) 91 { 92 state.print("PACKAGE ", node.fqn.pr(state.context)); 93 }