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.conversion;
20 
21 import dsymbol.cache_entry;
22 import dsymbol.conversion.first;
23 import dsymbol.conversion.second;
24 import dsymbol.modulecache;
25 import dsymbol.scope_;
26 import dsymbol.string_interning;
27 import dsymbol.symbol;
28 import dsymbol.semantic;
29 import dparse.ast;
30 import dparse.lexer;
31 import dparse.parser;
32 import dparse.rollback_allocator;
33 import stdx.allocator;
34 
35 /**
36  * Used by autocompletion.
37  */
38 ScopeSymbolPair generateAutocompleteTrees(const(Token)[] tokens,
39 	IAllocator symbolAllocator, RollbackAllocator* parseAllocator,
40 	size_t cursorPosition, ref ModuleCache cache)
41 {
42 	Module m = parseModuleForAutocomplete(tokens, internString("stdin"),
43 		parseAllocator, cursorPosition);
44 
45 	scope first = new FirstPass(m, internString("stdin"), symbolAllocator,
46 		symbolAllocator, true, &cache);
47 	first.run();
48 
49 	secondPass(first.rootSymbol, first.moduleScope, cache);
50 	auto r = first.rootSymbol.acSymbol;
51 	typeid(SemanticSymbol).destroy(first.rootSymbol);
52 	return ScopeSymbolPair(r, first.moduleScope);
53 }
54 
55 struct ScopeSymbolPair
56 {
57 	void destroy()
58 	{
59 		typeid(DSymbol).destroy(symbol);
60 		typeid(Scope).destroy(scope_);
61 	}
62 
63 	DSymbol* symbol;
64 	Scope* scope_;
65 }
66 
67 /**
68  * Used by import symbol caching.
69  *
70  * Params:
71  *     tokens = the tokens that compose the file
72  *     fileName = the name of the file being parsed
73  *     parseAllocator = the allocator to use for the AST
74  * Returns: the parsed module
75  */
76 Module parseModuleSimple(const(Token)[] tokens, string fileName, RollbackAllocator* parseAllocator)
77 {
78 	assert (parseAllocator !is null);
79 	scope parser = new SimpleParser();
80 	parser.fileName = fileName;
81 	parser.tokens = tokens;
82 	parser.messageFunction = &doesNothing;
83 	parser.allocator = parseAllocator;
84 	return parser.parseModule();
85 }
86 
87 private:
88 
89 Module parseModuleForAutocomplete(const(Token)[] tokens, string fileName,
90 	RollbackAllocator* parseAllocator, size_t cursorPosition)
91 {
92 	scope parser = new AutocompleteParser();
93 	parser.fileName = fileName;
94 	parser.tokens = tokens;
95 	parser.messageFunction = &doesNothing;
96 	parser.allocator = parseAllocator;
97 	parser.cursorPosition = cursorPosition;
98 	return parser.parseModule();
99 }
100 
101 class AutocompleteParser : Parser
102 {
103 	override BlockStatement parseBlockStatement()
104 	{
105 		if (!currentIs(tok!"{"))
106 			return null;
107 		if (current.index > cursorPosition)
108 		{
109 			BlockStatement bs = allocator.make!(BlockStatement);
110 			bs.startLocation = current.index;
111 			skipBraces();
112 			bs.endLocation = tokens[index - 1].index;
113 			return bs;
114 		}
115 		immutable start = current.index;
116 		auto b = setBookmark();
117 		skipBraces();
118 		if (tokens[index - 1].index < cursorPosition)
119 		{
120 			abandonBookmark(b);
121 			BlockStatement bs = allocator.make!BlockStatement();
122 			bs.startLocation = start;
123 			bs.endLocation = tokens[index - 1].index;
124 			return bs;
125 		}
126 		else
127 		{
128 			goToBookmark(b);
129 			return super.parseBlockStatement();
130 		}
131 	}
132 
133 private:
134 	size_t cursorPosition;
135 }
136 
137 class SimpleParser : Parser
138 {
139 	override Unittest parseUnittest()
140 	{
141 		expect(tok!"unittest");
142 		if (currentIs(tok!"{"))
143 			skipBraces();
144 		return allocator.make!Unittest;
145 	}
146 
147 	override MissingFunctionBody parseMissingFunctionBody()
148 	{
149 		// Unlike many of the other parsing functions, it is valid and expected
150 		// for this one to return `null` on valid code. Returning `null` in
151 		// this function means that we are looking at a SpecifiedFunctionBody
152 		// or ShortenedFunctionBody.
153 		//
154 		// The super-class will handle re-trying with the correct parsing
155 		// function.
156 
157 		const bool needDo = skipContracts();
158 		if (needDo && moreTokens && (currentIs(tok!"do") || current.text == "body"))
159 			return null;
160 		if (currentIs(tok!";"))
161 			advance();
162 		else
163 			return null;
164 		return allocator.make!MissingFunctionBody;
165 	}
166 
167 	override SpecifiedFunctionBody parseSpecifiedFunctionBody()
168 	{
169 		if (currentIs(tok!"{"))
170 			skipBraces();
171 		else
172 		{
173 			skipContracts();
174 			if (currentIs(tok!"do") || (currentIs(tok!"identifier") && current.text == "body"))
175 				advance();
176 			if (currentIs(tok!"{"))
177 				skipBraces();
178 		}
179 		return allocator.make!SpecifiedFunctionBody;
180 	}
181 
182 	override ShortenedFunctionBody parseShortenedFunctionBody()
183 	{
184 		skipContracts();
185 		if (currentIs(tok!"=>"))
186 		{
187 			while (!currentIs(tok!";") && moreTokens)
188 			{
189 				if (currentIs(tok!"{")) // potential function literal
190 					skipBraces();
191 				else
192 					advance();
193 			}
194 			if (moreTokens)
195 				advance();
196 		}
197 		return allocator.make!ShortenedFunctionBody;
198 	}
199 
200 	/**
201 	 * Skip contracts, and return `true` if the type of contract used requires
202 	 * that the next token is `do`.
203 	 */
204 	private bool skipContracts()
205 	{
206 		bool needDo;
207 
208 		while (true)
209 		{
210 			if (currentIs(tok!"in"))
211 			{
212 				advance();
213 				if (currentIs(tok!"{"))
214 				{
215 					skipBraces();
216 					needDo = true;
217 				}
218 				if (currentIs(tok!"("))
219 					skipParens();
220 			}
221 			else if (currentIs(tok!"out"))
222 			{
223 				advance();
224 				if (currentIs(tok!"("))
225 				{
226 					immutable bool asExpr = peekIs(tok!";")
227 						|| (peekIs(tok!"identifier")
228 							&& index + 2 < tokens.length && tokens[index + 2].type == tok!";");
229 					skipParens();
230 					if (asExpr)
231 					{
232 						needDo = false;
233 						continue;
234 					}
235 				}
236 				if (currentIs(tok!"{"))
237 				{
238 					skipBraces();
239 					needDo = true;
240 				}
241 			}
242 			else
243 				break;
244 		}
245 		return needDo;
246 	}
247 }
248 
249 void doesNothing(string, size_t, size_t, string, bool) {}