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 }