1 module d.parser.declaration;
2 
3 import d.parser.adt;
4 import d.parser.base;
5 import d.parser.conditional;
6 import d.parser.expression;
7 import d.parser.identifier;
8 import d.parser.statement;
9 import d.parser.dtemplate;
10 import d.parser.type;
11 
12 import d.ast.declaration;
13 import d.ast.expression;
14 import d.ast.identifier;
15 import d.ast.type;
16 
17 import d.ir.expression;
18 
19 import source.name;
20 
21 /**
22  * Parse a set of declarations.
23  */
24 auto parseAggregate(bool globBraces = true)(ref TokenRange trange) {
25 	static if (globBraces) {
26 		trange.match(TokenType.OpenBrace);
27 	}
28 	
29 	Declaration[] declarations;
30 	
31 	while (!trange.empty && trange.front.type != TokenType.CloseBrace) {
32 		declarations ~= trange.parseDeclaration();
33 	}
34 	
35 	static if (globBraces) {
36 		trange.match(TokenType.CloseBrace);
37 	}
38 	
39 	return declarations;
40 }
41 
42 /**
43  * Parse a declaration
44  */
45 Declaration parseDeclaration(ref TokenRange trange) {
46 	auto location = trange.front.location;
47 	
48 	// First, declarations that do not support storage classes.
49 	switch (trange.front.type) with(TokenType) {
50 		case Static:
51 			// Handle static if.
52 			auto lookahead = trange.getLookahead();
53 			lookahead.popFront();
54 			switch (lookahead.front.type) {
55 				case If:
56 					return trange.parseStaticIf!Declaration();
57 				
58 				case Assert:
59 					return trange.parseStaticAssert!Declaration();
60 				
61 				default:
62 					break;
63 			}
64 			
65 			break;
66 		
67 		case Import:
68 			return trange.parseImport();
69 		
70 		case Version:
71 			return trange.parseVersion!Declaration();
72 		
73 		case Debug:
74 			return trange.parseDebug!Declaration();
75 		
76 		case Mixin:
77 			return trange.parseMixin!Declaration();
78 		
79 		default :
80 			break;
81 	}
82 	
83 	auto qualifier = TypeQualifier.Mutable;
84 	StorageClass stc = defaultStorageClass;
85 
86 	StorageClassLoop: while (true) {
87 		switch (trange.front.type) with(TokenType) {
88 			case Const:
89 				qualifier = TypeQualifier.Const;
90 				goto HandleTypeQualifier;
91 			
92 			case Immutable:
93 				qualifier = TypeQualifier.Immutable;
94 				goto HandleTypeQualifier;
95 			
96 			case Inout:
97 				qualifier = TypeQualifier.Inout;
98 				goto HandleTypeQualifier;
99 			
100 			case Shared:
101 				qualifier = TypeQualifier.Shared;
102 				goto HandleTypeQualifier;
103 			
104 			HandleTypeQualifier: {
105 				auto lookahead = trange.getLookahead();
106 				lookahead.popFront();
107 				if (lookahead.front.type == OpenParen) {
108 					// This is a type not a storage class.
109 					break StorageClassLoop;
110 				}
111 				
112 				// We have a qualifier(type) name type of declaration.
113 				stc.hasQualifier = true;
114 				stc.qualifier = stc.qualifier.add(qualifier);
115 				goto HandleStorageClass;
116 			}
117 			
118 			case Ref:
119 				stc.isRef = true;
120 				goto HandleStorageClass;
121 			
122 			case Abstract:
123 				stc.isAbstract = true;
124 				goto HandleStorageClass;
125 			
126 			case Deprecated:
127 				stc.isDeprecated = true;
128 				goto HandleStorageClass;
129 			
130 			case Nothrow:
131 				stc.isNoThrow = true;
132 				goto HandleStorageClass;
133 			
134 			case Override:
135 				stc.isOverride = true;
136 				goto HandleStorageClass;
137 			
138 			case Pure:
139 				stc.isPure = true;
140 				goto HandleStorageClass;
141 			
142 			case Static:
143 				stc.isStatic = true;
144 				goto HandleStorageClass;
145 			
146 			case Synchronized:
147 				stc.isSynchronized = true;
148 				goto HandleStorageClass;
149 			
150 			case __Gshared:
151 				stc.isGshared = true;
152 				goto HandleStorageClass;
153 			
154 			case Private:
155 				stc.hasVisibility = true;
156 				stc.visibility = Visibility.Private;
157 				goto HandleStorageClass;
158 			
159 			case Package:
160 				stc.hasVisibility = true;
161 				stc.visibility = Visibility.Package;
162 				goto HandleStorageClass;
163 			
164 			case Protected:
165 				stc.hasVisibility = true;
166 				stc.visibility = Visibility.Protected;
167 				goto HandleStorageClass;
168 			
169 			case Public:
170 				stc.hasVisibility = true;
171 				stc.visibility = Visibility.Public;
172 				goto HandleStorageClass;
173 			
174 			case Export:
175 				stc.hasVisibility = true;
176 				stc.visibility = Visibility.Export;
177 				goto HandleStorageClass;
178 			
179 			HandleStorageClass:
180 				trange.popFront();
181 				break;
182 			
183 			case Extern:
184 				trange.popFront();
185 				trange.match(OpenParen);
186 				auto linkageName = trange.front.name;
187 				trange.match(Identifier);
188 				
189 				stc.hasLinkage = true;
190 				if (linkageName == BuiltinName!"D") {
191 					stc.linkage = Linkage.D;
192 				} else if (linkageName == BuiltinName!"C") {
193 					// TODO: C++
194 					stc.linkage = Linkage.C;
195 				} else if (linkageName == BuiltinName!"Windows") {
196 					stc.linkage = Linkage.Windows;
197 				} else if (linkageName == BuiltinName!"System") {
198 					stc.linkage = Linkage.System;
199 				} else if (linkageName == BuiltinName!"Pascal") {
200 					stc.linkage = Linkage.Pascal;
201 				} else if (linkageName == BuiltinName!"Java") {
202 					stc.linkage = Linkage.Java;
203 				} else {
204 					assert(
205 						0,
206 						"Linkage not supported : "
207 							~ linkageName.toString(trange.context),
208 					);
209 				}
210 				
211 				trange.match(CloseParen);
212 				break;
213 			
214 			// Enum is a bit of a strange beast. half storage class, half declaration itself.
215 			case Enum:
216 				stc.isEnum = true;
217 				
218 				auto lookahead = trange.getLookahead();
219 				lookahead.popFront();
220 				
221 				switch (lookahead.front.type) {
222 					// enum : and enum { are special construct,
223 					// not classic storage class declaration.
224 					case Colon, OpenBrace:
225 						return trange.parseEnum(stc);
226 					
227 					case Identifier:
228 						lookahead.popFront();
229 						auto nextType = lookahead.front.type;
230 						if (nextType == Colon || nextType == OpenBrace) {
231 							// Named verion of the above.
232 							return trange.parseEnum(stc);
233 						}
234 						
235 						break;
236 					
237 					default :
238 						break;
239 				}
240 				
241 				goto HandleStorageClass;
242 			
243 			case At:
244 				trange.popFront();
245 				auto attr = trange.front.name;
246 				trange.match(Identifier);
247 				
248 				if (attr == BuiltinName!"property") {
249 					stc.isProperty = true;
250 				} else if (attr == BuiltinName!"nogc") {
251 					stc.isNoGC = true;
252 				} else {
253 					assert(
254 						0,
255 						"@" ~ attr.toString(trange.context)
256 							~ " is not supported.",
257 					);
258 				}
259 				
260 				break;
261 			
262 			default:
263 				break StorageClassLoop;
264 		}
265 		
266 		switch (trange.front.type) with(TokenType) {
267 			case Identifier:
268 				auto lookahead = trange.getLookahead();
269 				lookahead.popFront();
270 				auto nextType = lookahead.front.type;
271 				if (nextType == Equal || nextType == OpenParen) {
272 					return trange.parseTypedDeclaration(
273 						location,
274 						stc,
275 						AstType.getAuto(),
276 					);
277 				}
278 				
279 				break StorageClassLoop;
280 			
281 			case Colon:
282 				trange.popFront();
283 				auto declarations = trange.parseAggregate!false();
284 				
285 				location.spanTo(trange.front.location);
286 				return new GroupDeclaration(location, stc, declarations);
287 			
288 			case OpenBrace:
289 				auto declarations = trange.parseAggregate();
290 				
291 				location.spanTo(trange.front.location);
292 				return new GroupDeclaration(location, stc, declarations);
293 			
294 			default:
295 				break;
296 		}
297 	}
298 	
299 	switch (trange.front.type) with(TokenType) {
300 		// XXX: auto as a storage class ?
301 		case Auto:
302 			trange.popFront();
303 			return trange.parseTypedDeclaration(location, stc, AstType.getAuto());
304 		
305 		case Interface:
306 			return trange.parseInterface(stc);
307 		
308 		case Class:
309 			return trange.parseClass(stc);
310 		
311 		case Struct:
312 			return trange.parseStruct(stc);
313 		
314 		case Union:
315 			return trange.parseUnion(stc);
316 		
317 		case This:
318 			return trange.parseConstructor(stc);
319 		
320 		case Tilde:
321 			return trange.parseDestructor(stc);
322 		
323 		case Template:
324 			return trange.parseTemplate(stc);
325 		
326 		case Alias:
327 			return trange.parseAlias(stc);
328 		
329 		case Unittest:
330 			trange.popFront();
331 			
332 			auto name = BuiltinName!"";
333 			if (trange.front.type == Identifier) {
334 				name = trange.front.name;
335 				trange.popFront();
336 			}
337 			
338 			// In the future we may want to skip parsing unittest blocks.
339 			// if (!trange.context.enableUnittest) {
340 			// 	import source.parserutil;
341 			// 	popMatchingDelimiter!(TokenType.OpenBrace)();
342 			// }
343 			
344 			auto fbody = trange.parseBlock();
345 			location.spanTo(trange.previous);
346 			
347 			return new UnittestDeclaration(location, stc, name, fbody);
348 		
349 		default:
350 			return trange.parseTypedDeclaration(location, stc);
351 	}
352 	
353 	assert(0);
354 }
355 
356 /**
357  * Parse type identifier ... declarations.
358  * Function/variables.
359  */
360 Declaration parseTypedDeclaration(
361 	ref TokenRange trange,
362 	Location location,
363 	StorageClass stc,
364 ) {
365 	return trange.parseTypedDeclaration(location, stc, trange.parseType());
366 }
367 
368 /**
369  * Parse a declaration when you already have its type.
370  */
371 Declaration parseTypedDeclaration(
372 	ref TokenRange trange,
373 	Location location,
374 	StorageClass stc,
375 	AstType type,
376 ) {
377 	auto lookahead = trange.getLookahead();
378 	lookahead.popFront();
379 	if (lookahead.front.type == TokenType.OpenParen) {
380 		auto idLoc = trange.front.location;
381 		auto name = trange.front.name;
382 		trange.match(TokenType.Identifier);
383 		
384 		if (name.isReserved) {
385 			import source.exception;
386 			throw new CompileException(
387 				idLoc,
388 				name.toString(trange.context) ~ " is a reserved name",
389 			);
390 		}
391 		
392 		// TODO: implement ref return.
393 		return trange.parseFunction(
394 			location,
395 			stc,
396 			type.getParamType(stc.isRef ? ParamKind.Ref : ParamKind.Regular),
397 			name,
398 		);
399 	} else {
400 		Declaration[] variables;
401 		
402 		while (true) {
403 			auto name = trange.front.name;
404 			Location variableLocation = trange.front.location;
405 			trange.match(TokenType.Identifier);
406 			
407 			AstExpression value;
408 			if (trange.front.type == TokenType.Equal) {
409 				trange.popFront();
410 				value = trange.parseInitializer();
411 				variableLocation.spanTo(value.location);
412 			}
413 			
414 			variables ~= new VariableDeclaration(
415 				location,
416 				stc,
417 				type,
418 				name,
419 				value,
420 			);
421 			
422 			if (trange.front.type != TokenType.Comma) {
423 				break;
424 			}
425 			
426 			trange.popFront();
427 		}
428 		
429 		location.spanTo(trange.front.location);
430 		trange.match(TokenType.Semicolon);
431 		
432 		return new GroupDeclaration(location, stc, variables);
433 	}
434 }
435 
436 // XXX: one callsite, remove
437 private Declaration parseConstructor(ref TokenRange trange, StorageClass stc) {
438 	auto location = trange.front.location;
439 	trange.match(TokenType.This);
440 	
441 	return trange.parseFunction(
442 		location,
443 		stc,
444 		AstType.getAuto().getParamType(ParamKind.Regular),
445 		BuiltinName!"__ctor",
446 	);
447 }
448 
449 // XXX: one callsite, remove
450 private Declaration parseDestructor(ref TokenRange trange, StorageClass stc) {
451 	auto location = trange.front.location;
452 	trange.match(TokenType.Tilde);
453 	trange.match(TokenType.This);
454 	
455 	return trange.parseFunction(
456 		location,
457 		stc,
458 		AstType.getAuto().getParamType(ParamKind.Regular),
459 		BuiltinName!"__dtor",
460 	);
461 }
462 
463 /**
464  * Parse function declaration, starting with parameters.
465  * This allow to parse function as well as constructor or any special function.
466  * Additionnal parameters are used to construct the function.
467  */
468 private Declaration parseFunction(
469 	ref TokenRange trange,
470 	Location location,
471 	StorageClass stc,
472 	ParamAstType returnType,
473 	Name name,
474 ) {
475 	// Function declaration.
476 	bool isVariadic;
477 	AstTemplateParameter[] tplParameters;
478 	
479 	// Check if we have a function template
480 	import source.parserutil;
481 	auto lookahead = trange.getLookahead();
482 	lookahead.popMatchingDelimiter!(TokenType.OpenParen)();
483 	
484 	bool isTemplate = lookahead.front.type == TokenType.OpenParen;
485 	if (isTemplate) {
486 		tplParameters = trange.parseTemplateParameters();
487 	}
488 	
489 	auto parameters = trange.parseParameters(isVariadic);
490 	
491 	// If it is a template, it can have a constraint.
492 	if (tplParameters.ptr) {
493 		if (trange.front.type == TokenType.If) {
494 			trange.parseConstraint();
495 		}
496 	}
497 	
498 	auto qualifier = TypeQualifier.Mutable;
499 	
500 	while (true) {
501 		switch (trange.front.type) with(TokenType) {
502 			case Pure:
503 				stc.isPure = true;
504 				goto HandleStorageClass;
505 			
506 			case Const:
507 				qualifier = TypeQualifier.Const;
508 				goto HandleTypeQualifier;
509 			
510 			case Immutable:
511 				qualifier = TypeQualifier.Immutable;
512 				goto HandleTypeQualifier;
513 			
514 			case Inout:
515 				qualifier = TypeQualifier.Inout;
516 				goto HandleTypeQualifier;
517 			
518 			case Shared:
519 				qualifier = TypeQualifier.Shared;
520 				goto HandleTypeQualifier;
521 			
522 			HandleTypeQualifier: {
523 				// We have a qualifier(type) name type of declaration.
524 				stc.hasQualifier = true;
525 				stc.qualifier = stc.qualifier.add(qualifier);
526 				goto HandleStorageClass;
527 			}
528 			
529 			HandleStorageClass:
530 				trange.popFront();
531 				break;
532 			
533 			case At:
534 				trange.popFront();
535 				auto attr = trange.front.name;
536 				trange.match(Identifier);
537 				
538 				if (attr == BuiltinName!"property") {
539 					stc.isProperty = true;
540 				} else if (attr == BuiltinName!"nogc") {
541 					stc.isNoGC = true;
542 				} else {
543 					assert(
544 						0,
545 						"@" ~ attr.toString(trange.context)
546 							~ " is not supported.",
547 					);
548 				}
549 				
550 				continue;
551 			
552 			default:
553 				break;
554 		}
555 		
556 		break;
557 	}
558 	
559 	// TODO: parse contracts.
560 	// Skip contracts
561 	switch (trange.front.type) with(TokenType) {
562 		case In, Out:
563 			trange.popFront();
564 			trange.parseBlock();
565 			
566 			switch (trange.front.type) {
567 				case In, Out:
568 					trange.popFront();
569 					trange.parseBlock();
570 					break;
571 				
572 				default :
573 					break;
574 			}
575 			
576 			trange.match(Body);
577 			break;
578 		
579 		case Body:
580 			// Body without contract is just skipped.
581 			trange.popFront();
582 			break;
583 		
584 		default:
585 			break;
586 	}
587 	
588 	import d.ast.statement;
589 	BlockStatement fbody;
590 	switch (trange.front.type) with(TokenType) {
591 		case Semicolon:
592 			location.spanTo(trange.front.location);
593 			trange.popFront();
594 			
595 			break;
596 		
597 		case OpenBrace:
598 			fbody = trange.parseBlock();
599 			location.spanTo(fbody.location);
600 			
601 			break;
602 		
603 		default:
604 			// TODO: error.
605 			trange.match(Begin);
606 			assert(0);
607 	}
608 	
609 	auto fun = new FunctionDeclaration(
610 		location,
611 		stc,
612 		returnType,
613 		name,
614 		parameters,
615 		isVariadic,
616 		fbody,
617 	);
618 	
619 	if (!isTemplate) {
620 		return fun;
621 	}
622 	
623 	return new TemplateDeclaration(location, stc, name, tplParameters, [fun]);
624 }
625 
626 /**
627  * Parse function and delegate parameters.
628  */
629 auto parseParameters(bool matchOpenParen = true)(ref TokenRange trange, out bool isVariadic) {
630 	static if (matchOpenParen) {
631 		trange.match(TokenType.OpenParen);
632 	}
633 	
634 	ParamDecl[] parameters;
635 	while (trange.front.type != TokenType.CloseParen) {
636 		if (trange.front.type == TokenType.DotDotDot) {
637 			// This is a variadic function.
638 			trange.popFront();
639 			isVariadic = true;
640 			break;
641 		}
642 		
643 		parameters ~= trange.parseParameter();
644 		if (trange.front.type != TokenType.Comma) {
645 			break;
646 		}
647 		
648 		trange.popFront();
649 	}
650 	
651 	trange.match(TokenType.CloseParen);
652 	return parameters;
653 }
654 
655 /**
656  * Parse Initializer
657  */
658 auto parseInitializer(ref TokenRange trange) {
659 	if (trange.front.type != TokenType.Void) {
660 		return trange.parseAssignExpression();
661 	}
662 	
663 	auto location = trange.front.location;
664 	
665 	trange.popFront();
666 	return new AstVoidInitializer(location);
667 }
668 
669 private:
670 auto parseParameter(ref TokenRange trange) {
671 	bool isRef;
672 	
673 	// TODO: parse storage class
674 	ParseStorageClassLoop: while (true) {
675 		switch (trange.front.type) with(TokenType) {
676 			case In, Out, Lazy:
677 				assert(
678 					0,
679 					"storageclasses: in, out and lazy  are not yet implemented",
680 				);
681 			
682 			case Ref:
683 				trange.popFront();
684 				isRef = true;
685 				
686 				break;
687 			
688 			default:
689 				break ParseStorageClassLoop;
690 		}
691 	}
692 	
693 	auto location = trange.front.location;
694 	auto type = trange.parseType()
695 		.getParamType(isRef ? ParamKind.Ref : ParamKind.Regular);
696 	
697 	auto name = BuiltinName!"";
698 	AstExpression value;
699 	
700 	if (trange.front.type == TokenType.Identifier) {
701 		name = trange.front.name;
702 		
703 		trange.popFront();
704 		if (trange.front.type == TokenType.Equal) {
705 			trange.popFront();
706 			value = trange.parseAssignExpression();
707 		}
708 	}
709 	
710 	location.spanTo(trange.front.location);
711 	return ParamDecl(location, type, name, value);
712 }
713 
714 /**
715  * Parse alias declaration
716  */
717 Declaration parseAlias(ref TokenRange trange, StorageClass stc) {
718 	Location location = trange.front.location;
719 	trange.match(TokenType.Alias);
720 	
721 	auto name = trange.front.name;
722 	trange.match(TokenType.Identifier);
723 	
724 	if (trange.front.type == TokenType.Equal) {
725 		trange.popFront();
726 		
727 		import d.parser.ambiguous;
728 		return trange.parseAmbiguous!(delegate Declaration(parsed) {
729 			location.spanTo(trange.front.location);
730 			trange.match(TokenType.Semicolon);
731 			
732 			alias T = typeof(parsed);
733 			static if (is(T : AstType)) {
734 				return new TypeAliasDeclaration(location, stc, name, parsed);
735 			} else static if (is(T : AstExpression)) {
736 				return new ValueAliasDeclaration(location, stc, name, parsed);
737 			} else {
738 				return new IdentifierAliasDeclaration(
739 					location,
740 					stc,
741 					name,
742 					parsed,
743 				);
744 			}
745 		})();
746 	} else if (trange.front.type == TokenType.This) {
747 		// FIXME: move this before storage class parsing.
748 		trange.popFront();
749 		location.spanTo(trange.front.location);
750 		trange.match(TokenType.Semicolon);
751 		
752 		return new AliasThisDeclaration(location, name);
753 	}
754 	
755 	trange.match(TokenType.Begin);
756 	assert(0);
757 }
758 
759 /**
760  * Parse import declaration
761  */
762 auto parseImport(ref TokenRange trange) {
763 	Location location = trange.front.location;
764 	trange.match(TokenType.Import);
765 	
766 	auto parseModuleName(TokenRange)(ref TokenRange trange) {
767 		auto mod = [trange.front.name];
768 		trange.match(TokenType.Identifier);
769 		while (trange.front.type == TokenType.Dot) {
770 			trange.popFront();
771 		
772 			mod ~= trange.front.name;
773 			trange.match(TokenType.Identifier);
774 		}
775 		
776 		return mod;
777 	}
778 	
779 	auto modules = [parseModuleName(trange)];
780 	while (trange.front.type == TokenType.Comma) {
781 		trange.popFront();
782 		
783 		modules ~= parseModuleName(trange);
784 	}
785 	
786 	location.spanTo(trange.front.location);
787 	trange.match(TokenType.Semicolon);
788 	
789 	return new ImportDeclaration(location, modules);
790 }