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