1 module d.parser.statement;
2 
3 import d.ast.declaration;
4 import d.ast.expression;
5 import d.ast.statement;
6 import d.ast.type;
7 
8 import d.parser.ambiguous;
9 import d.parser.base;
10 import d.parser.conditional;
11 import d.parser.declaration;
12 import d.parser.expression;
13 import d.parser.type;
14 
15 Statement parseStatement(ref TokenRange trange) {
16 	Location location = trange.front.location;
17 	
18 	switch (trange.front.type) with(TokenType) {
19 		case OpenBrace:
20 			return trange.parseBlock();
21 		
22 		case Identifier:
23 			auto lookahead = trange.getLookahead();
24 			lookahead.popFront();
25 			
26 			if (lookahead.front.type == Colon) {
27 				goto ParseLabel;
28 			}
29 			
30 			// If it is not a labeled statement,
31 			// then it is a declaration or an expression.
32 			goto default;
33 		
34 		case If:
35 			trange.popFront();
36 			trange.match(OpenParen);
37 			
38 			auto condition = trange.parseExpression();
39 			
40 			trange.match(CloseParen);
41 			
42 			auto then = trange.parseStatement();
43 			
44 			Statement elseStatement;
45 			if (trange.front.type == Else) {
46 				trange.popFront();
47 				
48 				elseStatement = trange.parseStatement();
49 				
50 				location.spanTo(elseStatement.location);
51 			} else {
52 				location.spanTo(then.location);
53 			}
54 			
55 			return new IfStatement(location, condition, then, elseStatement);
56 		
57 		case While:
58 			trange.popFront();
59 			trange.match(OpenParen);
60 			auto condition = trange.parseExpression();
61 			
62 			trange.match(CloseParen);
63 			
64 			auto statement = trange.parseStatement();
65 			
66 			location.spanTo(statement.location);
67 			return new WhileStatement(location, condition, statement);
68 		
69 		case Do:
70 			trange.popFront();
71 			
72 			auto statement = trange.parseStatement();
73 			
74 			trange.match(While);
75 			trange.match(OpenParen);
76 			auto condition = trange.parseExpression();
77 			
78 			trange.match(CloseParen);
79 			trange.match(Semicolon);
80 			
81 			location.spanTo(trange.previous);
82 			return new DoWhileStatement(location, condition, statement);
83 		
84 		case For:
85 			trange.popFront();
86 			
87 			trange.match(OpenParen);
88 			
89 			Statement init;
90 			if (trange.front.type != Semicolon) {
91 				init = trange.parseStatement();
92 			} else {
93 				trange.popFront();
94 			}
95 			
96 			AstExpression condition;
97 			if (trange.front.type != Semicolon) {
98 				condition = trange.parseExpression();
99 			}
100 			
101 			trange.match(Semicolon);
102 			
103 			AstExpression increment;
104 			if (trange.front.type != CloseParen) {
105 				increment = trange.parseExpression();
106 			}
107 			
108 			trange.match(CloseParen);
109 			
110 			auto statement = trange.parseStatement();
111 			
112 			location.spanTo(statement.location);
113 			return new ForStatement(location, init, condition, increment, statement);
114 		
115 		case Foreach, ForeachReverse:
116 			bool reverse = (trange.front.type == ForeachReverse);
117 			trange.popFront();
118 			trange.match(OpenParen);
119 			
120 			ParamDecl parseForeachListElement() {
121 				Location elementLocation = trange.front.location;
122 				
123 				bool isRef = trange.front.type == Ref;
124 				if (isRef) {
125 					trange.popFront();
126 				}
127 				
128 				bool parseType = true;
129 				// If we have an idientifer, check if the type is implicit.
130 				if (trange.front.type == Identifier) {
131 						auto lookahead = trange.getLookahead();
132 						lookahead.popFront();
133 						if (lookahead.front.type == Comma || lookahead.front.type == Semicolon) {
134 							parseType = false;
135 						}
136 				}
137 				
138 				auto type = parseType
139 					? trange.parseType()
140 					: AstType.getAuto();
141 				
142 				auto name = trange.front.name;
143 				elementLocation.spanTo(trange.front.location);
144 				
145 				trange.match(Identifier);
146 				
147 				return ParamDecl(
148 					elementLocation,
149 					type.getParamType(isRef ? ParamKind.Ref : ParamKind.Regular),
150 					name,
151 					null,
152 				);
153 			}
154 			
155 			ParamDecl[] tupleElements = [parseForeachListElement()];
156 			while(trange.front.type == Comma) {
157 				trange.popFront();
158 				tupleElements ~= parseForeachListElement();
159 			}
160 			
161 			trange.match(Semicolon);
162 			auto iterrated = trange.parseExpression();
163 			
164 			bool isRange = trange.front.type == DotDot;
165 			
166 			AstExpression endOfRange;
167 			if (isRange) {
168 				trange.popFront();
169 				endOfRange = trange.parseExpression();
170 			}
171 			
172 			trange.match(CloseParen);
173 			
174 			auto statement = trange.parseStatement();
175 			location.spanTo(statement.location);
176 			
177 			return isRange
178 				? new ForeachRangeStatement(
179 					location,
180 					tupleElements,
181 					iterrated,
182 					endOfRange,
183 					statement,
184 					reverse,
185 				)
186 				: new ForeachStatement(
187 					location,
188 					tupleElements,
189 					iterrated,
190 					statement,
191 					reverse,
192 				);
193 		
194 		case Return:
195 			trange.popFront();
196 			
197 			AstExpression value;
198 			if (trange.front.type != Semicolon) {
199 				value = trange.parseExpression();
200 			}
201 			
202 			trange.match(Semicolon);
203 			
204 			location.spanTo(trange.previous);
205 			return new ReturnStatement(location, value);
206 		
207 		case Break:
208 			trange.popFront();
209 			if (trange.front.type == Identifier) {
210 				trange.popFront();
211 			}
212 			
213 			trange.match(Semicolon);
214 			
215 			location.spanTo(trange.previous);
216 			return new BreakStatement(location);
217 		
218 		case Continue:
219 			trange.popFront();
220 			if (trange.front.type == Identifier) {
221 				trange.popFront();
222 			}
223 			
224 			trange.match(Semicolon);
225 			
226 			location.spanTo(trange.previous);
227 			return new ContinueStatement(location);
228 		
229 		case Switch:
230 			trange.popFront();
231 			trange.match(OpenParen);
232 			
233 			auto expression = trange.parseExpression();
234 			
235 			trange.match(CloseParen);
236 			
237 			auto statement = trange.parseStatement();
238 			location.spanTo(statement.location);
239 			
240 			return new SwitchStatement(location, expression, statement);
241 		
242 		case Case:
243 			trange.popFront();
244 			
245 			AstExpression[] cases = trange.parseArguments();
246 			
247 			trange.match(Colon);
248 			location.spanTo(trange.front.location);
249 			
250 			Statement statement;
251 			if (trange.front.type != CloseBrace) {
252 				statement = trange.parseStatement();
253 				location.spanTo(statement.location);
254 			}
255 			
256 			location.spanTo(trange.previous);
257 			return new CaseStatement(location, cases, statement);
258 		
259 		case Default:
260 		ParseLabel:
261 			// Other labeled statement will jump here !
262 			auto label = trange.front.name;
263 			trange.popFront();
264 			trange.match(Colon);
265 			location.spanTo(trange.front.location);
266 			
267 			Statement statement;
268 			if (trange.front.type != CloseBrace) {
269 				statement = trange.parseStatement();
270 				location.spanTo(statement.location);
271 			}
272 			
273 			return new LabeledStatement(location, label, statement);
274 		
275 		case Goto:
276 			trange.popFront();
277 			
278 			import source.name;
279 			Name label;
280 			switch(trange.front.type) {
281 				case Identifier:
282 				case Default:
283 				case Case:
284 					label = trange.front.name;
285 					trange.popFront();
286 					break;
287 				
288 				default:
289 					trange.match(Identifier);
290 			}
291 			
292 			trange.match(Semicolon);
293 			
294 			location.spanTo(trange.previous);
295 			return new GotoStatement(location, label);
296 		
297 		case Scope:
298 			trange.popFront();
299 			trange.match(OpenParen);
300 			
301 			auto name = trange.front.name;
302 			trange.match(Identifier);
303 			
304 			import source.name;
305 			ScopeKind kind;
306 			if (name == BuiltinName!"exit") {
307 				kind = ScopeKind.Exit;
308 			} else if (name == BuiltinName!"success") {
309 				kind = ScopeKind.Success;
310 			} else if (name == BuiltinName!"failure") {
311 				kind = ScopeKind.Failure;
312 			} else {
313 				assert(0, name.toString(trange.context) ~ " is not a valid scope identifier.");
314 			}
315 			
316 			trange.match(CloseParen);
317 			
318 			auto statement = trange.parseStatement();
319 			location.spanTo(statement.location);
320 			
321 			return new ScopeStatement(location, kind, statement);
322 		
323 		case Assert:
324 			trange.popFront();
325 			trange.match(OpenParen);
326 			
327 			auto condition = trange.parseAssignExpression();
328 			AstExpression message;
329 			if (trange.front.type == Comma) {
330 				trange.popFront();
331 				message = trange.parseAssignExpression();
332 				
333 				// Trailing comma
334 				if (trange.front.type == Comma) {
335 					trange.popFront();
336 				}
337 			}
338 			
339 			trange.match(CloseParen);
340 			trange.match(Semicolon);
341 			
342 			location.spanTo(trange.previous);
343 			return new AssertStatement(location, condition, message);
344 		
345 		case Throw:
346 			trange.popFront();
347 			auto value = trange.parseExpression();
348 			
349 			trange.match(Semicolon);
350 			
351 			location.spanTo(trange.previous);
352 			return new ThrowStatement(location, value);
353 		
354 		case Try:
355 			trange.popFront();
356 			
357 			auto statement = trange.parseStatement();
358 			
359 			CatchBlock[] catches;
360 			while(trange.front.type == Catch) {
361 				auto catchLocation = trange.front.location;
362 				trange.popFront();
363 				
364 				if (trange.front.type == OpenParen) {
365 					trange.popFront();
366 					
367 					import d.parser.identifier;
368 					auto type = trange.parseIdentifier();
369 					
370 					import source.name;
371 					Name name;
372 					if (trange.front.type == Identifier) {
373 						name = trange.front.name;
374 						trange.popFront();
375 					}
376 					
377 					trange.match(CloseParen);
378 					
379 					auto catchStatement = trange.parseStatement();
380 					
381 					location.spanTo(catchStatement.location);
382 					catches ~= CatchBlock(location, type, name, catchStatement);
383 				} else {
384 					// TODO: handle final catches ?
385 					trange.parseStatement();
386 					assert(0, "Final catches not implemented");
387 				}
388 			}
389 			
390 			Statement finallyStatement;
391 			if (trange.front.type == Finally) {
392 				trange.popFront();
393 				finallyStatement = trange.parseStatement();
394 			}
395 			
396 			location.spanTo(trange.previous);
397 			return new TryStatement(location, statement, catches, finallyStatement);
398 		
399 		case Synchronized:
400 			trange.popFront();
401 			if (trange.front.type == OpenParen) {
402 				trange.popFront();
403 				trange.parseExpression();
404 				trange.match(CloseParen);
405 			}
406 			
407 			auto statement = trange.parseStatement();
408 			location.spanTo(statement.location);
409 			
410 			return new SynchronizedStatement(location, statement);
411 		
412 		case Mixin:
413 			trange.popFront();
414 			trange.match(OpenParen);
415 			
416 			auto expr = trange.parseAssignExpression();
417 			
418 			// To deambiguate vs TokenType.Mixin.
419 			import d.ast.conditional : Mixin;
420 
421 			trange.match(CloseParen);
422 			if (trange.front.type == Semicolon) {
423 				// mixin(expr); is a statement.
424 				location.spanTo(trange.front.location);
425 				trange.popFront();
426 				
427 				return new Mixin!Statement(location, expr);
428 			}
429 			
430 			location.spanTo(trange.previous);
431 			expr = new Mixin!AstExpression(location, expr);
432 			return trange.parseStatementSuffix(expr);
433 		
434 		case Static:
435 			auto lookahead = trange.getLookahead();
436 			lookahead.popFront();
437 			
438 			switch (lookahead.front.type) {
439 				case If:
440 					return trange.parseStaticIf!Statement();
441 				
442 				case Assert:
443 					return trange.parseStaticAssert!Statement();
444 				
445 				default:
446 					auto declaration = trange.parseDeclaration();
447 					return new DeclarationStatement(declaration);
448 			}
449 		
450 		case Version:
451 			return trange.parseVersion!Statement();
452 		
453 		case Debug:
454 			return trange.parseDebug!Statement();
455 		
456 		default:
457 			return trange.parseAmbiguousStatement();
458 	}
459 	
460 	assert(0);
461 }
462 
463 BlockStatement parseBlock(ref TokenRange trange) {
464 	Location location = trange.front.location;
465 	
466 	trange.match(TokenType.OpenBrace);
467 	
468 	Statement[] statements;
469 	
470 	while (trange.front.type != TokenType.CloseBrace) {
471 		statements ~= trange.parseStatement();
472 	}
473 	
474 	trange.popFront();
475 	
476 	location.spanTo(trange.previous);
477 	return new BlockStatement(location, statements);
478 }