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 }