1 /**
2  * This file is part of DCD, a development tool for the D programming language.
3  * Copyright (C) 2014 Brian Schott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 module dsymbol.scope_;
20 
21 import dsymbol.symbol;
22 import dsymbol.import_;
23 import dsymbol.builtin.names;
24 import containers.ttree;
25 import containers.unrolledlist;
26 import std.experimental.logger;
27 import stdx.allocator.mallocator : Mallocator;
28 
29 /**
30  * Contains symbols and supports lookup of symbols by cursor position.
31  */
32 struct Scope
33 {
34 	@disable this(this);
35 	@disable this();
36 
37 	/**
38 	 * Params:
39 	 *     begin = the beginning byte index
40 	 *     end = the ending byte index
41 	 */
42 	this (uint begin, uint end)
43 	{
44 		this.startLocation = begin;
45 		this.endLocation = end;
46 	}
47 
48 	~this()
49 	{
50 		foreach (child; children[])
51 			typeid(Scope).destroy(child);
52 		foreach (symbol; _symbols)
53 		{
54 			if (symbol.owned)
55 				typeid(DSymbol).destroy(symbol.ptr);
56 		}
57 	}
58 
59 	/**
60 	 * Params:
61 	 *     cursorPosition = the cursor position in bytes
62 	 * Returns:
63 	 *     the innermost scope that contains the given cursor position
64 	 */
65 	Scope* getScopeByCursor(size_t cursorPosition) pure @nogc
66 	{
67 		if (cursorPosition < startLocation) return null;
68 		if (cursorPosition > endLocation) return null;
69 		foreach (child; children[])
70 		{
71 			auto childScope = child.getScopeByCursor(cursorPosition);
72 			if (childScope !is null)
73 				return childScope;
74 		}
75 		return cast(typeof(return)) &this;
76 	}
77 
78 	/**
79 	 * Params:
80 	 *     cursorPosition = the cursor position in bytes
81 	 * Returns:
82 	 *     all symbols in the scope containing the cursor position, as well as
83 	 *     the symbols in parent scopes of that scope.
84 	 */
85 	DSymbol*[] getSymbolsInCursorScope(size_t cursorPosition)
86 	{
87 		import std.array : array;
88 		import std.algorithm.iteration : map;
89 
90 		auto s = getScopeByCursor(cursorPosition);
91 
92 		if (s is null)
93 			return [];
94 
95 		UnrolledList!(DSymbol*) retVal;
96 		Scope* sc = s;
97 		while (sc !is null)
98 		{
99 			foreach (item; sc._symbols[])
100 			{
101 				if (item.ptr.kind == CompletionKind.withSymbol)
102 				{
103 					if (item.ptr.type !is null)
104 						foreach (i; item.ptr.type.opSlice())
105 							retVal.insert(i);
106 				}
107 				else if (item.ptr.type !is null && item.ptr.kind == CompletionKind.importSymbol)
108 				{
109 					if (item.ptr.qualifier != SymbolQualifier.selectiveImport)
110 					{
111 						foreach (i; item.ptr.type.opSlice())
112 							retVal.insert(i);
113 					}
114 					else if (item.ptr !is null)
115 						retVal.insert(item.ptr.type);
116 				}
117 				else
118 					retVal.insert(item.ptr);
119 			}
120 			sc = sc.parent;
121 		}
122 		return array(retVal[]);
123 	}
124 
125 	/**
126 	 * Params:
127 	 *     name = the symbol name to search for
128 	 * Returns:
129 	 *     all symbols in this scope or parent scopes with the given name
130 	 */
131 	inout(DSymbol)*[] getSymbolsByName(istring name) inout
132 	{
133 		import std.array : array, appender;
134 		import std.algorithm.iteration : map;
135 
136 		DSymbol s = DSymbol(name);
137 		auto er = _symbols.equalRange(SymbolOwnership(&s));
138 		if (!er.empty)
139 			return cast(typeof(return)) array(er.map!(a => a.ptr));
140 
141 		// Check symbols from "with" statement
142 		DSymbol ir2 = DSymbol(WITH_SYMBOL_NAME);
143 		auto r2 = _symbols.equalRange(SymbolOwnership(&ir2));
144 		if (!r2.empty)
145 		{
146 			auto app = appender!(DSymbol*[])();
147 			foreach (e; r2)
148 			{
149 				if (e.type is null)
150 					continue;
151 				foreach (withSymbol; e.type.getPartsByName(s.name))
152 					app.put(cast(DSymbol*) withSymbol);
153 			}
154 			if (app.data.length > 0)
155 				return cast(typeof(return)) app.data;
156 		}
157 
158 		if (name.ptr != CONSTRUCTOR_SYMBOL_NAME.ptr
159 				&& name.ptr != DESTRUCTOR_SYMBOL_NAME.ptr
160 				&& name.ptr != UNITTEST_SYMBOL_NAME.ptr
161 				&& name.ptr != THIS_SYMBOL_NAME.ptr)
162 		{
163 			// Check imported symbols
164 			DSymbol ir = DSymbol(IMPORT_SYMBOL_NAME);
165 
166 			auto app = appender!(DSymbol*[])();
167 			foreach (e; _symbols.equalRange(SymbolOwnership(&ir)))
168 			{
169 				if (e.type is null)
170 					continue;
171 				if (e.qualifier == SymbolQualifier.selectiveImport &&
172 						e.type.name.ptr == name.ptr)
173 					app.put(cast(DSymbol*) e.type);
174 				else
175 					foreach (importedSymbol; e.type.getPartsByName(s.name))
176 						app.put(cast(DSymbol*) importedSymbol);
177 			}
178 			if (app.data.length > 0)
179 				return cast(typeof(return)) app.data;
180 		}
181 		if (parent is null)
182 			return [];
183 		return parent.getSymbolsByName(name);
184 	}
185 
186 	/**
187 	 * Params:
188 	 *     name = the symbol name to search for
189 	 *     cursorPosition = the cursor position in bytes
190 	 * Returns:
191 	 *     all symbols with the given name in the scope containing the cursor
192 	 *     and its parent scopes
193 	 */
194 	DSymbol*[] getSymbolsByNameAndCursor(istring name, size_t cursorPosition)
195 	{
196 		auto s = getScopeByCursor(cursorPosition);
197 		if (s is null)
198 			return [];
199 		return s.getSymbolsByName(name);
200 	}
201 
202 	DSymbol* getFirstSymbolByNameAndCursor(istring name, size_t cursorPosition)
203 	{
204 		auto s = getSymbolsByNameAndCursor(name, cursorPosition);
205 		return s.length > 0 ? s[0] : null;
206 	}
207 
208 	/**
209 	 * Returns an array of symbols that are present at global scope
210 	 */
211 	inout(DSymbol)*[] getSymbolsAtGlobalScope(istring name) inout
212 	{
213 		if (parent !is null)
214 			return parent.getSymbolsAtGlobalScope(name);
215 		return getSymbolsByName(name);
216 	}
217 
218 	/// The scope that contains this one
219 	Scope* parent;
220 
221 	/// Child scopes
222 	UnrolledList!(Scope*, Mallocator, false) children;
223 
224 	/// Start location of this scope in bytes
225 	uint startLocation;
226 
227 	/// End location of this scope in bytes
228 	uint endLocation;
229 
230 	auto symbols() @property
231 	{
232 		return _symbols[];
233 	}
234 
235 	/**
236 	 * Adds the given symbol to this scope.
237 	 * Params:
238 	 *     symbol = the symbol to add
239 	 *     owns = if true, the symbol's destructor will be called when this
240 	 *         scope's destructor is called.
241 	 */
242 	void addSymbol(DSymbol* symbol, bool owns)
243 	{
244 		assert(symbol !is null);
245 		_symbols.insert(SymbolOwnership(symbol, owns));
246 	}
247 
248 private:
249 	/// Symbols contained in this scope
250 	TTree!(SymbolOwnership, Mallocator, true, "a.opCmp(b) < 0", false) _symbols;
251 }