d: Merge upstream dmd, druntime e48bc0987d, phobos 2458e8f82.
[official-gcc.git] / gcc / d / dmd / cparse.d
blob0b738cdc3d5941ac14388af4d990454d54a9b4f0
1 /**
2 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
4 * Specification: C11
6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d, _cparse.d)
10 * Documentation: https://dlang.org/phobos/dmd_cparse.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d
14 module dmd.cparse;
16 import core.stdc.stdio;
17 import core.stdc.string : memcpy;
19 import dmd.astenums;
20 import dmd.errorsink;
21 import dmd.id;
22 import dmd.identifier;
23 import dmd.lexer;
24 import dmd.location;
25 import dmd.parse;
26 import dmd.root.array;
27 import dmd.common.outbuffer;
28 import dmd.root.rmem;
29 import dmd.tokens;
31 /***********************************************************
33 final class CParser(AST) : Parser!AST
35 AST.Dsymbols* symbols; // symbols declared in current scope
37 bool addFuncName; /// add declaration of __func__ to function symbol table
38 bool importBuiltins; /// seen use of C compiler builtins, so import __builtins;
40 private
42 structalign_t packalign; // current state of #pragma pack alignment
44 // #pragma pack stack
45 Array!Identifier* records; // identifers (or null)
46 Array!structalign_t* packs; // parallel alignment values
49 /* C cannot be parsed without determining if an identifier is a type or a variable.
50 * For expressions like `(T)-3`, is it a cast or a minus expression?
51 * It also occurs with `typedef int (F)(); F fun;`
52 * but to build the AST we need to distinguish `fun` being a function as opposed to a variable.
53 * To fix, build a symbol table for the typedefs.
54 * Symbol table of typedefs indexed by Identifier cast to void*.
55 * 1. if an identifier is a typedef, then it will return a non-null Type
56 * 2. if an identifier is not a typedef, then it will return null
58 Array!(void*) typedefTab; /// Array of AST.Type[Identifier], typedef's indexed by Identifier
60 /* This is passed in as a list of #define lines, as generated by the C preprocessor with the
61 * appropriate switch to emit them. We append to it any #define's and #undef's encountered in the source
62 * file, as cpp with the -dD embeds them in the preprocessed output file.
63 * Once the file is parsed, then the #define's are converted to D symbols and appended to the array
64 * of Dsymbols returned by parseModule().
66 OutBuffer* defines;
68 extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment,
69 ErrorSink errorSink,
70 const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope
72 const bool doUnittests = false;
73 super(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
75 //printf("CParser.this()\n");
76 mod = _module;
77 linkage = LINK.c;
78 Ccompile = true;
79 this.packalign.setDefault();
80 this.defines = defines;
82 // Configure sizes for C `long`, `long double`, `wchar_t`, ...
83 this.boolsize = target.boolsize;
84 this.shortsize = target.shortsize;
85 this.intsize = target.intsize;
86 this.longsize = target.longsize;
87 this.long_longsize = target.long_longsize;
88 this.long_doublesize = target.long_doublesize;
89 this.wchar_tsize = target.wchar_tsize;
91 // C `char` is always unsigned in ImportC
94 /********************************************
95 * Parse translation unit.
96 * C11 6.9
97 * translation-unit:
98 * external-declaration
99 * translation-unit external-declaration
101 * external-declaration:
102 * function-definition
103 * declaration
104 * Returns:
105 * array of Dsymbols that were declared
107 override AST.Dsymbols* parseModule()
109 //printf("cparseTranslationUnit()\n");
110 symbols = new AST.Dsymbols();
111 typedefTab.push(null); // C11 6.2.1-3 symbol table for "file scope"
112 while (1)
114 if (token.value == TOK.endOfFile)
116 addDefines(); // convert #define's to Dsymbols
118 // wrap the symbols in `extern (C) { symbols }`
119 auto wrap = new AST.Dsymbols();
120 auto ld = new AST.LinkDeclaration(token.loc, LINK.c, symbols);
121 wrap.push(ld);
123 if (importBuiltins)
125 /* Seen references to C builtin functions.
126 * Import their definitions
128 auto s = new AST.Import(Loc.initial, null, Id.builtins, null, false);
129 wrap.push(s);
132 // end of file scope
133 typedefTab.pop();
134 assert(typedefTab.length == 0);
136 return wrap;
139 cparseDeclaration(LVL.global);
143 /******************************************************************************/
144 /********************************* Statement Parser ***************************/
147 /**********************
148 * C11 6.8
149 * statement:
150 * labeled-statement
151 * compound-statement
152 * expression-statement
153 * selection-statement
154 * iteration-statement
155 * jump-statement
157 * Params:
158 * flags = PSxxxx
159 * endPtr = store location of closing brace
160 * pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
161 * Returns:
162 * parsed statement
164 AST.Statement cparseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
166 AST.Statement s;
167 const loc = token.loc;
169 //printf("cparseStatement()\n");
171 const typedefTabLengthSave = typedefTab.length;
172 auto symbolsSave = symbols;
173 if (flags & ParseStatementFlags.scope_)
175 typedefTab.push(null); // introduce new block scope
178 if (!(flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope)))
180 symbols = new AST.Dsymbols();
183 switch (token.value)
185 case TOK.identifier:
186 /* A leading identifier can be a declaration, label, or expression.
187 * A quick check of the next token can disambiguate most cases.
189 switch (peekNext())
191 case TOK.colon:
193 // It's a label
194 auto ident = token.ident;
195 nextToken(); // advance to `:`
196 nextToken(); // advance past `:`
197 if (token.value == TOK.rightCurly)
198 s = null;
199 else if (token.value == TOK.leftCurly)
200 s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
201 else
202 s = cparseStatement(0);
203 s = new AST.LabelStatement(loc, ident, s);
204 break;
207 case TOK.dot:
208 case TOK.arrow:
209 case TOK.plusPlus:
210 case TOK.minusMinus:
211 case TOK.leftBracket:
212 case TOK.question:
213 case TOK.assign:
214 case TOK.addAssign:
215 case TOK.minAssign:
216 case TOK.mulAssign:
217 case TOK.divAssign:
218 case TOK.modAssign:
219 case TOK.andAssign:
220 case TOK.orAssign:
221 case TOK.xorAssign:
222 case TOK.leftShiftAssign:
223 case TOK.rightShiftAssign:
224 goto Lexp;
226 case TOK.leftParenthesis:
227 if (auto pt = lookupTypedef(token.ident))
229 if (*pt)
230 goto Ldeclaration;
232 goto Lexp; // function call
234 case TOK.semicolon:
235 goto Lexp;
237 default:
239 /* If tokens look like a declaration, assume it is one
241 auto tk = &token;
242 if (isCDeclaration(tk))
243 goto Ldeclaration;
244 goto Lexp;
247 break;
249 case TOK.charLiteral:
250 case TOK.int32Literal:
251 case TOK.uns32Literal:
252 case TOK.int64Literal:
253 case TOK.uns64Literal:
254 case TOK.int128Literal:
255 case TOK.uns128Literal:
256 case TOK.float32Literal:
257 case TOK.float64Literal:
258 case TOK.float80Literal:
259 case TOK.imaginary32Literal:
260 case TOK.imaginary64Literal:
261 case TOK.imaginary80Literal:
262 case TOK.leftParenthesis:
263 case TOK.and:
264 case TOK.mul:
265 case TOK.min:
266 case TOK.add:
267 case TOK.tilde:
268 case TOK.not:
269 case TOK.plusPlus:
270 case TOK.minusMinus:
271 case TOK.sizeof_:
272 case TOK._Generic:
273 case TOK._assert:
274 Lexp:
275 auto exp = cparseExpression();
276 if (token.value == TOK.identifier && exp.op == EXP.identifier)
278 error(token.loc, "found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
279 nextToken();
281 else
282 check(TOK.semicolon, "statement");
283 s = new AST.ExpStatement(loc, exp);
284 break;
286 // type-specifiers
287 case TOK.void_:
288 case TOK.char_:
289 case TOK.int16:
290 case TOK.int32:
291 case TOK.int64:
292 case TOK.__int128:
293 case TOK.float32:
294 case TOK.float64:
295 case TOK.signed:
296 case TOK.unsigned:
297 case TOK._Bool:
298 //case TOK._Imaginary:
299 case TOK._Complex:
300 case TOK.struct_:
301 case TOK.union_:
302 case TOK.enum_:
303 case TOK.typeof_:
305 // storage-class-specifiers
306 case TOK.typedef_:
307 case TOK.extern_:
308 case TOK.static_:
309 case TOK._Thread_local:
310 case TOK.__thread:
311 case TOK.auto_:
312 case TOK.register:
314 // function-specifiers
315 case TOK.inline:
316 case TOK._Noreturn:
318 // type-qualifiers
319 case TOK.const_:
320 case TOK.volatile:
321 case TOK.restrict:
322 case TOK.__stdcall:
324 // alignment-specifier
325 case TOK._Alignas:
327 // atomic-type-specifier or type_qualifier
328 case TOK._Atomic:
330 case TOK.__attribute__:
332 Ldeclaration:
334 cparseDeclaration(LVL.local);
335 if (symbols.length > 1)
337 auto as = new AST.Statements();
338 as.reserve(symbols.length);
339 foreach (d; (*symbols)[])
341 s = new AST.ExpStatement(loc, d);
342 as.push(s);
344 s = new AST.CompoundDeclarationStatement(loc, as);
345 symbols.setDim(0);
347 else if (symbols.length == 1)
349 auto d = (*symbols)[0];
350 s = new AST.ExpStatement(loc, d);
351 symbols.setDim(0);
353 else
354 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
355 if (flags & ParseStatementFlags.scope_)
356 s = new AST.ScopeStatement(loc, s, token.loc);
357 break;
360 case TOK._Static_assert: // _Static_assert ( constant-expression, string-literal ) ;
361 s = new AST.StaticAssertStatement(cparseStaticAssert());
362 break;
364 case TOK.leftCurly:
366 /* C11 6.8.2
367 * compound-statement:
368 * { block-item-list (opt) }
370 * block-item-list:
371 * block-item
372 * block-item-list block-item
374 * block-item:
375 * declaration
376 * statement
378 nextToken();
379 auto statements = new AST.Statements();
380 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
382 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
384 if (endPtr)
385 *endPtr = token.ptr;
386 endloc = token.loc;
387 if (pEndloc)
389 *pEndloc = token.loc;
390 pEndloc = null; // don't set it again
392 s = new AST.CompoundStatement(loc, statements);
393 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
394 s = new AST.ScopeStatement(loc, s, token.loc);
395 check(TOK.rightCurly, "compound statement");
396 break;
399 case TOK.while_:
401 nextToken();
402 check(TOK.leftParenthesis);
403 auto condition = cparseExpression();
404 check(TOK.rightParenthesis);
405 Loc endloc;
406 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
407 s = new AST.WhileStatement(loc, condition, _body, endloc, null);
408 break;
411 case TOK.semicolon:
412 /* C11 6.8.3 null statement
414 nextToken();
415 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
416 break;
418 case TOK.do_:
420 nextToken();
421 auto _body = cparseStatement(ParseStatementFlags.scope_);
422 check(TOK.while_);
423 check(TOK.leftParenthesis);
424 auto condition = cparseExpression();
425 check(TOK.rightParenthesis);
426 check(TOK.semicolon, "terminating `;` required after do-while statement");
427 s = new AST.DoStatement(loc, _body, condition, token.loc);
428 break;
431 case TOK.for_:
433 AST.Statement _init;
434 AST.Expression condition;
435 AST.Expression increment;
437 nextToken();
438 check(TOK.leftParenthesis);
439 if (token.value == TOK.semicolon)
441 _init = null;
442 nextToken();
444 else
446 _init = cparseStatement(0);
448 if (token.value == TOK.semicolon)
450 condition = null;
451 nextToken();
453 else
455 condition = cparseExpression();
456 check(TOK.semicolon, "`for` condition");
458 if (token.value == TOK.rightParenthesis)
460 increment = null;
461 nextToken();
463 else
465 increment = cparseExpression();
466 check(TOK.rightParenthesis);
468 Loc endloc;
469 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
470 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
471 break;
474 case TOK.if_:
476 nextToken();
477 check(TOK.leftParenthesis);
478 auto condition = cparseExpression();
479 check(TOK.rightParenthesis);
480 auto ifbody = cparseStatement(ParseStatementFlags.scope_);
481 AST.Statement elsebody;
482 if (token.value == TOK.else_)
484 nextToken();
485 elsebody = cparseStatement(ParseStatementFlags.scope_);
487 else
488 elsebody = null;
489 if (condition && ifbody)
490 s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc);
491 else
492 s = null; // don't propagate parsing errors
493 break;
496 case TOK.else_:
497 error("found `else` without a corresponding `if` statement");
498 goto Lerror;
500 case TOK.switch_:
502 nextToken();
503 check(TOK.leftParenthesis);
504 auto condition = cparseExpression();
505 check(TOK.rightParenthesis);
506 auto _body = cparseStatement(ParseStatementFlags.scope_);
507 s = new AST.SwitchStatement(loc, null, condition, _body, false, token.loc);
508 break;
511 case TOK.case_:
514 nextToken();
515 auto exp = cparseAssignExp();
516 AST.Expression expHigh;
517 if (token.value == TOK.dotDotDot)
519 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
521 nextToken();
522 expHigh = cparseAssignExp();
524 check(TOK.colon);
526 if (flags & ParseStatementFlags.curlyScope)
528 auto statements = new AST.Statements();
529 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
531 auto cur = cparseStatement(ParseStatementFlags.curlyScope);
532 statements.push(cur);
534 // https://issues.dlang.org/show_bug.cgi?id=21739
535 // Stop at the last break s.t. the following non-case statements are
536 // not merged into the current case. This can happen for
537 // case 1: ... break;
538 // debug { case 2: ... }
539 if (cur && cur.isBreakStatement())
540 break;
542 s = new AST.CompoundStatement(loc, statements);
544 else
546 s = cparseStatement(0);
548 s = new AST.ScopeStatement(loc, s, token.loc);
549 if (expHigh)
550 s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
551 else
552 s = new AST.CaseStatement(loc, exp, s);
553 break;
556 case TOK.default_:
558 nextToken();
559 check(TOK.colon);
561 if (flags & ParseStatementFlags.curlyScope)
563 auto statements = new AST.Statements();
564 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
566 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
568 s = new AST.CompoundStatement(loc, statements);
570 else
571 s = cparseStatement(0);
572 s = new AST.ScopeStatement(loc, s, token.loc);
573 s = new AST.DefaultStatement(loc, s);
574 break;
577 case TOK.return_:
579 /* return ;
580 * return expression ;
582 nextToken();
583 auto exp = token.value == TOK.semicolon ? null : cparseExpression();
584 check(TOK.semicolon, "`return` statement");
585 s = new AST.ReturnStatement(loc, exp);
586 break;
589 case TOK.break_:
590 nextToken();
591 check(TOK.semicolon, "`break` statement");
592 s = new AST.BreakStatement(loc, null);
593 break;
595 case TOK.continue_:
596 nextToken();
597 check(TOK.semicolon, "`continue` statement");
598 s = new AST.ContinueStatement(loc, null);
599 break;
601 case TOK.goto_:
603 Identifier ident;
604 nextToken();
605 if (token.value != TOK.identifier)
607 error("identifier expected following `goto`");
608 ident = null;
610 else
612 ident = token.ident;
613 nextToken();
615 s = new AST.GotoStatement(loc, ident);
616 check(TOK.semicolon, "`goto` statement");
617 break;
620 case TOK.asm_:
621 switch (peekNext())
623 case TOK.goto_:
624 case TOK.inline:
625 case TOK.volatile:
626 case TOK.leftParenthesis:
627 s = cparseGnuAsm();
628 break;
630 default:
631 // ImportC extensions: parse as a D asm block.
632 s = parseAsm(compileEnv.masm);
633 break;
635 break;
637 default:
638 error("found `%s` instead of statement", token.toChars());
639 goto Lerror;
641 Lerror:
642 panic();
643 if (token.value == TOK.semicolon)
644 nextToken();
645 s = null;
646 break;
648 if (pEndloc)
649 *pEndloc = prevloc;
650 symbols = symbolsSave;
651 typedefTab.setDim(typedefTabLengthSave);
652 return s;
656 /*******************************************************************************/
657 /********************************* Expression Parser ***************************/
660 /**************
661 * C11 6.5.17
662 * expression:
663 * assignment-expression
664 * expression , assignment-expression
666 AST.Expression cparseExpression()
668 auto loc = token.loc;
670 //printf("cparseExpression() loc = %d\n", loc.linnum);
671 auto e = cparseAssignExp();
672 while (token.value == TOK.comma)
674 nextToken();
675 auto e2 = cparseAssignExp();
676 e = new AST.CommaExp(loc, e, e2, false);
677 loc = token.loc;
679 return e;
683 /*********************
684 * C11 6.5.1
685 * primary-expression:
686 * identifier
687 * constant
688 * string-literal
689 * ( expression )
690 * generic-selection
691 * __builtin_va_arg(assign_expression, type)
693 AST.Expression cparsePrimaryExp()
695 AST.Expression e;
696 const loc = token.loc;
698 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
699 switch (token.value)
701 case TOK.identifier:
702 const id = token.ident.toString();
703 if (id.length > 2 && id[0] == '_' && id[1] == '_') // leading double underscore
705 if (token.ident is Id.__func__)
707 addFuncName = true; // implicitly declare __func__
709 else if (token.ident is Id.builtin_va_arg)
711 e = cparseBuiltin_va_arg();
712 break;
714 else
715 importBuiltins = true; // probably one of those compiler extensions
717 e = new AST.IdentifierExp(loc, token.ident);
718 nextToken();
719 break;
721 case TOK.charLiteral:
722 case TOK.int32Literal:
723 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
724 nextToken();
725 break;
727 case TOK.uns32Literal:
728 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
729 nextToken();
730 break;
732 case TOK.int64Literal:
733 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
734 nextToken();
735 break;
737 case TOK.uns64Literal:
738 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
739 nextToken();
740 break;
742 case TOK.float32Literal:
743 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
744 nextToken();
745 break;
747 case TOK.float64Literal:
748 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
749 nextToken();
750 break;
752 case TOK.float80Literal:
753 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
754 nextToken();
755 break;
757 case TOK.imaginary32Literal:
758 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
759 nextToken();
760 break;
762 case TOK.imaginary64Literal:
763 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
764 nextToken();
765 break;
767 case TOK.imaginary80Literal:
768 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
769 nextToken();
770 break;
772 case TOK.string_:
774 // cat adjacent strings
775 auto s = token.ustring;
776 auto len = token.len;
777 auto postfix = token.postfix;
778 while (1)
780 nextToken();
781 if (token.value == TOK.string_)
783 if (token.postfix)
785 if (token.postfix != postfix)
786 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
787 postfix = token.postfix;
790 const len1 = len;
791 const len2 = token.len;
792 len = len1 + len2;
793 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
794 memcpy(s2, s, len1 * char.sizeof);
795 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
796 s = s2;
798 else
799 break;
801 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
802 break;
805 case TOK.leftParenthesis:
806 nextToken();
807 if (token.value == TOK.leftCurly)
808 e = cparseStatementExpression(); // gcc extension
809 else
810 e = cparseExpression();
811 check(TOK.rightParenthesis);
812 break;
814 case TOK._Generic:
815 e = cparseGenericSelection();
816 break;
818 case TOK._assert: // __check(assign-exp) extension
819 nextToken();
820 check(TOK.leftParenthesis, "`__check`");
821 e = parseAssignExp();
822 check(TOK.rightParenthesis);
823 e = new AST.AssertExp(loc, e, null);
824 break;
826 default:
827 error("expression expected, not `%s`", token.toChars());
828 // Anything for e, as long as it's not NULL
829 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
830 nextToken();
831 break;
833 return e;
836 /*********************************
837 * C11 6.5.2
838 * postfix-expression:
839 * primary-expression
840 * postfix-expression [ expression ]
841 * postfix-expression ( argument-expression-list (opt) )
842 * postfix-expression . identifier
843 * postfix-expression -> identifier
844 * postfix-expression ++
845 * postfix-expression --
846 * ( type-name ) { initializer-list }
847 * ( type-name ) { initializer-list , }
849 * argument-expression-list:
850 * assignment-expression
851 * argument-expression-list , assignment-expression
853 private AST.Expression cparsePostfixExp(AST.Expression e)
855 e = cparsePrimaryExp();
856 return cparsePostfixOperators(e);
859 /********************************
860 * C11 6.5.2
861 * Parse a series of operators for a postfix expression after already parsing
862 * a primary-expression or compound literal expression.
863 * Params:
864 * e = parsed primary or compound literal expression
865 * Returns:
866 * parsed postfix expression
868 private AST.Expression cparsePostfixOperators(AST.Expression e)
870 while (1)
872 const loc = token.loc;
873 switch (token.value)
875 case TOK.dot:
876 nextToken();
877 if (token.value == TOK.identifier)
879 Identifier id = token.ident;
880 e = new AST.DotIdExp(loc, e, id);
881 break;
883 error("identifier expected following `.`, not `%s`", token.toChars());
884 break;
886 case TOK.arrow:
887 nextToken();
888 if (token.value == TOK.identifier)
890 Identifier id = token.ident;
891 auto die = new AST.DotIdExp(loc, e, id);
892 die.arrow = true;
893 e = die;
894 break;
896 error("identifier expected following `->`, not `%s`", token.toChars());
897 break;
899 case TOK.plusPlus:
900 e = new AST.PostExp(EXP.plusPlus, loc, e);
901 break;
903 case TOK.minusMinus:
904 e = new AST.PostExp(EXP.minusMinus, loc, e);
905 break;
907 case TOK.leftParenthesis:
908 e = new AST.CallExp(loc, e, cparseArguments());
909 continue;
911 case TOK.leftBracket:
913 // array dereferences:
914 // array[index]
915 AST.Expression index;
916 auto arguments = new AST.Expressions();
918 inBrackets++;
919 nextToken();
920 index = cparseAssignExp();
921 arguments.push(index);
922 check(TOK.rightBracket);
923 inBrackets--;
924 e = new AST.ArrayExp(loc, e, arguments);
925 continue;
927 default:
928 return e;
930 nextToken();
934 /************************
935 * C11 6.5.3
936 * unary-expression:
937 * postfix-expression
938 * ++ unary-expression
939 * -- unary-expression
940 * unary-operator cast-expression
941 * sizeof unary-expression
942 * sizeof ( type-name )
943 * _Alignof ( type-name )
945 * unary-operator:
946 * & * + - ~ !
948 private AST.Expression cparseUnaryExp()
950 AST.Expression e;
951 const loc = token.loc;
953 switch (token.value)
955 case TOK.plusPlus:
956 nextToken();
957 // Parse `++` as an unary operator so that cast expressions only give
958 // an error for being non-lvalues.
959 e = cparseCastExp();
960 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
961 break;
963 case TOK.minusMinus:
964 nextToken();
965 // Parse `--` as an unary operator, same as prefix increment.
966 e = cparseCastExp();
967 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
968 break;
970 case TOK.and:
971 nextToken();
972 e = cparseCastExp();
973 e = new AST.AddrExp(loc, e);
974 break;
976 case TOK.mul:
977 nextToken();
978 e = cparseCastExp();
979 e = new AST.PtrExp(loc, e);
980 break;
982 case TOK.min:
983 nextToken();
984 e = cparseCastExp();
985 e = new AST.NegExp(loc, e);
986 break;
988 case TOK.add:
989 nextToken();
990 e = cparseCastExp();
991 e = new AST.UAddExp(loc, e);
992 break;
994 case TOK.not:
995 nextToken();
996 e = cparseCastExp();
997 e = new AST.NotExp(loc, e);
998 break;
1000 case TOK.tilde:
1001 nextToken();
1002 e = cparseCastExp();
1003 e = new AST.ComExp(loc, e);
1004 break;
1006 case TOK.sizeof_:
1008 nextToken();
1009 if (token.value == TOK.leftParenthesis)
1011 auto tk = peek(&token);
1012 if (isTypeName(tk))
1014 /* Expression may be either be requesting the sizeof a type-name
1015 * or a compound literal, which requires checking whether
1016 * the next token is leftCurly
1018 nextToken();
1019 auto t = cparseTypeName();
1020 check(TOK.rightParenthesis);
1021 if (token.value == TOK.leftCurly)
1023 // ( type-name ) { initializer-list }
1024 auto ci = cparseInitializer();
1025 e = new AST.CompoundLiteralExp(loc, t, ci);
1026 e = cparsePostfixOperators(e);
1028 else
1030 // ( type-name )
1031 e = new AST.TypeExp(loc, t);
1034 else
1036 // must be an expression
1037 e = cparseUnaryExp();
1040 else
1042 //C11 6.5.3
1043 e = cparseUnaryExp();
1046 e = new AST.DotIdExp(loc, e, Id.__sizeof);
1047 break;
1050 case TOK._Alignof:
1052 nextToken();
1053 check(TOK.leftParenthesis);
1054 auto t = cparseTypeName();
1055 check(TOK.rightParenthesis);
1056 e = new AST.TypeExp(loc, t);
1057 e = new AST.DotIdExp(loc, e, Id.__xalignof);
1058 break;
1061 default:
1062 e = cparsePostfixExp(e);
1063 break;
1065 assert(e);
1066 return e;
1069 /**************
1070 * C11 6.5.4
1071 * cast-expression
1072 * unary-expression
1073 * ( type-name ) cast-expression
1075 private AST.Expression cparseCastExp()
1077 if (token.value == TOK.leftParenthesis)
1079 //printf("cparseCastExp()\n");
1080 auto tk = peek(&token);
1081 bool iscast;
1082 bool isexp;
1083 if (tk.value == TOK.identifier)
1085 iscast = isTypedef(tk.ident);
1086 isexp = !iscast;
1088 if (isexp)
1090 // ( identifier ) is an expression
1091 return cparseUnaryExp();
1094 // If ( type-name )
1095 auto pt = &token;
1097 if (isCastExpression(pt))
1099 // Expression may be either a cast or a compound literal, which
1100 // requires checking whether the next token is leftCurly
1101 const loc = token.loc;
1102 nextToken();
1103 auto t = cparseTypeName();
1104 check(TOK.rightParenthesis);
1105 pt = &token;
1107 if (token.value == TOK.leftCurly)
1109 // C11 6.5.2.5 ( type-name ) { initializer-list }
1110 auto ci = cparseInitializer();
1111 auto ce = new AST.CompoundLiteralExp(loc, t, ci);
1112 return cparsePostfixOperators(ce);
1115 if (iscast)
1117 // ( type-name ) cast-expression
1118 auto ce = cparseCastExp();
1119 return new AST.CastExp(loc, ce, t);
1122 if (t.isTypeIdentifier() &&
1123 isexp &&
1124 token.value == TOK.leftParenthesis &&
1125 !isCastExpression(pt))
1127 /* (t)(...)... might be a cast expression or a function call,
1128 * with different grammars: a cast would be cparseCastExp(),
1129 * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1130 * We can't know until t is known. So, parse it as a function call
1131 * and let semantic() rewrite the AST as a CastExp if it turns out
1132 * to be a type.
1134 auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
1135 ie.parens = true; // let semantic know it might be a CastExp
1136 AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
1137 return cparsePostfixOperators(e);
1140 // ( type-name ) cast-expression
1141 auto ce = cparseCastExp();
1142 return new AST.CastExp(loc, ce, t);
1145 return cparseUnaryExp();
1148 /**************
1149 * C11 6.5.5
1150 * multiplicative-expression
1151 * cast-expression
1152 * multiplicative-expression * cast-expression
1153 * multiplicative-expression / cast-expression
1154 * multiplicative-expression % cast-expression
1156 private AST.Expression cparseMulExp()
1158 const loc = token.loc;
1159 auto e = cparseCastExp();
1161 while (1)
1163 switch (token.value)
1165 case TOK.mul:
1166 nextToken();
1167 auto e2 = cparseCastExp();
1168 e = new AST.MulExp(loc, e, e2);
1169 continue;
1171 case TOK.div:
1172 nextToken();
1173 auto e2 = cparseCastExp();
1174 e = new AST.DivExp(loc, e, e2);
1175 continue;
1177 case TOK.mod:
1178 nextToken();
1179 auto e2 = cparseCastExp();
1180 e = new AST.ModExp(loc, e, e2);
1181 continue;
1183 default:
1184 break;
1186 break;
1188 return e;
1191 /**************
1192 * C11 6.5.6
1193 * additive-expression
1194 * multiplicative-expression
1195 * additive-expression + multiplicative-expression
1196 * additive-expression - multiplicative-expression
1198 private AST.Expression cparseAddExp()
1200 const loc = token.loc;
1201 auto e = cparseMulExp();
1203 while (1)
1205 switch (token.value)
1207 case TOK.add:
1208 nextToken();
1209 auto e2 = cparseMulExp();
1210 e = new AST.AddExp(loc, e, e2);
1211 continue;
1213 case TOK.min:
1214 nextToken();
1215 auto e2 = cparseMulExp();
1216 e = new AST.MinExp(loc, e, e2);
1217 continue;
1219 default:
1220 break;
1222 break;
1224 return e;
1227 /**************
1228 * C11 6.5.7
1229 * shift-expression
1230 * additive-expression
1231 * shift-expression << additive-expression
1232 * shift-expression >> additive-expression
1234 private AST.Expression cparseShiftExp()
1236 const loc = token.loc;
1237 auto e = cparseAddExp();
1239 while (1)
1241 switch (token.value)
1243 case TOK.leftShift:
1244 nextToken();
1245 auto e2 = cparseAddExp();
1246 e = new AST.ShlExp(loc, e, e2);
1247 continue;
1249 case TOK.rightShift:
1250 nextToken();
1251 auto e2 = cparseAddExp();
1252 e = new AST.ShrExp(loc, e, e2);
1253 continue;
1255 default:
1256 break;
1258 break;
1260 return e;
1263 /**************
1264 * C11 6.5.8
1265 * relational-expression
1266 * shift-expression
1267 * relational-expression < shift-expression
1268 * relational-expression > shift-expression
1269 * relational-expression <= shift-expression
1270 * relational-expression >= shift-expression
1272 private AST.Expression cparseRelationalExp()
1274 const loc = token.loc;
1276 auto e = cparseShiftExp();
1278 EXP op = EXP.reserved;
1279 switch (token.value)
1281 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
1282 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
1283 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
1284 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
1285 Lcmp:
1286 nextToken();
1287 auto e2 = cparseShiftExp();
1288 e = new AST.CmpExp(op, loc, e, e2);
1289 break;
1291 default:
1292 break;
1294 return e;
1297 /**************
1298 * C11 6.5.9
1299 * equality-expression
1300 * relational-expression
1301 * equality-expression == relational-expression
1302 * equality-expression != relational-expression
1304 private AST.Expression cparseEqualityExp()
1306 const loc = token.loc;
1308 auto e = cparseRelationalExp();
1310 EXP op = EXP.reserved;
1311 switch (token.value)
1313 case TOK.equal: op = EXP.equal; goto Lequal;
1314 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
1315 Lequal:
1316 nextToken();
1317 auto e2 = cparseRelationalExp();
1318 e = new AST.EqualExp(op, loc, e, e2);
1319 break;
1321 default:
1322 break;
1324 return e;
1327 /**************
1328 * C11 6.5.10
1329 * AND-expression
1330 * equality-expression
1331 * AND-expression & equality-expression
1333 private AST.Expression cparseAndExp()
1335 Loc loc = token.loc;
1336 auto e = cparseEqualityExp();
1337 while (token.value == TOK.and)
1339 nextToken();
1340 auto e2 = cparseEqualityExp();
1341 e = new AST.AndExp(loc, e, e2);
1342 loc = token.loc;
1344 return e;
1347 /**************
1348 * C11 6.5.11
1349 * exclusive-OR-expression
1350 * AND-expression
1351 * exclusive-OR-expression ^ AND-expression
1353 private AST.Expression cparseXorExp()
1355 const loc = token.loc;
1357 auto e = cparseAndExp();
1358 while (token.value == TOK.xor)
1360 nextToken();
1361 auto e2 = cparseAndExp();
1362 e = new AST.XorExp(loc, e, e2);
1364 return e;
1367 /**************
1368 * C11 6.5.12
1369 * inclusive-OR-expression
1370 * exclusive-OR-expression
1371 * inclusive-OR-expression | exclusive-OR-expression
1373 private AST.Expression cparseOrExp()
1375 const loc = token.loc;
1377 auto e = cparseXorExp();
1378 while (token.value == TOK.or)
1380 nextToken();
1381 auto e2 = cparseXorExp();
1382 e = new AST.OrExp(loc, e, e2);
1384 return e;
1387 /**************
1388 * C11 6.5.13
1389 * logical-AND-expression
1390 * inclusive-OR-expression
1391 * logical-AND-expression && inclusive-OR-expression
1393 private AST.Expression cparseAndAndExp()
1395 const loc = token.loc;
1397 auto e = cparseOrExp();
1398 while (token.value == TOK.andAnd)
1400 nextToken();
1401 auto e2 = cparseOrExp();
1402 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
1404 return e;
1407 /**************
1408 * C11 6.5.14
1409 * logical-OR-expression
1410 * logical-AND-expression
1411 * logical-OR-expression || logical-AND-expression
1413 private AST.Expression cparseOrOrExp()
1415 const loc = token.loc;
1417 auto e = cparseAndAndExp();
1418 while (token.value == TOK.orOr)
1420 nextToken();
1421 auto e2 = cparseAndAndExp();
1422 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
1424 return e;
1427 /**************
1428 * C11 6.5.15
1429 * conditional-expression:
1430 * logical-OR-expression
1431 * logical-OR-expression ? expression : conditional-expression
1433 private AST.Expression cparseCondExp()
1435 const loc = token.loc;
1437 auto e = cparseOrOrExp();
1438 if (token.value == TOK.question)
1440 nextToken();
1441 auto e1 = cparseExpression();
1442 check(TOK.colon);
1443 auto e2 = cparseCondExp();
1444 e = new AST.CondExp(loc, e, e1, e2);
1446 return e;
1449 /**************
1450 * C11 6.5.16
1451 * assignment-expression:
1452 * conditional-expression
1453 * unary-expression assignment-operator assignment-expression
1455 * assignment-operator:
1456 * = *= /= %= += -= <<= >>= &= ^= |=
1458 AST.Expression cparseAssignExp()
1460 AST.Expression e;
1461 e = cparseCondExp(); // constrain it to being unary-expression in semantic pass
1462 if (e is null)
1463 return e;
1465 const loc = token.loc;
1466 switch (token.value)
1468 case TOK.assign:
1469 nextToken();
1470 auto e2 = cparseAssignExp();
1471 e = new AST.AssignExp(loc, e, e2);
1472 break;
1474 case TOK.addAssign:
1475 nextToken();
1476 auto e2 = cparseAssignExp();
1477 e = new AST.AddAssignExp(loc, e, e2);
1478 break;
1480 case TOK.minAssign:
1481 nextToken();
1482 auto e2 = cparseAssignExp();
1483 e = new AST.MinAssignExp(loc, e, e2);
1484 break;
1486 case TOK.mulAssign:
1487 nextToken();
1488 auto e2 = cparseAssignExp();
1489 e = new AST.MulAssignExp(loc, e, e2);
1490 break;
1492 case TOK.divAssign:
1493 nextToken();
1494 auto e2 = cparseAssignExp();
1495 e = new AST.DivAssignExp(loc, e, e2);
1496 break;
1498 case TOK.modAssign:
1499 nextToken();
1500 auto e2 = cparseAssignExp();
1501 e = new AST.ModAssignExp(loc, e, e2);
1502 break;
1504 case TOK.andAssign:
1505 nextToken();
1506 auto e2 = cparseAssignExp();
1507 e = new AST.AndAssignExp(loc, e, e2);
1508 break;
1510 case TOK.orAssign:
1511 nextToken();
1512 auto e2 = cparseAssignExp();
1513 e = new AST.OrAssignExp(loc, e, e2);
1514 break;
1516 case TOK.xorAssign:
1517 nextToken();
1518 auto e2 = cparseAssignExp();
1519 e = new AST.XorAssignExp(loc, e, e2);
1520 break;
1522 case TOK.leftShiftAssign:
1523 nextToken();
1524 auto e2 = cparseAssignExp();
1525 e = new AST.ShlAssignExp(loc, e, e2);
1526 break;
1528 case TOK.rightShiftAssign:
1529 nextToken();
1530 auto e2 = cparseAssignExp();
1531 e = new AST.ShrAssignExp(loc, e, e2);
1532 break;
1534 default:
1535 break;
1538 return e;
1541 /***********************
1542 * C11 6.5.1.1
1543 * _Generic ( assignment-expression, generic-assoc-list )
1545 * generic-assoc-list:
1546 * generic-association
1547 * generic-assoc-list generic-association
1549 * generic-association:
1550 * type-name : assignment-expression
1551 * default : assignment-expression
1553 private AST.Expression cparseGenericSelection()
1555 const loc = token.loc;
1556 nextToken();
1557 check(TOK.leftParenthesis);
1558 auto cntlExp = cparseAssignExp();
1559 check(TOK.comma);
1560 auto types = new AST.Types();
1561 auto exps = new AST.Expressions();
1562 bool sawDefault;
1563 while (1)
1565 AST.Type t;
1566 if (token.value == TOK.default_)
1568 nextToken();
1569 if (sawDefault)
1570 error("only one `default` allowed in generic-assoc-list");
1571 sawDefault = true;
1572 t = null;
1574 else
1575 t = cparseTypeName();
1576 types.push(t);
1578 check(TOK.colon);
1579 auto e = cparseAssignExp();
1580 exps.push(e);
1581 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
1582 break;
1583 check(TOK.comma);
1585 check(TOK.rightParenthesis);
1586 return new AST.GenericExp(loc, cntlExp, types, exps);
1589 /***********************
1590 * C11 6.6 Constant expressions
1591 * constant-expression:
1592 * conditional-expression
1594 private AST.Expression cparseConstantExp()
1596 return cparseAssignExp();
1599 /*****************************
1600 * gcc extension:
1601 * type __builtin_va_arg(assign-expression, type)
1602 * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1603 * va_arg!(type)(assign-expression);
1604 * Lexer is on `__builtin_va_arg`
1606 private AST.Expression cparseBuiltin_va_arg()
1608 importBuiltins = true; // need core.stdc.stdarg
1610 nextToken();
1611 check(TOK.leftParenthesis);
1613 auto arguments = new AST.Expressions();
1614 auto arg = cparseAssignExp();
1615 arguments.push(arg);
1617 check(TOK.comma);
1619 auto t = cparseTypeName();
1620 auto tiargs = new AST.Objects();
1621 tiargs.push(t);
1623 const loc = loc;
1624 auto ti = new AST.TemplateInstance(loc, Id.va_arg, tiargs);
1625 auto tie = new AST.ScopeExp(loc, ti);
1627 AST.Expression e = new AST.CallExp(loc, tie, arguments);
1629 check(TOK.rightParenthesis);
1630 return e;
1633 /*****************************
1634 * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1635 * Represent as a function literal, then call the function literal.
1636 * Parser is on opening curly brace.
1638 private AST.Expression cparseStatementExpression()
1640 AST.ParameterList parameterList;
1641 StorageClass stc = 0;
1642 const loc = token.loc;
1643 typedefTab.push(null);
1644 auto fbody = cparseStatement(ParseStatementFlags.scope_);
1645 typedefTab.pop(); // end of function scope
1647 // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1648 auto ss = fbody.isScopeStatement();
1649 auto cs = ss.statement.isCompoundStatement();
1650 assert(cs);
1651 if (const len = (*cs.statements).length)
1653 auto s = (*cs.statements)[len - 1];
1654 if (auto es = s.isExpStatement())
1655 (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
1658 auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
1659 auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
1660 fd.fbody = fbody;
1662 auto fe = new AST.FuncExp(loc, fd);
1663 auto args = new AST.Expressions();
1664 auto e = new AST.CallExp(loc, fe, args); // call the function literal
1665 return e;
1669 /********************************************************************************/
1670 /********************************* Declaration Parser ***************************/
1673 /*************************************
1674 * C11 6.7
1675 * declaration:
1676 * declaration-specifiers init-declarator-list (opt) ;
1677 * static_assert-declaration
1679 * init-declarator-list:
1680 * init-declarator
1681 * init-declarator-list , init-declarator
1683 * init-declarator:
1684 * declarator
1685 * declarator = initializer
1687 * Params:
1688 * level = declaration context
1690 void cparseDeclaration(LVL level)
1692 //printf("cparseDeclaration(level = %d)\n", level);
1693 if (token.value == TOK._Static_assert)
1695 auto s = cparseStaticAssert();
1696 symbols.push(s);
1697 return;
1700 if (token.value == TOK.__pragma)
1702 uupragmaDirective(scanloc);
1703 return;
1706 if (token.value == TOK._import) // import declaration extension
1708 auto a = parseImport();
1709 if (a && a.length)
1710 symbols.append(a);
1711 return;
1714 const typedefTabLengthSave = typedefTab.length;
1715 auto symbolsSave = symbols;
1716 Specifier specifier;
1717 specifier.packalign = this.packalign;
1718 auto tspec = cparseDeclarationSpecifiers(level, specifier);
1720 AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
1722 /* `struct tag;` and `struct tag { ... };`
1723 * always result in a declaration in the current scope
1725 auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
1726 (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
1727 new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
1728 if (!tt.packalign.isUnknown())
1730 // saw `struct __declspec(align(N)) Tag ...`
1731 auto st = stag.isStructDeclaration();
1732 st.alignment = tt.packalign;
1734 stag.members = tt.members;
1735 tt.members = null;
1736 if (!symbols)
1737 symbols = new AST.Dsymbols();
1738 auto stags = applySpecifier(stag, specifier);
1739 symbols.push(stags);
1740 return stags;
1743 /* If a declarator does not follow, it is unnamed
1745 if (token.value == TOK.semicolon)
1747 if (!tspec)
1749 nextToken();
1750 return; // accept empty declaration as an extension
1753 if (auto ti = tspec.isTypeIdentifier())
1755 // C11 6.7.2-2
1756 error("type-specifier missing for declaration of `%s`", ti.ident.toChars());
1757 nextToken();
1758 return;
1761 nextToken();
1762 auto tt = tspec.isTypeTag();
1763 if (!tt ||
1764 !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
1765 return; // legal but meaningless empty declaration, ignore it
1767 auto stags = declareTag(tt, specifier);
1769 if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it
1771 if (!tt.members)
1772 error(tt.loc, "`enum %s` has no members", stags.toChars());
1774 return;
1777 if (!tspec)
1779 error("no type for declarator before `%s`", token.toChars());
1780 panic();
1781 nextToken();
1782 return;
1785 if (tspec && specifier.mod & MOD.xconst)
1787 tspec = toConst(tspec);
1788 specifier.mod &= ~MOD.xnone; // 'used' it
1791 void scanPastSemicolon()
1793 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1794 nextToken();
1795 nextToken();
1798 if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier())
1800 /* C11 6.7.2-2
1801 * Special check for `const b = 1;` because some compilers allow it
1803 error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars());
1804 return scanPastSemicolon();
1807 bool first = true;
1808 while (1)
1810 Identifier id;
1811 AST.StringExp asmName;
1812 auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
1813 if (!dt)
1815 panic();
1816 nextToken();
1817 break; // error recovery
1820 /* GNU Extensions
1821 * init-declarator:
1822 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1823 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1825 switch (token.value)
1827 case TOK.assign:
1828 case TOK.comma:
1829 case TOK.semicolon:
1830 case TOK.asm_:
1831 case TOK.__attribute__:
1832 if (token.value == TOK.asm_)
1833 asmName = cparseGnuAsmLabel();
1834 if (token.value == TOK.__attribute__)
1836 cparseGnuAttributes(specifier);
1837 if (token.value == TOK.leftCurly)
1838 break; // function definition
1840 /* This is a data definition, there cannot now be a
1841 * function definition.
1843 first = false;
1844 break;
1846 default:
1847 break;
1850 if (specifier.alignExps && dt.isTypeFunction())
1851 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1852 if (specifier.alignExps && specifier.scw == SCW.xregister)
1853 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1855 /* C11 6.9.1 Function Definitions
1856 * function-definition:
1857 * declaration-specifiers declarator declaration-list (opt) compound-statement
1859 * declaration-list:
1860 * declaration
1861 * declaration-list declaration
1863 auto t = &token;
1864 if (first && // first declarator
1865 id &&
1866 dt.isTypeFunction() && // function type not inherited from a typedef
1867 isDeclarationList(t) && // optional declaration-list
1868 level == LVL.global && // function definitions only at global scope
1869 t.value == TOK.leftCurly) // start of compound-statement
1871 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
1872 typedefTab.setDim(typedefTabLengthSave);
1873 symbols = symbolsSave;
1874 symbols.push(s);
1875 return;
1877 AST.Dsymbol s = null;
1878 typedefTab.setDim(typedefTabLengthSave);
1879 symbols = symbolsSave;
1880 if (!symbols)
1881 symbols = new AST.Dsymbols; // lazilly create it
1883 if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
1884 error("declaration-specifier-seq required");
1885 else if (specifier.scw == SCW.xtypedef)
1887 if (token.value == TOK.assign)
1888 error("no initializer for typedef declaration");
1889 if (specifier.alignExps)
1890 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1892 bool isalias = true;
1893 if (auto ts = dt.isTypeStruct())
1895 if (ts.sym.isAnonymous())
1897 // This is a typedef for an anonymous struct-or-union.
1898 // Directly set the ident for the struct-or-union.
1899 ts.sym.ident = id;
1900 isalias = false;
1903 else if (auto te = dt.isTypeEnum())
1905 if (te.sym.isAnonymous())
1907 // This is a typedef for an anonymous enum.
1908 te.sym.ident = id;
1909 isalias = false;
1912 else if (auto tt = dt.isTypeTag())
1914 if (tt.id || tt.tok == TOK.enum_)
1916 if (!tt.id && id)
1917 /* This applies for enums declared as
1918 * typedef enum {A} E;
1920 tt.id = id;
1921 Specifier spec;
1922 declareTag(tt, spec);
1925 if (isalias)
1927 auto ad = new AST.AliasDeclaration(token.loc, id, dt);
1928 ad.adFlags |= ad.hidden; // do not print when generating .di files
1929 s = ad;
1932 insertTypedefToTypedefTab(id, dt); // remember typedefs
1934 else if (id)
1936 if (auto tt = dt.isTypeTag())
1938 if (tt.members && (tt.id || tt.tok == TOK.enum_))
1940 Specifier spec;
1941 declareTag(tt, spec);
1945 if (level == LVL.prototype)
1946 break; // declared later as Parameter, not VarDeclaration
1948 if (dt.ty == AST.Tvoid)
1949 error("`void` has no value");
1951 AST.Initializer initializer;
1952 bool hasInitializer;
1953 if (token.value == TOK.assign)
1955 nextToken();
1956 hasInitializer = true;
1957 initializer = cparseInitializer();
1959 // declare the symbol
1960 assert(id);
1962 if (isFunctionTypedef(dt))
1964 if (hasInitializer)
1965 error("no initializer for function declaration");
1966 if (specifier.scw & SCW.x_Thread_local)
1967 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1968 StorageClass stc = specifiersToSTC(level, specifier);
1969 stc &= ~STC.gshared; // no gshared functions
1970 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn);
1971 specifiersToFuncDeclaration(fd, specifier);
1972 s = fd;
1974 else
1976 // Give non-extern variables an implicit void initializer
1977 // if one has not been explicitly set.
1978 if (!hasInitializer &&
1979 !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
1980 initializer = new AST.VoidInitializer(token.loc);
1981 auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
1982 specifiersToVarDeclaration(vd, specifier);
1983 s = vd;
1985 if (level != LVL.global)
1986 insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes
1988 if (s !is null)
1990 // Saw `asm("name")` in the function, type, or variable definition.
1991 // This is equivalent to `pragma(mangle, "name")` in D
1992 if (asmName)
1995 https://issues.dlang.org/show_bug.cgi?id=23012
1996 Ideally this would be translated to a pragma(mangle)
1997 decl. This is not possible because ImportC symbols are
1998 (currently) merged before semantic analysis is performed,
1999 so the pragma(mangle) never effects any change on the declarations
2000 it pertains too.
2002 Writing to mangleOverride directly avoids this, and is possible
2003 because C only a StringExp is allowed unlike a full fat pragma(mangle)
2004 which is more liberal.
2006 if (auto p = s.isDeclaration())
2008 auto str = asmName.peekString();
2009 p.mangleOverride = str;
2010 // p.adFlags |= AST.VarDeclaration.nounderscore;
2011 p.adFlags |= 4; // cannot get above line to compile on Ubuntu
2014 s = applySpecifier(s, specifier);
2015 if (level == LVL.local)
2017 // Wrap the declaration in `extern (C) { declaration }`
2018 // Necessary for function pointers, but harmless to apply to all.
2019 auto decls = new AST.Dsymbols(1);
2020 (*decls)[0] = s;
2021 s = new AST.LinkDeclaration(s.loc, linkage, decls);
2023 symbols.push(s);
2025 first = false;
2027 switch (token.value)
2029 case TOK.identifier:
2030 if (s)
2032 error(token.loc, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
2033 goto Lend;
2035 goto default;
2037 case TOK.semicolon:
2038 nextToken();
2039 return;
2041 case TOK.comma:
2042 if (!symbolsSave)
2043 symbolsSave = symbols;
2044 nextToken();
2045 break;
2047 default:
2048 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars());
2049 Lend:
2050 return scanPastSemicolon();
2055 /***************************************
2056 * C11 Function Definitions
2057 * function-definition
2058 * declaration-specifiers declarator declaration-list (opt) compound-statement
2060 * declaration-list:
2061 * declaration
2062 * declaration-list declaration
2064 * It's already been parsed up to the declaration-list (opt).
2065 * Pick it up from there.
2066 * Params:
2067 * id = function identifier
2068 * ft = function type
2069 * specifier = function specifiers
2070 * Returns:
2071 * Dsymbol for the function
2073 AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
2075 /* Start function scope
2077 typedefTab.push(null);
2079 if (token.value != TOK.leftCurly) // if not start of a compound-statement
2081 // Do declaration-list
2084 cparseDeclaration(LVL.parameter);
2085 } while (token.value != TOK.leftCurly);
2087 /* Since there were declarations, the parameter-list must have been
2088 * an identifier-list.
2090 ft.parameterList.hasIdentifierList = true; // semantic needs to know to adjust parameter types
2091 auto pl = ft.parameterList;
2092 if (pl.varargs != AST.VarArg.none && pl.length)
2093 error("function identifier-list cannot end with `...`");
2094 ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments
2095 auto plLength = pl.length;
2096 if (symbols.length != plLength)
2097 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
2099 /* Transfer the types and storage classes from symbols[] to pl[]
2101 foreach (i; 0 .. plLength)
2103 auto p = pl[i]; // yes, quadratic
2105 // Convert typedef-identifier to identifier
2106 if (p.type)
2108 if (auto t = p.type.isTypeIdentifier())
2110 p.ident = t.ident;
2111 p.type = null;
2115 if (p.type || !(p.storageClass & STC.parameter))
2116 error("storage class and type are not allowed in identifier-list");
2117 foreach (s; (*symbols)[]) // yes, quadratic
2119 auto ad = s.isAttribDeclaration();
2120 if (ad)
2121 s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration
2123 auto d = s.isDeclaration();
2124 if (d && p.ident == d.ident && d.type)
2126 p.type = d.type;
2127 p.storageClass = d.storage_class;
2128 d.type = null; // don't reuse
2129 break;
2132 if (!p.type)
2134 error("no declaration for identifier `%s`", p.ident.toChars());
2135 p.type = AST.Type.terror;
2140 addFuncName = false; // gets set to true if somebody references __func__ in this function
2141 const locFunc = token.loc;
2143 auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope
2144 typedefTab.pop(); // end of function scope
2146 StorageClass stc = specifiersToSTC(LVL.global, specifier);
2147 stc &= ~STC.gshared; // no gshared functions
2148 auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn);
2149 specifiersToFuncDeclaration(fd, specifier);
2151 if (addFuncName)
2153 auto s = createFuncName(locFunc, id);
2154 body = new AST.CompoundStatement(locFunc, s, body);
2156 fd.fbody = body;
2158 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2160 return fd;
2163 /***************************************
2164 * C11 Initialization
2165 * initializer:
2166 * assignment-expression
2167 * { initializer-list }
2168 * { initializer-list , }
2170 * initializer-list:
2171 * designation (opt) initializer
2172 * initializer-list , designation (opt) initializer
2174 * designation:
2175 * designator-list =
2177 * designator-list:
2178 * designator
2179 * designator-list designator
2181 * designator:
2182 * [ constant-expression ]
2183 * . identifier
2184 * Returns:
2185 * initializer
2187 AST.Initializer cparseInitializer()
2189 if (token.value != TOK.leftCurly)
2191 auto ae = cparseAssignExp(); // assignment-expression
2192 return new AST.ExpInitializer(token.loc, ae);
2194 nextToken();
2195 const loc = token.loc;
2197 /* Collect one or more `designation (opt) initializer`
2198 * into ci.initializerList, but lazily create ci
2200 AST.CInitializer ci;
2201 while (1)
2203 /* There can be 0 or more designators preceding an initializer.
2204 * Collect them in desigInit
2206 AST.DesigInit desigInit;
2207 while (1)
2209 if (token.value == TOK.leftBracket) // [ constant-expression ]
2211 nextToken();
2212 auto e = cparseConstantExp();
2213 check(TOK.rightBracket);
2214 if (!desigInit.designatorList)
2215 desigInit.designatorList = new AST.Designators;
2216 desigInit.designatorList.push(AST.Designator(e));
2218 else if (token.value == TOK.dot) // . identifier
2220 nextToken();
2221 if (token.value != TOK.identifier)
2223 error("identifier expected following `.` designator");
2224 break;
2226 if (!desigInit.designatorList)
2227 desigInit.designatorList = new AST.Designators;
2228 desigInit.designatorList.push(AST.Designator(token.ident));
2229 nextToken();
2231 else
2233 if (desigInit.designatorList)
2234 check(TOK.assign);
2235 break;
2239 desigInit.initializer = cparseInitializer();
2240 if (!ci)
2241 ci = new AST.CInitializer(loc);
2242 ci.initializerList.push(desigInit);
2243 if (token.value == TOK.comma)
2245 nextToken();
2246 if (token.value != TOK.rightCurly)
2247 continue;
2249 break;
2251 check(TOK.rightCurly);
2252 //printf("ci: %s\n", ci.toChars());
2253 return ci;
2256 /*************************************
2257 * C11 6.7
2258 * declaration-specifier:
2259 * storage-class-specifier declaration-specifiers (opt)
2260 * type-specifier declaration-specifiers (opt)
2261 * type-qualifier declaration-specifiers (opt)
2262 * function-specifier declaration-specifiers (opt)
2263 * alignment-specifier declaration-specifiers (opt)
2264 * Params:
2265 * level = declaration context
2266 * specifier = specifiers in and out
2267 * Returns:
2268 * resulting type, null if not specified
2270 private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier)
2272 enum TKW : uint
2274 xnone = 0,
2275 xchar = 1,
2276 xsigned = 2,
2277 xunsigned = 4,
2278 xshort = 8,
2279 xint = 0x10,
2280 xlong = 0x20,
2281 xllong = 0x40,
2282 xfloat = 0x80,
2283 xdouble = 0x100,
2284 xldouble = 0x200,
2285 xtag = 0x400,
2286 xident = 0x800,
2287 xvoid = 0x1000,
2288 xbool = 0x4000,
2289 ximaginary = 0x8000,
2290 xcomplex = 0x10000,
2291 x_Atomic = 0x20000,
2292 xint128 = 0x40000,
2295 AST.Type t;
2296 Loc loc;
2297 //printf("parseDeclarationSpecifiers()\n");
2299 TKW tkw;
2300 SCW scw = specifier.scw & SCW.xtypedef;
2301 MOD mod;
2302 Identifier id;
2303 Identifier previd;
2305 Lwhile:
2306 while (1)
2308 //printf("token %s\n", token.toChars());
2309 TKW tkwx;
2310 SCW scwx;
2311 MOD modx;
2312 switch (token.value)
2314 // Storage class specifiers
2315 case TOK.static_: scwx = SCW.xstatic; break;
2316 case TOK.extern_: scwx = SCW.xextern; break;
2317 case TOK.auto_: scwx = SCW.xauto; break;
2318 case TOK.register: scwx = SCW.xregister; break;
2319 case TOK.typedef_: scwx = SCW.xtypedef; break;
2320 case TOK.inline: scwx = SCW.xinline; break;
2321 case TOK._Noreturn: scwx = SCW.x_Noreturn; break;
2322 case TOK.__thread:
2323 case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
2325 // Type qualifiers
2326 case TOK.const_: modx = MOD.xconst; break;
2327 case TOK.volatile: modx = MOD.xvolatile; break;
2328 case TOK.restrict: modx = MOD.xrestrict; break;
2329 case TOK.__stdcall: modx = MOD.x__stdcall; break;
2331 // Type specifiers
2332 case TOK.char_: tkwx = TKW.xchar; break;
2333 case TOK.signed: tkwx = TKW.xsigned; break;
2334 case TOK.unsigned: tkwx = TKW.xunsigned; break;
2335 case TOK.int16: tkwx = TKW.xshort; break;
2336 case TOK.int32: tkwx = TKW.xint; break;
2337 case TOK.int64: tkwx = TKW.xlong; break;
2338 case TOK.__int128: tkwx = TKW.xint128; break;
2339 case TOK.float32: tkwx = TKW.xfloat; break;
2340 case TOK.float64: tkwx = TKW.xdouble; break;
2341 case TOK.void_: tkwx = TKW.xvoid; break;
2342 case TOK._Bool: tkwx = TKW.xbool; break;
2343 case TOK._Imaginary: tkwx = TKW.ximaginary; break;
2344 case TOK._Complex: tkwx = TKW.xcomplex; break;
2346 case TOK.identifier:
2347 tkwx = TKW.xident;
2348 id = token.ident;
2349 break;
2351 case TOK.struct_:
2352 case TOK.union_:
2354 const structOrUnion = token.value;
2355 const sloc = token.loc;
2356 nextToken();
2358 Specifier tagSpecifier;
2360 /* GNU Extensions
2361 * struct-or-union-specifier:
2362 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2363 * struct-or-union gnu-attribute (opt) identifier
2365 while (1)
2367 if (token.value == TOK.__attribute__)
2368 cparseGnuAttributes(tagSpecifier);
2369 else if (token.value == TOK.__declspec)
2370 cparseDeclspec(tagSpecifier);
2371 else if (token.value == TOK.__pragma)
2372 uupragmaDirective(sloc);
2373 else
2374 break;
2376 t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols);
2377 tkwx = TKW.xtag;
2378 break;
2381 case TOK.enum_:
2382 t = cparseEnum(symbols);
2383 tkwx = TKW.xtag;
2384 break;
2386 case TOK._Atomic:
2388 // C11 6.7.2.4
2389 // type-specifier if followed by `( type-name )`
2390 auto tk = peek(&token);
2391 if (tk.value == TOK.leftParenthesis)
2393 tk = peek(tk);
2394 if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
2396 nextToken();
2397 nextToken();
2398 t = cparseTypeName();
2399 tkwx = TKW.x_Atomic;
2400 break;
2403 // C11 6.7.3 type-qualifier if not
2404 modx = MOD.x_Atomic;
2405 break;
2408 case TOK._Alignas:
2410 /* C11 6.7.5
2411 * _Alignas ( type-name )
2412 * _Alignas ( constant-expression )
2415 if (level & (LVL.parameter | LVL.prototype))
2416 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2418 nextToken();
2419 check(TOK.leftParenthesis);
2420 AST.Expression exp;
2421 auto tk = &token;
2422 if (isTypeName(tk)) // _Alignas ( type-name )
2424 auto talign = cparseTypeName();
2425 /* Convert type to expression: `talign.alignof`
2427 auto e = new AST.TypeExp(loc, talign);
2428 exp = new AST.DotIdExp(loc, e, Id.__xalignof);
2430 else // _Alignas ( constant-expression )
2432 exp = cparseConstantExp();
2435 if (!specifier.alignExps)
2436 specifier.alignExps = new AST.Expressions(0);
2437 specifier.alignExps.push(exp);
2439 check(TOK.rightParenthesis);
2440 break;
2443 case TOK.__attribute__:
2445 /* GNU Extensions
2446 * declaration-specifiers:
2447 * gnu-attributes declaration-specifiers (opt)
2449 cparseGnuAttributes(specifier);
2450 break;
2453 case TOK.__declspec:
2455 /* Microsoft extension
2457 cparseDeclspec(specifier);
2458 break;
2461 case TOK.typeof_:
2463 nextToken();
2464 check(TOK.leftParenthesis);
2466 auto tk = &token;
2467 AST.Expression e;
2468 if (isTypeName(tk))
2469 e = new AST.TypeExp(loc, cparseTypeName());
2470 else
2471 e = cparseExpression();
2472 t = new AST.TypeTypeof(loc, e);
2474 if(token.value == TOK.rightParenthesis)
2475 nextToken();
2476 else
2478 t = AST.Type.terror;
2479 error("`typeof` operator expects an expression or type name in parentheses");
2481 // skipParens et. al expect to be on the opening parenthesis
2482 int parens;
2483 loop: while(1)
2485 switch(token.value)
2487 case TOK.leftParenthesis:
2488 parens++;
2489 break;
2490 case TOK.rightParenthesis:
2491 parens--;
2492 if(parens < 0)
2493 goto case;
2494 break;
2495 case TOK.endOfFile:
2496 break loop;
2497 default:
2499 nextToken();
2503 tkwx = TKW.xtag;
2504 break;
2507 default:
2508 break Lwhile;
2511 if (tkwx)
2513 if (tkw & TKW.xlong && tkwx & TKW.xlong)
2515 tkw &= ~TKW.xlong;
2516 tkwx = TKW.xllong;
2518 if (tkw && tkwx & TKW.xident)
2520 // 2nd identifier can't be a typedef
2521 break Lwhile; // leave parser on the identifier for the following declarator
2523 else if (tkwx & TKW.xident)
2525 // 1st identifier, save it for TypeIdentifier
2526 previd = id;
2528 if (tkw & TKW.xident && tkwx || // typedef-name followed by type-specifier
2529 tkw & tkwx) // duplicate type-specifiers
2531 error("illegal combination of type specifiers");
2532 tkwx = TKW.init;
2534 tkw |= tkwx;
2535 if (!(tkwx & TKW.xtag)) // if parser already advanced
2536 nextToken();
2537 continue;
2540 if (modx)
2542 mod |= modx;
2543 nextToken();
2544 continue;
2547 if (scwx)
2549 if (scw & scwx)
2550 error("duplicate storage class");
2551 scw |= scwx;
2552 // C11 6.7.1-2 At most one storage-class may be given, except that
2553 // _Thread_local may appear with static or extern.
2554 const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef);
2555 if (scw2 & (scw2 - 1) ||
2556 scw & (SCW.x_Thread_local) && scw & (SCW.xauto | SCW.xregister | SCW.xtypedef))
2558 error("multiple storage classes in declaration specifiers");
2559 scw &= ~scwx;
2561 if (level == LVL.local &&
2562 scw & (SCW.x_Thread_local) && scw & (SCW.xinline | SCW.x_Noreturn))
2564 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2565 scw &= ~scwx;
2567 if (level == LVL.local &&
2568 scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
2570 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2571 scw &= ~scwx;
2573 if (level & (LVL.parameter | LVL.prototype) &&
2574 scw & ~SCW.xregister)
2576 error("only `register` storage class allowed for function parameters");
2577 scw &= ~scwx;
2579 if (level == LVL.global &&
2580 scw & (SCW.xauto | SCW.xregister))
2582 error("`auto` and `register` storage class not allowed for global");
2583 scw &= ~scwx;
2585 nextToken();
2586 continue;
2590 specifier.scw = scw;
2591 specifier.mod = mod;
2593 // Convert TKW bits to type t
2594 switch (tkw)
2596 case TKW.xnone: t = null; break;
2598 case TKW.xchar: t = AST.Type.tchar; break;
2599 case TKW.xsigned | TKW.xchar: t = AST.Type.tint8; break;
2600 case TKW.xunsigned | TKW.xchar: t = AST.Type.tuns8; break;
2602 case TKW.xshort:
2603 case TKW.xsigned | TKW.xshort:
2604 case TKW.xsigned | TKW.xshort | TKW.xint:
2605 case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break;
2607 case TKW.xunsigned | TKW.xshort | TKW.xint:
2608 case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break;
2610 case TKW.xint:
2611 case TKW.xsigned:
2612 case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break;
2614 case TKW.xunsigned:
2615 case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break;
2617 case TKW.xlong:
2618 case TKW.xsigned | TKW.xlong:
2619 case TKW.xsigned | TKW.xlong | TKW.xint:
2620 case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break;
2622 case TKW.xunsigned | TKW.xlong | TKW.xint:
2623 case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break;
2625 case TKW.xllong:
2626 case TKW.xsigned | TKW.xllong:
2627 case TKW.xsigned | TKW.xllong | TKW.xint:
2628 case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break;
2630 case TKW.xunsigned | TKW.xllong | TKW.xint:
2631 case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break;
2633 case TKW.xint128:
2634 case TKW.xsigned | TKW.xint128: t = integerTypeForSize(16); break;
2636 case TKW.xunsigned | TKW.xint128: t = unsignedTypeForSize(16); break;
2638 case TKW.xvoid: t = AST.Type.tvoid; break;
2639 case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break;
2641 case TKW.xfloat: t = AST.Type.tfloat32; break;
2642 case TKW.xdouble: t = AST.Type.tfloat64; break;
2643 case TKW.xlong | TKW.xdouble: t = realType(RTFlags.realfloat); break;
2645 case TKW.ximaginary | TKW.xfloat: t = AST.Type.timaginary32; break;
2646 case TKW.ximaginary | TKW.xdouble: t = AST.Type.timaginary64; break;
2647 case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break;
2649 case TKW.xcomplex | TKW.xfloat: t = AST.Type.tcomplex32; break;
2650 case TKW.xcomplex | TKW.xdouble: t = AST.Type.tcomplex64; break;
2651 case TKW.xcomplex | TKW.xlong | TKW.xdouble: t = realType(RTFlags.complex); break;
2653 case TKW.xident:
2655 const idx = previd.toString();
2656 if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore
2657 importBuiltins = true; // probably one of those compiler extensions
2658 t = null;
2660 /* Punch through to what the typedef is, to support things like:
2661 * typedef T* T;
2663 auto pt = lookupTypedef(previd);
2664 if (pt && *pt) // if previd is a known typedef
2665 t = *pt;
2667 if (!t)
2668 t = new AST.TypeIdentifier(loc, previd);
2669 break;
2672 case TKW.xtag:
2673 case TKW.x_Atomic: // no atomics for you
2674 break; // t is already set
2676 default:
2677 error("illegal type combination");
2678 t = AST.Type.terror;
2679 break;
2682 return t;
2685 /********************************
2686 * C11 6.7.6
2687 * Parse a declarator (including function definitions).
2688 * declarator:
2689 * pointer (opt) direct-declarator
2691 * direct-declarator :
2692 * identifier
2693 * ( declarator )
2694 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2695 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2696 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2697 * direct-declarator [ type-qualifier-list (opt) * ]
2698 * direct-declarator ( parameter-type-list )
2699 * direct-declarator ( identifier-list (opt) )
2701 * pointer :
2702 * * type-qualifier-list (opt)
2703 * * type-qualifier-list (opt) pointer
2705 * type-qualifier-list :
2706 * type-qualifier
2707 * type-qualifier-list type-qualifier
2709 * parameter-type-list :
2710 * parameter-list
2711 * parameter-list , ...
2713 * parameter-list :
2714 * parameter-declaration
2715 * parameter-list , parameter-declaration
2717 * parameter-declaration :
2718 * declaration-specifiers declarator
2719 * declaration-specifiers abstract-declarator (opt)
2721 * identifier-list :
2722 * identifier
2723 * identifier-list , identifier
2725 * Params:
2726 * declarator = declarator kind
2727 * tbase = base type to start with
2728 * pident = set to Identifier if there is one, null if not
2729 * specifier = specifiers in and out
2730 * Returns:
2731 * type declared. If a TypeFunction is returned, this.symbols is the
2732 * symbol table for the parameter-type-list, which will contain any
2733 * declared struct, union or enum tags.
2735 private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
2736 out Identifier pident, ref Specifier specifier)
2738 //printf("cparseDeclarator(%d, %p)\n", declarator, t);
2739 AST.Types constTypes; // all the Types that will need `const` applied to them
2741 /* Insert tx -> t into
2742 * ts -> ... -> t
2743 * so that
2744 * ts -> ... -> tx -> t
2746 static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t)
2748 AST.Type* pt;
2749 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
2752 *pt = tx;
2755 AST.Type parseDecl(AST.Type t)
2757 AST.Type ts;
2758 while (1)
2760 switch (token.value)
2762 case TOK.identifier: // identifier
2763 //printf("identifier %s\n", token.ident.toChars());
2764 if (declarator == DTR.xabstract)
2765 error("identifier not allowed in abstract-declarator");
2766 pident = token.ident;
2767 ts = t;
2768 nextToken();
2769 break;
2771 case TOK.leftParenthesis: // ( declarator )
2772 /* like: T (*fp)();
2773 * T ((*fp))();
2775 nextToken();
2777 if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
2779 specifier.mod |= MOD.x__stdcall;
2780 nextToken();
2783 ts = parseDecl(t);
2784 check(TOK.rightParenthesis);
2785 break;
2787 case TOK.mul: // pointer
2788 t = new AST.TypePointer(t);
2789 nextToken();
2790 // add post fixes const/volatile/restrict/_Atomic
2791 const mod = cparseTypeQualifierList();
2792 if (mod & MOD.xconst)
2793 constTypes.push(t);
2794 if (token.value == TOK.__attribute__)
2795 cparseGnuAttributes(specifier);
2796 continue;
2798 default:
2799 if (declarator == DTR.xdirect)
2801 if (!t || t.isTypeIdentifier())
2803 // const arr[1];
2804 error("no type-specifier for declarator");
2805 t = AST.Type.tint32;
2807 else
2808 error("identifier or `(` expected"); // )
2809 panic();
2811 ts = t;
2812 break;
2814 break;
2817 // parse DeclaratorSuffixes
2818 while (1)
2820 switch (token.value)
2822 case TOK.leftBracket:
2824 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2825 AST.Type ta;
2826 nextToken();
2828 auto mod = cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2830 bool isStatic;
2831 bool isVLA;
2832 if (token.value == TOK.static_)
2834 isStatic = true; // `static`
2835 nextToken();
2836 if (!mod) // type qualifiers after `static`
2837 mod = cparseTypeQualifierList();
2839 else if (token.value == TOK.mul)
2841 if (peekNext() == TOK.rightBracket)
2843 isVLA = true; // `*`
2844 nextToken();
2848 if (isStatic || token.value != TOK.rightBracket)
2850 //printf("It's a static array\n");
2851 AST.Expression e = cparseAssignExp(); // [ expression ]
2852 ta = new AST.TypeSArray(t, e);
2854 else
2856 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2858 ta = new AST.TypeSArray(t);
2860 check(TOK.rightBracket);
2862 // Issue errors for unsupported types.
2863 if (isVLA) // C11 6.7.6.2
2865 error("variable length arrays are not supported");
2867 if (isStatic) // C11 6.7.6.3
2869 error("static array parameters are not supported");
2871 if (declarator != DTR.xparameter)
2873 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2875 if (isVLA)
2876 error("variable length array used outside of function prototype");
2877 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2878 * in a declaration of a function parameter with an array type.
2880 if (isStatic || mod)
2881 error("static or type qualifier used outside of function prototype");
2883 if (ts.isTypeSArray() || ts.isTypeDArray())
2885 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2886 * in the outermost array type derivation.
2888 if (isStatic || mod)
2889 error("static or type qualifier used in non-outermost array type derivation");
2890 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2891 * function type.
2893 if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
2894 error("array type has incomplete element type `%s`", ta.toChars());
2897 // Apply type qualifiers to the constructed type.
2898 if (mod & MOD.xconst) // ignore the other bits
2899 ta = toConst(ta);
2900 insertTx(ts, ta, t); // ts -> ... -> ta -> t
2901 continue;
2904 case TOK.leftParenthesis:
2906 // New symbol table for parameter-list
2907 auto symbolsSave = this.symbols;
2908 this.symbols = null;
2910 auto parameterList = cparseParameterList();
2911 const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage;
2912 StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
2913 if (specifier._pure)
2914 stc |= STC.pure_;
2915 AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
2916 // tf = tf.addSTC(storageClass); // TODO
2917 insertTx(ts, tf, t); // ts -> ... -> tf -> t
2919 if (ts != tf)
2920 this.symbols = symbolsSave;
2921 break;
2924 default:
2925 break;
2927 break;
2929 return ts;
2932 auto t = parseDecl(tbase);
2934 if (specifier.vector_size)
2936 auto length = new AST.IntegerExp(token.loc, specifier.vector_size / tbase.size(), AST.Type.tuns32);
2937 auto tsa = new AST.TypeSArray(tbase, length);
2938 AST.Type tv = new AST.TypeVector(tsa);
2939 specifier.vector_size = 0; // used it up
2941 insertTx(t, tv, tbase); // replace tbase with tv
2944 /* Because const is transitive, cannot assemble types from
2945 * fragments. Instead, types to be annotated with const are put
2946 * in constTypes[], and a bottom up scan of t is done to apply
2947 * const
2949 if (constTypes.length)
2951 AST.Type constApply(AST.Type t)
2953 if (t.nextOf())
2955 auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this
2956 tn.next = constApply(tn.next);
2958 foreach (tc; constTypes[])
2960 if (tc is t)
2962 return toConst(t);
2965 return t;
2968 if (declarator == DTR.xparameter &&
2969 t.isTypePointer())
2971 /* Because there are instances in .h files of "const pointer to mutable",
2972 * skip applying transitive `const`
2973 * https://issues.dlang.org/show_bug.cgi?id=22534
2975 auto tn = cast(AST.TypeNext)t;
2976 tn.next = constApply(tn.next);
2978 else
2979 t = constApply(t);
2982 //printf("result: %s\n", t.toChars());
2983 return t;
2986 /******************************
2987 * C11 6.7.3
2988 * type-qualifier:
2989 * const
2990 * restrict
2991 * volatile
2992 * _Atomic
2993 * __stdcall
2995 MOD cparseTypeQualifierList()
2997 MOD mod;
2998 while (1)
3000 switch (token.value)
3002 case TOK.const_: mod |= MOD.xconst; break;
3003 case TOK.volatile: mod |= MOD.xvolatile; break;
3004 case TOK.restrict: mod |= MOD.xrestrict; break;
3005 case TOK._Atomic: mod |= MOD.x_Atomic; break;
3006 case TOK.__stdcall: mod |= MOD.x__stdcall; break;
3008 default:
3009 return mod;
3011 nextToken();
3015 /***********************************
3016 * C11 6.7.7
3018 AST.Type cparseTypeName()
3020 Specifier specifier;
3021 specifier.packalign.setDefault();
3022 auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
3023 if (!tspec)
3025 error("type-specifier is missing");
3026 tspec = AST.Type.tint32;
3028 if (tspec && specifier.mod & MOD.xconst)
3030 tspec = toConst(tspec);
3031 specifier.mod = MOD.xnone; // 'used' it
3033 Identifier id;
3034 return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
3037 /***********************************
3038 * C11 6.7.2.1
3039 * specifier-qualifier-list:
3040 * type-specifier specifier-qualifier-list (opt)
3041 * type-qualifier specifier-qualifier-list (opt)
3042 * Params:
3043 * level = declaration context
3044 * specifier = specifiers in and out
3045 * Returns:
3046 * resulting type, null if not specified
3048 AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier)
3050 auto t = cparseDeclarationSpecifiers(level, specifier);
3051 if (specifier.scw)
3052 error("storage class not allowed in specifier-qualified-list");
3053 return t;
3056 /***********************************
3057 * C11 6.7.6.3
3058 * ( parameter-type-list )
3059 * ( identifier-list (opt) )
3061 AST.ParameterList cparseParameterList()
3063 auto parameters = new AST.Parameters();
3064 AST.VarArg varargs = AST.VarArg.none;
3065 StorageClass varargsStc;
3067 check(TOK.leftParenthesis);
3068 if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
3070 nextToken();
3071 nextToken();
3072 return AST.ParameterList(parameters, varargs, varargsStc);
3075 if (token.value == TOK.rightParenthesis) // func()
3077 nextToken();
3078 return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc);
3081 /* Create function prototype scope
3083 typedefTab.push(null);
3085 AST.ParameterList finish()
3087 typedefTab.pop();
3088 return AST.ParameterList(parameters, varargs, varargsStc);
3091 /* The check for identifier-list comes later,
3092 * when doing the trailing declaration-list (opt)
3094 while (1)
3096 if (token.value == TOK.rightParenthesis)
3097 break;
3098 if (token.value == TOK.dotDotDot)
3100 if (parameters.length == 0) // func(...)
3101 error("named parameter required before `...`");
3102 importBuiltins = true; // will need __va_list_tag
3103 varargs = AST.VarArg.variadic; // C-style variadics
3104 nextToken();
3105 check(TOK.rightParenthesis);
3106 return finish();
3109 Specifier specifier;
3110 specifier.packalign.setDefault();
3111 auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
3112 if (!tspec)
3114 error("no type-specifier for parameter");
3115 tspec = AST.Type.tint32;
3118 if (specifier.mod & MOD.xconst)
3120 if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) &&
3121 tspec.isTypeIdentifier())
3122 error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars());
3124 tspec = toConst(tspec);
3125 specifier.mod = MOD.xnone; // 'used' it
3128 Identifier id;
3129 const paramLoc = token.loc;
3130 auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
3131 if (token.value == TOK.__attribute__)
3132 cparseGnuAttributes(specifier);
3133 if (specifier.mod & MOD.xconst)
3134 t = toConst(t);
3135 auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
3136 t, id, null, null);
3137 parameters.push(param);
3138 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
3139 break;
3140 check(TOK.comma);
3142 check(TOK.rightParenthesis);
3143 return finish();
3146 /***********************************
3147 * C11 6.7.10
3148 * _Static_assert ( constant-expression , string-literal ) ;
3150 private AST.StaticAssert cparseStaticAssert()
3152 const loc = token.loc;
3154 //printf("cparseStaticAssert()\n");
3155 nextToken();
3156 check(TOK.leftParenthesis);
3157 auto exp = cparseConstantExp();
3158 check(TOK.comma);
3159 if (token.value != TOK.string_)
3160 error("string literal expected");
3161 auto msg = cparsePrimaryExp();
3162 check(TOK.rightParenthesis);
3163 check(TOK.semicolon);
3164 return new AST.StaticAssert(loc, exp, msg);
3167 /*************************
3168 * Collect argument list.
3169 * Parser is on opening parenthesis.
3170 * Returns:
3171 * the arguments
3173 private AST.Expressions* cparseArguments()
3175 nextToken();
3176 auto arguments = new AST.Expressions();
3177 while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile)
3179 auto arg = cparseAssignExp();
3180 arguments.push(arg);
3181 if (token.value != TOK.comma)
3182 break;
3184 nextToken(); // consume comma
3187 check(TOK.rightParenthesis);
3189 return arguments;
3192 /*************************
3193 * __declspec parser
3194 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3195 * decl-specifier:
3196 * __declspec ( extended-decl-modifier-seq )
3198 * extended-decl-modifier-seq:
3199 * extended-decl-modifier (opt)
3200 * extended-decl-modifier extended-decl-modifier-seq
3202 * extended-decl-modifier:
3203 * align(number)
3204 * deprecated(depMsg)
3205 * dllimport
3206 * dllexport
3207 * naked
3208 * noinline
3209 * noreturn
3210 * nothrow
3211 * thread
3212 * Params:
3213 * specifier = filled in with the attribute(s)
3215 private void cparseDeclspec(ref Specifier specifier)
3217 //printf("cparseDeclspec()\n");
3218 /* Check for dllexport, dllimport
3219 * Ignore the rest
3221 nextToken(); // move past __declspec
3222 check(TOK.leftParenthesis);
3223 while (1)
3225 if (token.value == TOK.rightParenthesis)
3227 nextToken();
3228 break;
3230 else if (token.value == TOK.endOfFile)
3231 break;
3232 else if (token.value == TOK.identifier)
3234 if (token.ident == Id.dllimport)
3236 specifier.dllimport = true;
3237 nextToken();
3239 else if (token.ident == Id.dllexport)
3241 specifier.dllexport = true;
3242 nextToken();
3244 else if (token.ident == Id.naked)
3246 specifier.naked = true;
3247 nextToken();
3249 else if (token.ident == Id.noinline)
3251 specifier.scw |= SCW.xnoinline;
3252 nextToken();
3254 else if (token.ident == Id.noreturn)
3256 specifier.noreturn = true;
3257 nextToken();
3259 else if (token.ident == Id._nothrow)
3261 specifier._nothrow = true;
3262 nextToken();
3264 else if (token.ident == Id.thread)
3266 specifier.scw |= SCW.x_Thread_local;
3267 nextToken();
3269 else if (token.ident == Id._align)
3271 // Microsoft spec is very imprecise as to how this actually works
3272 nextToken();
3273 check(TOK.leftParenthesis);
3274 if (token.value == TOK.int32Literal)
3276 const n = token.unsvalue;
3277 if (n < 1 || n & (n - 1) || 8192 < n)
3278 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
3279 specifier.packalign.set(cast(uint)n);
3280 specifier.packalign.setPack(true);
3281 nextToken();
3283 else
3285 error("alignment value expected, not `%s`", token.toChars());
3286 nextToken();
3289 check(TOK.rightParenthesis);
3291 else if (token.ident == Id._deprecated)
3293 specifier._deprecated = true;
3294 nextToken();
3295 if (token.value == TOK.leftParenthesis) // optional deprecation message
3297 nextToken();
3298 specifier.depMsg = cparseExpression();
3299 check(TOK.rightParenthesis);
3302 else
3304 nextToken();
3305 if (token.value == TOK.leftParenthesis)
3306 cparseParens();
3309 else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3310 nextToken();
3311 else
3313 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token.toChars());
3314 nextToken();
3315 if (token.value != TOK.rightParenthesis)
3316 break;
3321 /*************************
3322 * Parser for asm label. It appears after the declarator, and has apparently
3323 * nothing to do with inline assembler.
3324 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3325 * simple-asm-expr:
3326 * asm ( asm-string-literal )
3328 * asm-string-literal:
3329 * string-literal
3331 private AST.StringExp cparseGnuAsmLabel()
3333 nextToken(); // move past asm
3334 check(TOK.leftParenthesis);
3335 if (token.value != TOK.string_)
3336 error("string literal expected for Asm Label, not `%s`", token.toChars());
3337 auto label = cparsePrimaryExp();
3338 check(TOK.rightParenthesis);
3339 return cast(AST.StringExp) label;
3342 /********************
3343 * Parse C inline assembler statement in Gnu format.
3344 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3345 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3346 * Current token is on the `asm`.
3347 * Returns:
3348 * inline assembler expression as a Statement
3350 private AST.Statement cparseGnuAsm()
3352 // Defer parsing of AsmStatements until semantic processing.
3353 const loc = token.loc;
3355 nextToken();
3357 // Consume all asm-qualifiers. As a future optimization, we could record
3358 // the `inline` and `volatile` storage classes against the statement.
3359 while (token.value == TOK.goto_ ||
3360 token.value == TOK.inline ||
3361 token.value == TOK.volatile)
3362 nextToken();
3364 check(TOK.leftParenthesis);
3365 if (token.value != TOK.string_)
3366 error("string literal expected for Assembler Template, not `%s`", token.toChars());
3367 Token* toklist = null;
3368 Token** ptoklist = &toklist;
3369 //Identifier label = null;
3370 auto statements = new AST.Statements();
3372 int parens;
3373 while (1)
3375 switch (token.value)
3377 case TOK.leftParenthesis:
3378 ++parens;
3379 goto default;
3381 case TOK.rightParenthesis:
3382 --parens;
3383 if (parens >= 0)
3384 goto default;
3385 break;
3387 case TOK.semicolon:
3388 error("matching `)` expected, not `;`");
3389 break;
3391 case TOK.endOfFile:
3392 /* ( */
3393 error("matching `)` expected, not end of file");
3394 break;
3396 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
3397 *ptoklist = allocateToken();
3398 memcpy(*ptoklist, &token, Token.sizeof);
3399 (*ptoklist).value = TOK.colon;
3400 ptoklist = &(*ptoklist).next;
3402 *ptoklist = allocateToken();
3403 memcpy(*ptoklist, &token, Token.sizeof);
3404 (*ptoklist).value = TOK.colon;
3405 ptoklist = &(*ptoklist).next;
3407 *ptoklist = null;
3408 nextToken();
3409 continue;
3411 default:
3412 *ptoklist = allocateToken();
3413 memcpy(*ptoklist, &token, Token.sizeof);
3414 ptoklist = &(*ptoklist).next;
3415 *ptoklist = null;
3416 nextToken();
3417 continue;
3419 if (toklist)
3421 // Create AsmStatement from list of tokens we've saved
3422 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
3423 statements.push(s);
3425 break;
3427 nextToken();
3428 auto s = new AST.CompoundAsmStatement(loc, statements, 0);
3429 return s;
3432 /*************************
3433 * __attribute__ parser
3434 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3435 * gnu-attributes:
3436 * gnu-attributes gnu-attribute-specifier
3438 * gnu-attribute-specifier:
3439 * __attribute__ (( gnu-attribute-list ))
3441 * gnu-attribute-list:
3442 * gnu-attribute (opt)
3443 * gnu-attribute-list , gnu-attribute
3445 * Params:
3446 * specifier = filled in with the attribute(s)
3448 private void cparseGnuAttributes(ref Specifier specifier)
3450 while (token.value == TOK.__attribute__)
3452 nextToken(); // move past __attribute__
3453 check(TOK.leftParenthesis);
3454 check(TOK.leftParenthesis);
3456 if (token.value != TOK.rightParenthesis)
3458 while (1)
3460 cparseGnuAttribute(specifier);
3461 if (token.value != TOK.comma)
3462 break;
3463 nextToken();
3467 check(TOK.rightParenthesis);
3468 check(TOK.rightParenthesis);
3472 /*************************
3473 * Parse a single GNU attribute
3474 * gnu-attribute:
3475 * gnu-attribute-name
3476 * gnu-attribute-name ( identifier )
3477 * gnu-attribute-name ( identifier , expression-list )
3478 * gnu-attribute-name ( expression-list (opt) )
3480 * gnu-attribute-name:
3481 * keyword
3482 * identifier
3484 * expression-list:
3485 * constant-expression
3486 * expression-list , constant-expression
3488 * Params:
3489 * specifier = filled in with the attribute(s)
3491 private void cparseGnuAttribute(ref Specifier specifier)
3493 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3494 * Ignore the rest
3496 if (!isGnuAttributeName())
3497 return;
3499 if (token.value == TOK.identifier)
3501 if (token.ident == Id.aligned)
3503 nextToken();
3504 if (token.value == TOK.leftParenthesis)
3506 nextToken();
3507 if (token.value == TOK.int32Literal)
3509 const n = token.unsvalue;
3510 if (n < 1 || n & (n - 1) || ushort.max < n)
3511 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3512 specifier.packalign.set(cast(uint)n);
3513 specifier.packalign.setPack(true);
3514 nextToken();
3516 else
3518 error("alignment value expected, not `%s`", token.toChars());
3519 nextToken();
3522 check(TOK.rightParenthesis);
3524 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3525 * type on the target machine. It's the opposite of __attribute__((packed))
3528 else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3530 specifier.scw |= SCW.xinline;
3531 nextToken();
3533 else if (token.ident == Id._deprecated)
3535 specifier._deprecated = true;
3536 nextToken();
3537 if (token.value == TOK.leftParenthesis) // optional deprecation message
3539 nextToken();
3540 specifier.depMsg = cparseExpression();
3541 check(TOK.rightParenthesis);
3544 else if (token.ident == Id.dllimport)
3546 specifier.dllimport = true;
3547 nextToken();
3549 else if (token.ident == Id.dllexport)
3551 specifier.dllexport = true;
3552 nextToken();
3554 else if (token.ident == Id.naked)
3556 specifier.naked = true;
3557 nextToken();
3559 else if (token.ident == Id.noinline)
3561 specifier.scw |= SCW.xnoinline;
3562 nextToken();
3564 else if (token.ident == Id.noreturn)
3566 specifier.noreturn = true;
3567 nextToken();
3569 else if (token.ident == Id._nothrow)
3571 specifier._nothrow = true;
3572 nextToken();
3574 else if (token.ident == Id._pure)
3576 specifier._pure = true;
3577 nextToken();
3579 else if (token.ident == Id.vector_size)
3581 nextToken();
3582 check(TOK.leftParenthesis);
3583 if (token.value == TOK.int32Literal)
3585 const n = token.unsvalue;
3586 if (n < 1 || n & (n - 1) || ushort.max < n)
3587 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3588 specifier.vector_size = cast(uint) n;
3589 nextToken();
3591 else
3593 error("value for vector_size expected, not `%s`", token.toChars());
3594 nextToken();
3596 check(TOK.rightParenthesis);
3598 else
3600 nextToken();
3601 if (token.value == TOK.leftParenthesis)
3602 cparseParens();
3605 else
3607 nextToken();
3608 if (token.value == TOK.leftParenthesis)
3609 cparseParens();
3613 /*************************
3614 * See if match for GNU attribute name, which may be any identifier,
3615 * storage-class-specifier, type-specifier, or type-qualifier.
3616 * Returns:
3617 * true if a valid GNU attribute name
3619 private bool isGnuAttributeName()
3621 switch (token.value)
3623 case TOK.identifier:
3624 case TOK.static_:
3625 case TOK.unsigned:
3626 case TOK.int64:
3627 case TOK.const_:
3628 case TOK.extern_:
3629 case TOK.register:
3630 case TOK.typedef_:
3631 case TOK.int16:
3632 case TOK.inline:
3633 case TOK._Noreturn:
3634 case TOK.volatile:
3635 case TOK.signed:
3636 case TOK.auto_:
3637 case TOK.restrict:
3638 case TOK._Complex:
3639 case TOK._Thread_local:
3640 case TOK.int32:
3641 case TOK.__int128:
3642 case TOK.char_:
3643 case TOK.float32:
3644 case TOK.float64:
3645 case TOK.void_:
3646 case TOK._Bool:
3647 case TOK._Atomic:
3648 return true;
3650 default:
3651 return false;
3655 /***************************
3656 * Like skipParens(), but consume the tokens.
3658 private void cparseParens()
3660 check(TOK.leftParenthesis);
3661 int parens = 1;
3663 while (1)
3665 switch (token.value)
3667 case TOK.leftParenthesis:
3668 ++parens;
3669 break;
3671 case TOK.rightParenthesis:
3672 --parens;
3673 if (parens < 0)
3675 error("extra right parenthesis");
3676 return;
3678 if (parens == 0)
3680 nextToken();
3681 return;
3683 break;
3685 case TOK.endOfFile:
3686 error("end of file found before right parenthesis");
3687 return;
3689 default:
3690 break;
3692 nextToken();
3697 /******************************************************************************/
3698 /***************************** Struct & Enum Parser ***************************/
3701 /*************************************
3702 * C11 6.7.2.2
3703 * enum-specifier:
3704 * enum identifier (opt) { enumerator-list }
3705 * enum identifier (opt) { enumerator-list , }
3706 * enum identifier
3708 * enumerator-list:
3709 * enumerator
3710 * enumerator-list , enumerator
3712 * enumerator:
3713 * enumeration-constant
3714 * enumeration-constant = constant-expression
3716 * enumeration-constant:
3717 * identifier
3719 * Params:
3720 * symbols = symbols to add enum declaration to
3721 * Returns:
3722 * type of the enum
3724 private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
3726 const loc = token.loc;
3727 nextToken();
3729 /* GNU Extensions
3730 * enum-specifier:
3731 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3732 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3733 * enum gnu-attributes (opt) identifier
3735 Specifier specifier;
3736 specifier.packalign.setDefault();
3737 if (token.value == TOK.__attribute__)
3738 cparseGnuAttributes(specifier);
3740 Identifier tag;
3741 if (token.value == TOK.identifier)
3743 tag = token.ident;
3744 nextToken();
3747 /* clang extension: add optional base type after the identifier
3748 * https://en.cppreference.com/w/cpp/language/enum
3749 * enum Identifier : Type
3751 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3752 AST.Type base = null; // C23 says base type is determined by enum member values
3753 if (token.value == TOK.colon)
3755 nextToken();
3756 base = cparseTypeName();
3759 AST.Dsymbols* members;
3760 if (token.value == TOK.leftCurly)
3762 nextToken();
3763 members = new AST.Dsymbols();
3765 if (token.value == TOK.rightCurly) // C11 6.7.2.2-1
3767 if (tag)
3768 error("no members for `enum %s`", tag.toChars());
3769 else
3770 error("no members for anonymous enum");
3773 while (token.value == TOK.identifier)
3775 auto ident = token.ident; // enumeration-constant
3776 nextToken();
3777 auto mloc = token.loc;
3779 if (token.value == TOK.__attribute__)
3781 /* gnu-attributes can appear here, but just scan and ignore them
3782 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3784 Specifier specifierx;
3785 specifierx.packalign.setDefault();
3786 cparseGnuAttributes(specifierx);
3789 AST.Expression value;
3790 if (token.value == TOK.assign)
3792 nextToken();
3793 value = cparseConstantExp();
3794 // TODO C11 6.7.2.2-2 value must fit into an int
3797 if (token.value == TOK.__attribute__)
3799 /* gnu-attributes can appear here, but just scan and ignore them
3800 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3802 Specifier specifierx;
3803 specifierx.packalign.setDefault();
3804 cparseGnuAttributes(specifierx);
3807 auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
3808 members.push(em);
3810 if (token.value == TOK.comma)
3812 nextToken();
3813 continue;
3815 break;
3817 check(TOK.rightCurly);
3819 /* GNU Extensions
3820 * Parse the postfix gnu-attributes (opt)
3822 if (token.value == TOK.__attribute__)
3823 cparseGnuAttributes(specifier);
3825 else if (!tag)
3826 error("missing `identifier` after `enum`");
3828 /* Need semantic information to determine if this is a declaration,
3829 * redeclaration, or reference to existing declaration.
3830 * Defer to the semantic() pass with a TypeTag.
3832 return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
3835 /*************************************
3836 * C11 6.7.2.1
3837 * Parse struct and union specifiers.
3838 * Parser is advanced to the tag identifier or brace.
3839 * struct-or-union-specifier:
3840 * struct-or-union identifier (opt) { struct-declaration-list }
3841 * struct-or-union identifier
3843 * struct-or-union:
3844 * struct
3845 * union
3847 * struct-declaration-list:
3848 * struct-declaration
3849 * struct-declaration-list struct-declaration
3851 * Params:
3852 * loc = location of `struct` or `union`
3853 * structOrUnion = TOK.struct_ or TOK.union_
3854 * packalign = alignment to use for struct members
3855 * symbols = symbols to add struct-or-union declaration to
3856 * Returns:
3857 * type of the struct
3859 private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
3861 Identifier tag;
3863 if (token.value == TOK.identifier)
3865 tag = token.ident;
3866 nextToken();
3869 AST.Dsymbols* members;
3870 if (token.value == TOK.leftCurly)
3872 nextToken();
3873 members = new AST.Dsymbols(); // so `members` will be non-null even with 0 members
3874 while (token.value != TOK.rightCurly)
3876 cparseStructDeclaration(members);
3878 if (token.value == TOK.endOfFile)
3879 break;
3881 check(TOK.rightCurly);
3883 if ((*members).length == 0) // C11 6.7.2.1-8
3885 /* allow empty structs as an extension
3886 * struct-declarator-list:
3887 * struct-declarator (opt)
3891 else if (!tag)
3892 error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
3894 // many ways and places to declare alignment
3895 if (packalign.isUnknown() && !this.packalign.isUnknown())
3896 packalign.set(this.packalign.get());
3898 /* Need semantic information to determine if this is a declaration,
3899 * redeclaration, or reference to existing declaration.
3900 * Defer to the semantic() pass with a TypeTag.
3902 return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
3905 /*************************************
3906 * C11 6.7.2.1
3907 * Parse a struct declaration member.
3908 * struct-declaration:
3909 * specifier-qualifier-list struct-declarator-list (opt) ;
3910 * static_assert-declaration
3912 * struct-declarator-list:
3913 * struct-declarator
3914 * struct-declarator-list , struct-declarator
3916 * struct-declarator:
3917 * declarator
3918 * declarator (opt) : constant-expression
3919 * Params:
3920 * members = where to put the fields (members)
3922 void cparseStructDeclaration(AST.Dsymbols* members)
3924 //printf("cparseStructDeclaration()\n");
3925 if (token.value == TOK._Static_assert)
3927 auto s = cparseStaticAssert();
3928 members.push(s);
3929 return;
3932 Specifier specifier;
3933 specifier.packalign = this.packalign;
3934 auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
3935 if (!tspec)
3937 error("no type-specifier for struct member");
3938 tspec = AST.Type.tint32;
3940 if (specifier.mod & MOD.xconst)
3942 tspec = toConst(tspec);
3943 specifier.mod = MOD.xnone; // 'used' it
3946 /* If a declarator does not follow, it is unnamed
3948 if (token.value == TOK.semicolon && tspec)
3950 nextToken();
3951 auto tt = tspec.isTypeTag();
3952 if (!tt)
3954 if (auto ti = tspec.isTypeIdentifier())
3956 error("type-specifier omitted before declaration of `%s`", ti.ident.toChars());
3958 return; // legal but meaningless empty declaration
3961 /* If anonymous struct declaration
3962 * struct { ... members ... };
3963 * C11 6.7.2.1-13
3965 if (!tt.id && tt.members)
3967 /* members of anonymous struct are considered members of
3968 * the containing struct
3970 auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
3971 auto s = applySpecifier(ad, specifier);
3972 members.push(s);
3973 return;
3975 if (!tt.id && !tt.members)
3976 return; // already gave error in cparseStruct()
3978 /* `struct tag;` and `struct tag { ... };`
3979 * always result in a declaration in the current scope
3981 // TODO: merge in specifier
3982 auto stag = (tt.tok == TOK.struct_)
3983 ? new AST.StructDeclaration(tt.loc, tt.id, false)
3984 : new AST.UnionDeclaration(tt.loc, tt.id);
3985 stag.members = tt.members;
3986 if (!symbols)
3987 symbols = new AST.Dsymbols();
3988 auto s = applySpecifier(stag, specifier);
3989 symbols.push(s);
3990 return;
3993 while (1)
3995 Identifier id;
3996 AST.Type dt;
3997 if (token.value == TOK.colon)
3999 if (auto ti = tspec.isTypeIdentifier())
4001 error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars());
4002 tspec = AST.Type.tint32;
4005 // C11 6.7.2.1-12 unnamed bit-field
4006 id = Identifier.generateAnonymousId("BitField");
4007 dt = tspec;
4009 else
4011 dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
4012 if (!dt)
4014 panic();
4015 nextToken();
4016 break; // error recovery
4020 AST.Expression width;
4021 if (token.value == TOK.colon)
4023 // C11 6.7.2.1-10 bit-field
4024 nextToken();
4025 width = cparseConstantExp();
4028 /* GNU Extensions
4029 * struct-declarator:
4030 * declarator gnu-attributes (opt)
4031 * declarator (opt) : constant-expression gnu-attributes (opt)
4033 if (token.value == TOK.__attribute__)
4034 cparseGnuAttributes(specifier);
4036 if (!tspec && !specifier.scw && !specifier.mod)
4037 error("specifier-qualifier-list required");
4038 else if (width)
4040 if (specifier.alignExps)
4041 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4042 auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
4043 members.push(s);
4045 else if (id)
4047 if (dt.ty == AST.Tvoid)
4048 error("`void` has no value");
4050 // declare the symbol
4051 // Give member variables an implicit void initializer
4052 auto initializer = new AST.VoidInitializer(token.loc);
4053 AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
4054 s = applySpecifier(s, specifier);
4055 members.push(s);
4058 switch (token.value)
4060 case TOK.identifier:
4061 error("missing comma");
4062 goto default;
4064 case TOK.semicolon:
4065 nextToken();
4066 return;
4068 case TOK.comma:
4069 nextToken();
4070 break;
4072 default:
4073 error("`;` or `,` expected");
4074 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
4075 nextToken();
4076 nextToken();
4077 return;
4083 /******************************************************************************/
4084 /********************************* Lookahead Parser ***************************/
4087 /************************************
4088 * Determine if the scanner is sitting on the start of a declaration.
4089 * Params:
4090 * t = current token of the scanner
4091 * needId = flag with additional requirements for a declaration
4092 * endtok = ending token
4093 * pt = will be set ending token (if not null)
4094 * Returns:
4095 * true at start of a declaration
4097 private bool isCDeclaration(ref Token* pt)
4099 auto t = pt;
4100 //printf("isCDeclaration() %s\n", t.toChars());
4101 if (!isDeclarationSpecifiers(t))
4102 return false;
4104 while (1)
4106 if (t.value == TOK.semicolon)
4108 t = peek(t);
4109 pt = t;
4110 return true;
4112 if (!isCDeclarator(t, DTR.xdirect))
4113 return false;
4114 if (t.value == TOK.asm_)
4116 t = peek(t);
4117 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4118 return false;
4120 if (t.value == TOK.__attribute__)
4122 t = peek(t);
4123 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4124 return false;
4126 if (t.value == TOK.assign)
4128 t = peek(t);
4129 if (!isInitializer(t))
4130 return false;
4132 switch (t.value)
4134 case TOK.comma:
4135 t = peek(t);
4136 break;
4138 case TOK.semicolon:
4139 t = peek(t);
4140 pt = t;
4141 return true;
4143 default:
4144 return false;
4149 /********************************
4150 * See if match for initializer.
4151 * Params:
4152 * pt = starting token, updated to one past end of initializer if true
4153 * Returns:
4154 * true if initializer
4156 private bool isInitializer(ref Token* pt)
4158 //printf("isInitializer()\n");
4159 auto t = pt;
4161 if (t.value == TOK.leftCurly)
4163 if (!skipBraces(t))
4164 return false;
4165 pt = t;
4166 return true;
4169 // skip over assignment-expression, ending before comma or semiColon or EOF
4170 if (!isAssignmentExpression(t))
4171 return false;
4172 pt = t;
4173 return true;
4176 /********************************
4177 * See if match for:
4178 * postfix-expression ( argument-expression-list(opt) )
4179 * Params:
4180 * pt = starting token, updated to one past end of initializer if true
4181 * Returns:
4182 * true if function call
4184 private bool isFunctionCall(ref Token* pt)
4186 //printf("isFunctionCall()\n");
4187 auto t = pt;
4189 if (!isPrimaryExpression(t))
4190 return false;
4191 if (t.value != TOK.leftParenthesis)
4192 return false;
4193 t = peek(t);
4194 while (1)
4196 if (!isAssignmentExpression(t))
4197 return false;
4198 if (t.value == TOK.comma)
4200 t = peek(t);
4201 continue;
4203 if (t.value == TOK.rightParenthesis)
4205 t = peek(t);
4206 break;
4208 return false;
4210 if (t.value != TOK.semicolon)
4211 return false;
4212 pt = t;
4213 return true;
4216 /********************************
4217 * See if match for assignment-expression.
4218 * Params:
4219 * pt = starting token, updated to one past end of assignment-expression if true
4220 * Returns:
4221 * true if assignment-expression
4223 private bool isAssignmentExpression(ref Token* pt)
4225 auto t = pt;
4226 //printf("isAssignmentExpression() %s\n", t.toChars());
4228 /* This doesn't actually check for grammar matching an
4229 * assignment-expression. It just matches ( ) [ ] looking for
4230 * an ending token that would terminate one.
4232 bool any;
4233 while (1)
4235 switch (t.value)
4237 case TOK.comma:
4238 case TOK.semicolon:
4239 case TOK.rightParenthesis:
4240 case TOK.rightBracket:
4241 case TOK.endOfFile:
4242 if (!any)
4243 return false;
4244 break;
4246 case TOK.leftParenthesis:
4247 if (!skipParens(t, &t))
4248 return false;
4250 https://issues.dlang.org/show_bug.cgi?id=22267
4251 If the parser encounters the following
4252 `identifier variableName = (expression);`
4253 the initializer is not identified as such since the parentheses
4254 cause the parser to keep walking indefinitely
4255 (whereas `(1) + 1` would not be affected.).
4257 any = true;
4258 continue;
4260 case TOK.leftBracket:
4261 if (!skipBrackets(t))
4262 return false;
4263 continue;
4265 case TOK.leftCurly:
4266 if (!skipBraces(t))
4267 return false;
4268 continue;
4270 default:
4271 any = true; // assume token was part of an a-e
4272 t = peek(t);
4273 continue;
4275 pt = t;
4276 return true;
4280 /********************************
4281 * See if match for constant-expression.
4282 * Params:
4283 * pt = starting token, updated to one past end of constant-expression if true
4284 * Returns:
4285 * true if constant-expression
4287 private bool isConstantExpression(ref Token* pt)
4289 return isAssignmentExpression(pt);
4292 /********************************
4293 * See if match for declaration-specifiers.
4294 * No errors are diagnosed.
4295 * Params:
4296 * pt = starting token, updated to one past end of declaration-specifiers if true
4297 * Returns:
4298 * true if declaration-specifiers
4300 private bool isDeclarationSpecifiers(ref Token* pt)
4302 //printf("isDeclarationSpecifiers()\n");
4304 auto t = pt;
4306 bool seenType;
4307 bool any;
4308 while (1)
4310 switch (t.value)
4312 // type-specifiers
4313 case TOK.void_:
4314 case TOK.char_:
4315 case TOK.int16:
4316 case TOK.int32:
4317 case TOK.int64:
4318 case TOK.__int128:
4319 case TOK.float32:
4320 case TOK.float64:
4321 case TOK.signed:
4322 case TOK.unsigned:
4323 case TOK._Bool:
4324 //case TOK._Imaginary:
4325 case TOK._Complex:
4326 t = peek(t);
4327 seenType = true;
4328 any = true;
4329 continue;
4331 case TOK.identifier: // typedef-name
4332 if (!seenType)
4334 t = peek(t);
4335 seenType = true;
4336 any = true;
4337 continue;
4339 break;
4341 case TOK.struct_:
4342 case TOK.union_:
4343 case TOK.enum_:
4344 t = peek(t);
4345 if (t.value == TOK.__attribute__ ||
4346 t.value == TOK.__declspec)
4348 t = peek(t);
4349 if (!skipParens(t, &t))
4350 return false;
4352 if (t.value == TOK.identifier)
4354 t = peek(t);
4355 if (t.value == TOK.leftCurly)
4357 if (!skipBraces(t))
4358 return false;
4361 else if (t.value == TOK.leftCurly)
4363 if (!skipBraces(t))
4364 return false;
4366 else
4367 return false;
4368 any = true;
4369 continue;
4371 // storage-class-specifiers
4372 case TOK.typedef_:
4373 case TOK.extern_:
4374 case TOK.static_:
4375 case TOK.__thread:
4376 case TOK._Thread_local:
4377 case TOK.auto_:
4378 case TOK.register:
4380 // function-specifiers
4381 case TOK.inline:
4382 case TOK._Noreturn:
4384 // type-qualifiers
4385 case TOK.const_:
4386 case TOK.volatile:
4387 case TOK.restrict:
4388 case TOK.__stdcall:
4389 t = peek(t);
4390 any = true;
4391 continue;
4393 case TOK._Alignas: // alignment-specifier
4394 case TOK.__declspec: // decl-specifier
4395 case TOK.__attribute__: // attribute-specifier
4396 t = peek(t);
4397 if (!skipParens(t, &t))
4398 return false;
4399 any = true;
4400 continue;
4402 // either atomic-type-specifier or type_qualifier
4403 case TOK._Atomic: // TODO _Atomic ( type-name )
4404 t = peek(t);
4405 if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
4407 auto tsave = t;
4408 t = peek(t);
4409 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4410 { // it's a type-qualifier
4411 t = tsave; // back up parser
4412 any = true;
4413 continue;
4415 t = peek(t); // move past right parenthesis of atomic-type-specifier
4417 any = true;
4418 continue;
4420 default:
4421 break;
4423 break;
4426 if (any)
4428 pt = t;
4429 return true;
4431 return false;
4434 /**************************************
4435 * See if declaration-list is present.
4436 * Returns:
4437 * true if declaration-list is present, even an empty one
4439 bool isDeclarationList(ref Token* pt)
4441 auto t = pt;
4442 while (1)
4444 if (t.value == TOK.leftCurly)
4446 pt = t;
4447 return true;
4449 if (!isCDeclaration(t))
4450 return false;
4454 /*******************************************
4455 * Skip braces.
4456 * Params:
4457 * pt = enters on left brace, set to token past right bracket on true
4458 * Returns:
4459 * true if successful
4461 private bool skipBraces(ref Token* pt)
4463 auto t = pt;
4464 if (t.value != TOK.leftCurly)
4465 return false;
4467 int braces = 0;
4469 while (1)
4471 switch (t.value)
4473 case TOK.leftCurly:
4474 ++braces;
4475 t = peek(t);
4476 continue;
4478 case TOK.rightCurly:
4479 --braces;
4480 if (braces == 0)
4482 pt = peek(t);
4483 return true;
4485 if (braces < 0)
4486 return false;
4488 t = peek(t);
4489 continue;
4491 case TOK.endOfFile:
4492 return false;
4494 default:
4495 t = peek(t);
4496 continue;
4501 /*******************************************
4502 * Skip brackets.
4503 * Params:
4504 * pt = enters on left bracket, set to token past right bracket on true
4505 * Returns:
4506 * true if successful
4508 private bool skipBrackets(ref Token* pt)
4510 auto t = pt;
4511 if (t.value != TOK.leftBracket)
4512 return false;
4514 int brackets = 0;
4516 while (1)
4518 switch (t.value)
4520 case TOK.leftBracket:
4521 ++brackets;
4522 t = peek(t);
4523 continue;
4525 case TOK.rightBracket:
4526 --brackets;
4527 if (brackets == 0)
4529 pt = peek(t);
4530 return true;
4532 if (brackets < 0)
4533 return false;
4535 t = peek(t);
4536 continue;
4538 case TOK.endOfFile:
4539 return false;
4541 default:
4542 t = peek(t);
4543 continue;
4548 /*********************************
4549 * Check to see if tokens starting with *pt form a declarator.
4550 * Params:
4551 * pt = pointer to starting token, updated to point past declarator if true is returned
4552 * declarator = declarator kind
4553 * Returns:
4554 * true if it does
4556 private bool isCDeclarator(ref Token* pt, DTR declarator)
4558 auto t = pt;
4559 while (1)
4561 if (t.value == TOK.mul) // pointer
4563 t = peek(t);
4564 if (!isTypeQualifierList(t))
4565 return false;
4567 else
4568 break;
4571 if (t.value == TOK.identifier)
4573 if (declarator == DTR.xabstract)
4574 return false;
4575 t = peek(t);
4577 else if (t.value == TOK.leftParenthesis)
4579 t = peek(t);
4580 if (!isCDeclarator(t, declarator))
4581 return false;
4582 if (t.value != TOK.rightParenthesis)
4583 return false;
4584 t = peek(t);
4586 else if (declarator == DTR.xdirect)
4588 return false;
4591 while (1)
4593 if (t.value == TOK.leftBracket)
4595 if (!skipBrackets(t))
4596 return false;
4598 else if (t.value == TOK.leftParenthesis)
4600 if (!skipParens(t, &t))
4601 return false;
4603 else
4604 break;
4606 pt = t;
4607 return true;
4610 /***************************
4611 * Is this the start of a type-qualifier-list?
4612 * (Can be empty.)
4613 * Params:
4614 * pt = first token; updated with past end of type-qualifier-list if true
4615 * Returns:
4616 * true if start of type-qualifier-list
4618 private bool isTypeQualifierList(ref Token* pt)
4620 auto t = pt;
4621 while (1)
4623 switch (t.value)
4625 case TOK.const_:
4626 case TOK.restrict:
4627 case TOK.volatile:
4628 case TOK._Atomic:
4629 case TOK.__stdcall:
4630 t = peek(t);
4631 continue;
4633 default:
4634 break;
4636 break;
4638 pt = t;
4639 return true;
4642 /***************************
4643 * Is this the start of a type-name?
4644 * Params:
4645 * pt = first token; updated with past end of type-name if true
4646 * Returns:
4647 * true if start of type-name
4649 private bool isTypeName(ref Token* pt)
4651 auto t = pt;
4652 //printf("isTypeName() %s\n", t.toChars());
4653 if (!isSpecifierQualifierList(t))
4654 return false;
4655 if (!isCDeclarator(t, DTR.xabstract))
4656 return false;
4657 if (t.value != TOK.rightParenthesis)
4658 return false;
4659 pt = t;
4660 return true;
4663 /***************************
4664 * Is this the start of a specifier-qualifier-list?
4665 * Params:
4666 * pt = first token; updated with past end of specifier-qualifier-list if true
4667 * Returns:
4668 * true if start of specifier-qualifier-list
4670 private bool isSpecifierQualifierList(ref Token* pt)
4672 auto t = pt;
4673 bool result;
4674 while (1)
4676 switch (t.value)
4678 // Type Qualifiers
4679 case TOK.const_:
4680 case TOK.restrict:
4681 case TOK.volatile:
4682 case TOK.__stdcall:
4684 // Type Specifiers
4685 case TOK.char_:
4686 case TOK.signed:
4687 case TOK.unsigned:
4688 case TOK.int16:
4689 case TOK.int32:
4690 case TOK.int64:
4691 case TOK.__int128:
4692 case TOK.float32:
4693 case TOK.float64:
4694 case TOK.void_:
4695 case TOK._Bool:
4696 //case TOK._Imaginary: // ? missing in Spec
4697 case TOK._Complex:
4698 t = peek(t);
4699 break;
4701 case TOK.identifier:
4702 // Use typedef table to disambiguate
4703 if (isTypedef(t.ident))
4705 t = peek(t);
4706 break;
4708 else
4710 return false;
4713 // struct-or-union-specifier
4714 // enum-specifier
4715 case TOK.struct_:
4716 case TOK.union_:
4717 case TOK.enum_:
4718 t = peek(t);
4719 if (t.value == TOK.identifier)
4721 t = peek(t);
4722 if (t.value == TOK.leftCurly)
4724 if (!skipBraces(t))
4725 return false;
4728 else if (t.value == TOK.leftCurly)
4730 if (!skipBraces(t))
4731 return false;
4733 else
4734 return false;
4735 break;
4737 // atomic-type-specifier
4738 case TOK._Atomic:
4739 case TOK.typeof_:
4740 case TOK.__attribute__:
4741 t = peek(t);
4742 if (t.value != TOK.leftParenthesis ||
4743 !skipParens(t, &t))
4744 return false;
4745 break;
4747 default:
4748 if (result)
4749 pt = t;
4750 return result;
4752 result = true;
4756 /************************************
4757 * Looking at the leading left parenthesis, and determine if it is
4758 * either of the following:
4759 * ( type-name ) cast-expression
4760 * ( type-name ) { initializer-list }
4761 * as opposed to:
4762 * ( expression )
4763 * Params:
4764 * pt = starting token, updated to one past end of constant-expression if true
4765 * afterParenType = true if already seen `( type-name )`
4766 * Returns:
4767 * true if matches ( type-name ) ...
4769 private bool isCastExpression(ref Token* pt, bool afterParenType = false)
4771 enum log = false;
4772 if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
4773 auto t = pt;
4774 switch (t.value)
4776 case TOK.leftParenthesis:
4777 auto tk = peek(t); // move past left parenthesis
4778 if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
4780 if (afterParenType)
4781 goto default; // could be ( type-name ) ( unary-expression )
4782 return false;
4784 tk = peek(tk); // move past right parenthesis
4786 if (tk.value == TOK.leftCurly)
4788 // ( type-name ) { initializer-list }
4789 if (!isInitializer(tk))
4791 return false;
4793 t = tk;
4794 break;
4797 if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
4799 return false; // (type-name)() is not a cast (it might be a function call)
4802 if (!isCastExpression(tk, true))
4804 if (afterParenType) // could be ( type-name ) ( unary-expression )
4805 goto default; // where unary-expression also matched type-name
4806 return true;
4808 // ( type-name ) cast-expression
4809 t = tk;
4810 break;
4812 default:
4813 if (!afterParenType || !isUnaryExpression(t, afterParenType))
4815 return false;
4817 // if we've already seen ( type-name ), then this is a cast
4818 break;
4820 pt = t;
4821 if (log) printf("isCastExpression true\n");
4822 return true;
4825 /********************************
4826 * See if match for unary-expression.
4827 * Params:
4828 * pt = starting token, updated to one past end of constant-expression if true
4829 * afterParenType = true if already seen ( type-name ) of a cast-expression
4830 * Returns:
4831 * true if unary-expression
4833 private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
4835 auto t = pt;
4836 switch (t.value)
4838 case TOK.plusPlus:
4839 case TOK.minusMinus:
4840 t = peek(t);
4841 if (!isUnaryExpression(t, afterParenType))
4842 return false;
4843 break;
4845 case TOK.and:
4846 case TOK.mul:
4847 case TOK.min:
4848 case TOK.add:
4849 case TOK.not:
4850 case TOK.tilde:
4851 t = peek(t);
4852 if (!isCastExpression(t, afterParenType))
4853 return false;
4854 break;
4856 case TOK.sizeof_:
4857 t = peek(t);
4858 if (t.value == TOK.leftParenthesis)
4860 auto tk = peek(t);
4861 if (isTypeName(tk))
4863 if (tk.value != TOK.rightParenthesis)
4864 return false;
4865 t = peek(tk);
4866 break;
4869 if (!isUnaryExpression(t, afterParenType))
4870 return false;
4871 break;
4873 case TOK._Alignof:
4874 t = peek(t);
4875 if (t.value != TOK.leftParenthesis)
4876 return false;
4877 t = peek(t);
4878 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4879 return false;
4880 break;
4882 default:
4883 // Compound literals are handled by cast and sizeof expressions,
4884 // so be content with just seeing a primary expression.
4885 if (!isPrimaryExpression(t))
4886 return false;
4887 break;
4889 pt = t;
4890 return true;
4893 /********************************
4894 * See if match for primary-expression.
4895 * Params:
4896 * pt = starting token, updated to one past end of constant-expression if true
4897 * Returns:
4898 * true if primary-expression
4900 private bool isPrimaryExpression(ref Token* pt)
4902 auto t = pt;
4903 switch (t.value)
4905 case TOK.identifier:
4906 case TOK.charLiteral:
4907 case TOK.int32Literal:
4908 case TOK.uns32Literal:
4909 case TOK.int64Literal:
4910 case TOK.uns64Literal:
4911 case TOK.float32Literal:
4912 case TOK.float64Literal:
4913 case TOK.float80Literal:
4914 case TOK.imaginary32Literal:
4915 case TOK.imaginary64Literal:
4916 case TOK.imaginary80Literal:
4917 case TOK.string_:
4918 t = peek(t);
4919 break;
4921 case TOK.leftParenthesis:
4922 // ( expression )
4923 if (!skipParens(t, &t))
4924 return false;
4925 break;
4927 case TOK._Generic:
4928 t = peek(t);
4929 if (!skipParens(t, &t))
4930 return false;
4931 break;
4933 default:
4934 return false;
4936 pt = t;
4937 return true;
4941 /******************************************************************************/
4942 /********************************* More ***************************************/
4945 /**************
4946 * Declaration context
4948 enum LVL
4950 global = 1, /// global
4951 parameter = 2, /// function parameter (declarations for function identifier-list)
4952 prototype = 4, /// function prototype
4953 local = 8, /// local
4954 member = 0x10, /// struct member
4957 /// Types of declarator to parse
4958 enum DTR
4960 xdirect = 1, /// C11 6.7.6 direct-declarator
4961 xabstract = 2, /// C11 6.7.7 abstract-declarator
4962 xparameter = 3, /// parameter declarator may be either direct or abstract
4965 /// C11 6.7.1 Storage-class specifiers
4966 enum SCW : uint
4968 xnone = 0,
4969 xtypedef = 1,
4970 xextern = 2,
4971 xstatic = 4,
4972 x_Thread_local = 8,
4973 xauto = 0x10,
4974 xregister = 0x20,
4975 // C11 6.7.4 Function specifiers
4976 xinline = 0x40,
4977 x_Noreturn = 0x80,
4979 xnoinline = 0x100,
4982 /// C11 6.7.3 Type qualifiers
4983 enum MOD : uint
4985 xnone = 0,
4986 xconst = 1,
4987 xvolatile = 2,
4988 xrestrict = 4,
4989 x_Atomic = 8,
4990 x__stdcall = 0x10, // Windows linkage extension
4993 /**********************************
4994 * Aggregate for all the various specifiers
4996 struct Specifier
4998 bool noreturn; /// noreturn attribute
4999 bool naked; /// naked attribute
5000 bool _nothrow; /// nothrow attribute
5001 bool _pure; /// pure attribute
5002 bool dllimport; /// dllimport attribute
5003 bool dllexport; /// dllexport attribute
5004 bool _deprecated; /// deprecated attribute
5005 AST.Expression depMsg; /// deprecated message
5006 uint vector_size; /// positive power of 2 multipe of base type size
5008 SCW scw; /// storage-class specifiers
5009 MOD mod; /// type qualifiers
5010 AST.Expressions* alignExps; /// alignment
5011 structalign_t packalign; /// #pragma pack alignment value
5014 /***********************
5015 * Convert from C specifiers to D storage class
5016 * Params:
5017 * level = declaration context
5018 * specifier = specifiers, context, etc.
5019 * Returns:
5020 * corresponding D storage class
5022 StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
5024 StorageClass stc;
5025 if (specifier.scw & SCW.x_Thread_local)
5027 if (level == LVL.global)
5029 if (specifier.scw & SCW.xextern)
5030 stc = AST.STC.extern_;
5031 else if (specifier.scw & SCW.xstatic)
5032 stc = AST.STC.static_;
5034 else if (level == LVL.local)
5036 if (specifier.scw & SCW.xextern)
5037 stc = AST.STC.extern_;
5038 else if (specifier.scw & SCW.xstatic)
5039 stc = AST.STC.static_;
5041 else if (level == LVL.member)
5043 if (specifier.scw & SCW.xextern)
5044 stc = AST.STC.extern_;
5045 else if (specifier.scw & SCW.xstatic)
5046 stc = AST.STC.static_;
5049 else
5051 if (level == LVL.global)
5053 if (specifier.scw & SCW.xextern)
5054 stc = AST.STC.extern_ | AST.STC.gshared;
5055 else if (specifier.scw & SCW.xstatic)
5056 stc = AST.STC.gshared | AST.STC.static_;
5057 else
5058 stc = AST.STC.gshared;
5060 else if (level == LVL.local)
5062 if (specifier.scw & SCW.xextern)
5063 stc = AST.STC.extern_ | AST.STC.gshared;
5064 else if (specifier.scw & SCW.xstatic)
5065 stc = AST.STC.gshared;
5066 else if (specifier.scw & SCW.xregister)
5067 stc = AST.STC.register;
5069 else if (level == LVL.parameter)
5071 if (specifier.scw & SCW.xregister)
5072 stc = AST.STC.register | AST.STC.parameter;
5073 else
5074 stc = AST.STC.parameter;
5076 else if (level == LVL.member)
5078 if (specifier.scw & SCW.xextern)
5079 stc = AST.STC.extern_ | AST.STC.gshared;
5080 else if (specifier.scw & SCW.xstatic)
5081 stc = AST.STC.gshared;
5084 if (specifier._deprecated && !specifier.depMsg)
5085 stc |= AST.STC.deprecated_;
5086 return stc;
5089 /***********************
5090 * Add attributes from Specifier to function
5091 * Params:
5092 * fd = function to apply them to
5093 * specifier = specifiers
5095 void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
5097 fd.isNaked = specifier.naked;
5098 fd.dllImport = specifier.dllimport;
5099 fd.dllExport = specifier.dllexport;
5101 if (specifier.scw & SCW.xnoinline)
5102 fd.inlining = PINLINE.never;
5103 else if (specifier.scw & SCW.xinline)
5104 fd.inlining = PINLINE.always;
5107 /***********************
5108 * Add attributes from Specifier to variable
5109 * Params:
5110 * vd = function to apply them to
5111 * specifier = specifiers
5113 void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
5115 vd.dllImport = specifier.dllimport;
5116 vd.dllExport = specifier.dllexport;
5119 /***********************
5120 * Return suitable signed integer type for the given size
5121 * Params:
5122 * size = size of type
5123 * Returns:
5124 * corresponding signed D integer type
5126 private AST.Type integerTypeForSize(ubyte size)
5128 if (size <= 1)
5129 return AST.Type.tint8;
5130 if (size <= 2)
5131 return AST.Type.tint16;
5132 if (size <= 4)
5133 return AST.Type.tint32;
5134 if (size <= 8)
5135 return AST.Type.tint64;
5136 if (size == 16)
5138 error("__int128 not supported");
5139 return AST.Type.terror;
5141 error("unsupported integer type");
5142 return AST.Type.terror;
5145 /***********************
5146 * Return suitable unsigned integer type for the given size
5147 * Params:
5148 * size = size of type
5149 * Returns:
5150 * corresponding unsigned D integer type
5152 private AST.Type unsignedTypeForSize(ubyte size)
5154 if (size <= 1)
5155 return AST.Type.tuns8;
5156 if (size <= 2)
5157 return AST.Type.tuns16;
5158 if (size <= 4)
5159 return AST.Type.tuns32;
5160 if (size <= 8)
5161 return AST.Type.tuns64;
5162 if (size == 16)
5164 error("unsigned __int128 not supported");
5165 return AST.Type.terror;
5167 error("unsupported integer type");
5168 return AST.Type.terror;
5171 /***********************
5172 * Return suitable D float type for C `long double`
5173 * Params:
5174 * flags = kind of float to return (real, imaginary, complex).
5175 * Returns:
5176 * corresponding D type
5178 private AST.Type realType(RTFlags flags)
5180 if (long_doublesize == AST.Type.tfloat80.size())
5182 // On GDC and LDC, D `real` types map to C `long double`, so never
5183 // return a double type when real.sizeof == double.sizeof.
5184 final switch (flags)
5186 case RTFlags.realfloat: return AST.Type.tfloat80;
5187 case RTFlags.imaginary: return AST.Type.timaginary80;
5188 case RTFlags.complex: return AST.Type.tcomplex80;
5191 else
5193 final switch (flags)
5195 case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
5196 case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
5197 case RTFlags.complex: return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
5202 /**************
5203 * Flags for realType
5205 private enum RTFlags
5207 realfloat,
5208 imaginary,
5209 complex,
5212 /********************
5213 * C11 6.4.2.2 Create declaration to predefine __func__
5214 * `static const char __func__[] = " function-name ";`
5215 * Params:
5216 * loc = location for this declaration
5217 * id = identifier of function
5218 * Returns:
5219 * statement representing the declaration of __func__
5221 private AST.Statement createFuncName(Loc loc, Identifier id)
5223 const fn = id.toString(); // function-name
5224 auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
5225 auto ifn = new AST.ExpInitializer(loc, efn);
5226 auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
5227 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
5228 efn.type = tfn.immutableOf();
5229 efn.committed = true;
5230 auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
5231 auto e = new AST.DeclarationExp(loc, sfn);
5232 return new AST.ExpStatement(loc, e);
5235 /************************
5236 * After encountering an error, scan forward until a right brace or ; is found
5237 * or the end of the file.
5239 void panic()
5241 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
5242 nextToken();
5245 /**************************
5246 * Apply `const` to a type.
5247 * Params:
5248 * t = type to add const to
5249 * Returns:
5250 * resulting type
5252 private AST.Type toConst(AST.Type t)
5254 // `const` is always applied to the return type, not the
5255 // type function itself.
5256 if (auto tf = t.isTypeFunction())
5257 tf.next = tf.next.addSTC(STC.const_);
5258 else if (auto tt = t.isTypeTag())
5259 tt.mod |= MODFlags.const_;
5260 else
5262 /* Ignore const if the result would be const pointer to mutable
5264 auto tn = t.nextOf();
5265 if (!tn || tn.isConst())
5266 t = t.addSTC(STC.const_);
5268 return t;
5271 /***************************
5272 * Apply specifier to a Dsymbol.
5273 * Params:
5274 * s = Dsymbol
5275 * specifier = specifiers to apply
5276 * Returns:
5277 * Dsymbol with specifiers applied
5279 private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
5281 //printf("applySpecifier() %s\n", s.toChars());
5282 if (specifier._deprecated)
5284 if (specifier.depMsg)
5286 // Wrap declaration in a DeprecatedDeclaration
5287 auto decls = new AST.Dsymbols(1);
5288 (*decls)[0] = s;
5289 s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
5293 if (specifier.alignExps)
5295 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5296 // Wrap declaration in an AlignDeclaration
5297 auto decls = new AST.Dsymbols(1);
5298 (*decls)[0] = s;
5299 s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
5301 else if (!specifier.packalign.isDefault() && !specifier.packalign.isUnknown())
5303 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5304 // Wrap #pragma pack in an AlignDeclaration
5305 auto decls = new AST.Dsymbols(1);
5306 (*decls)[0] = s;
5307 s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
5309 return s;
5314 /******************************************************************************/
5315 /************************** typedefTab symbol table ***************************/
5318 /********************************
5319 * Determines if type t is a function type.
5320 * Params:
5321 * t = type to test
5322 * Returns:
5323 * true if it represents a function
5325 bool isFunctionTypedef(AST.Type t)
5327 //printf("isFunctionTypedef() %s\n", t.toChars());
5328 if (t.isTypeFunction())
5329 return true;
5330 if (auto tid = t.isTypeIdentifier())
5332 auto pt = lookupTypedef(tid.ident);
5333 if (pt && *pt)
5335 return (*pt).isTypeFunction() !is null;
5338 return false;
5341 /********************************
5342 * Determine if `id` is a symbol for a Typedef.
5343 * Params:
5344 * id = possible typedef
5345 * Returns:
5346 * true if id is a Type
5348 bool isTypedef(Identifier id)
5350 auto pt = lookupTypedef(id);
5351 return (pt && *pt);
5354 /*******************************
5355 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5356 * Params: id = identifier for non-typedef symbol
5358 void insertIdToTypedefTab(Identifier id)
5360 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5361 if (isTypedef(id)) // if existing typedef
5363 /* Add id as null, so we can later distinguish it from a non-null typedef
5365 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5366 tab[cast(void*)id] = cast(void*)null;
5370 /*******************************
5371 * Add `id` to typedefTab[]
5372 * Params:
5373 * id = identifier for typedef symbol
5374 * t = type of the typedef symbol
5376 void insertTypedefToTypedefTab(Identifier id, AST.Type t)
5378 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5379 if (auto tid = t.isTypeIdentifier())
5381 // Try to resolve the TypeIdentifier to its type
5382 auto pt = lookupTypedef(tid.ident);
5383 if (pt && *pt)
5384 t = *pt;
5386 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5387 tab[cast(void*)id] = cast(void*)t;
5388 typedefTab[$ - 1] = cast(void*)tab;
5391 /*********************************
5392 * Lookup id in typedefTab[].
5393 * Returns:
5394 * if not found, then null.
5395 * if found, then Type*. Deferencing it will yield null if it is not
5396 * a typedef, and a type if it is a typedef.
5398 AST.Type* lookupTypedef(Identifier id)
5400 foreach_reverse (tab; typedefTab[])
5402 if (auto pt = cast(void*)id in cast(void*[void*])tab)
5404 return cast(AST.Type*)pt;
5407 return null; // not found
5412 /******************************************************************************/
5413 /********************************* Directive Parser ***************************/
5416 override bool parseSpecialTokenSequence()
5418 Token n;
5419 scan(&n);
5420 if (n.value == TOK.int32Literal)
5422 poundLine(n, true);
5423 return true;
5425 if (n.value == TOK.identifier)
5427 if (n.ident == Id.line)
5429 poundLine(n, false);
5430 return true;
5432 else if (defines && (n.ident == Id.define || n.ident == Id.undef))
5434 /* Append this line to `defines`.
5435 * Not canonicalizing it - assume it already is
5437 defines.writeByte('#');
5438 defines.writestring(n.ident.toString());
5439 skipToNextLine(defines);
5440 defines.writeByte('\n');
5441 return true;
5443 else if (n.ident == Id.__pragma)
5445 pragmaDirective(scanloc);
5446 return true;
5448 else if (n.ident == Id.ident) // #ident "string"
5450 scan(&n);
5451 if (n.value == TOK.string_ && n.ptr[0] == '"' && n.postfix == 0)
5453 /* gcc inserts string into the .comment section in the object file.
5454 * Just ignore it for now, but can support it later by writing
5455 * the string to obj_exestr()
5457 //auto comment = n.ustring;
5459 scan(&n);
5460 if (n.value == TOK.endOfFile || n.value == TOK.endOfLine)
5461 return true;
5463 error("\"string\" expected after `#ident`");
5464 return false;
5467 if (n.ident != Id.undef)
5468 error("C preprocessor directive `#%s` is not supported", n.toChars());
5469 return false;
5472 /*********************************************
5473 * VC __pragma
5474 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5475 * Scanner is on the `__pragma`
5476 * Params:
5477 * startloc = location to use for error messages
5479 private void uupragmaDirective(const ref Loc startloc)
5481 const loc = startloc;
5482 nextToken(); // move past __pragma
5483 if (token.value != TOK.leftParenthesis)
5485 error(loc, "left parenthesis expected to follow `__pragma` instead of `%s`", token.toChars());
5486 nextToken();
5487 return;
5489 nextToken();
5491 if (token.value == TOK.identifier)
5493 if (token.ident == Id.pack)
5494 pragmaPack(startloc, false);
5495 else
5497 nextToken();
5498 if (token.value == TOK.leftParenthesis)
5499 cparseParens();
5503 else if (token.value == TOK.endOfFile)
5506 else if (token.value == TOK.rightParenthesis)
5509 else
5510 error(loc, "unrecognized `__pragma(%s)`", token.toChars());
5512 if (token.value != TOK.rightParenthesis)
5514 error(loc, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token.toChars());
5515 return;
5517 nextToken();
5520 /*********************************************
5521 * C11 6.10.6 Pragma directive
5522 * # pragma pp-tokens(opt) new-line
5523 * The C preprocessor sometimes leaves pragma directives in
5524 * the preprocessed output. Ignore them.
5525 * Upon return, p is at start of next line.
5527 private void pragmaDirective(const ref Loc loc)
5529 Token n;
5530 scan(&n);
5531 if (n.value == TOK.identifier && n.ident == Id.pack)
5532 return pragmaPack(loc, true);
5533 if (n.value != TOK.endOfLine)
5534 skipToNextLine();
5537 /*********
5538 * # pragma pack
5539 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5540 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5541 * Scanner is on the `pack`
5542 * Params:
5543 * startloc = location to use for error messages
5544 * useScan = use scan() to retrieve next token, instead of nextToken()
5546 private void pragmaPack(const ref Loc startloc, bool useScan)
5548 const loc = startloc;
5550 /* Pull tokens from scan() or nextToken()
5552 void scan(Token* t)
5554 if (useScan)
5556 Lexer.scan(t);
5558 else
5560 nextToken();
5561 *t = token;
5565 Token n;
5566 scan(&n);
5567 if (n.value != TOK.leftParenthesis)
5569 error(loc, "left parenthesis expected to follow `#pragma pack`");
5570 if (n.value != TOK.endOfLine)
5571 skipToNextLine();
5572 return;
5575 void closingParen()
5577 if (n.value != TOK.rightParenthesis)
5579 error(loc, "right parenthesis expected to close `#pragma pack(`");
5581 if (n.value != TOK.endOfLine)
5582 skipToNextLine();
5585 void setPackAlign(ref const Token t)
5587 const n = t.unsvalue;
5588 if (n < 1 || n & (n - 1) || ushort.max < n)
5589 error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
5590 packalign.set(cast(uint)n);
5591 packalign.setPack(true);
5594 scan(&n);
5596 if (!records)
5598 records = new Array!Identifier;
5599 packs = new Array!structalign_t;
5602 /* # pragma pack ( show )
5604 if (n.value == TOK.identifier && n.ident == Id.show)
5606 if (packalign.isDefault())
5607 eSink.warning(startloc, "current pack attribute is default");
5608 else
5609 eSink.warning(startloc, "current pack attribute is %d", packalign.get());
5610 scan(&n);
5611 return closingParen();
5613 /* # pragma pack ( push )
5614 * # pragma pack ( push , identifier )
5615 * # pragma pack ( push , integer )
5616 * # pragma pack ( push , identifier , integer )
5618 if (n.value == TOK.identifier && n.ident == Id.push)
5620 scan(&n);
5621 Identifier record = null;
5622 if (n.value == TOK.comma)
5624 scan(&n);
5625 if (n.value == TOK.identifier)
5627 record = n.ident;
5628 scan(&n);
5629 if (n.value == TOK.comma)
5631 scan(&n);
5632 if (n.value == TOK.int32Literal)
5634 setPackAlign(n);
5635 scan(&n);
5637 else
5638 error(loc, "alignment value expected, not `%s`", n.toChars());
5641 else if (n.value == TOK.int32Literal)
5643 setPackAlign(n);
5644 scan(&n);
5646 else
5647 error(loc, "alignment value expected, not `%s`", n.toChars());
5649 this.records.push(record);
5650 this.packs.push(packalign);
5651 return closingParen();
5653 /* # pragma pack ( pop )
5654 * # pragma pack ( pop PopList )
5655 * PopList :
5656 * , IdentifierOrInteger
5657 * , IdentifierOrInteger PopList
5658 * IdentifierOrInteger:
5659 * identifier
5660 * integer
5662 if (n.value == TOK.identifier && n.ident == Id.pop)
5664 scan(&n);
5665 size_t len = this.records.length;
5666 if (n.value == TOK.rightParenthesis) // #pragma pack ( pop )
5668 if (len == 0) // nothing to pop
5669 return closingParen();
5671 this.records.setDim(len - 1);
5672 this.packs.setDim(len - 1);
5673 if (len == 1) // stack is now empty
5674 packalign.setDefault();
5675 else
5676 packalign = (*this.packs)[len - 1];
5677 return closingParen();
5679 while (n.value == TOK.comma) // #pragma pack ( pop ,
5681 scan(&n);
5682 if (n.value == TOK.identifier)
5684 /* pragma pack(pop, identifier
5685 * Pop until identifier is found, pop that one too, and set
5686 * alignment to the new top of the stack.
5687 * If identifier is not found, do nothing.
5689 for ( ; len; --len)
5691 if ((*this.records)[len - 1] == n.ident)
5693 this.records.setDim(len - 1);
5694 this.packs.setDim(len - 1);
5695 if (len > 1)
5696 packalign = (*this.packs)[len - 2];
5697 else
5698 packalign.setDefault(); // stack empty, use default
5699 break;
5702 scan(&n);
5704 else if (n.value == TOK.int32Literal)
5706 setPackAlign(n);
5707 scan(&n);
5709 else
5711 error(loc, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n.toChars());
5712 scan(&n);
5715 return closingParen();
5717 /* # pragma pack ( integer )
5718 * Sets alignment to integer
5720 if (n.value == TOK.int32Literal)
5722 setPackAlign(n);
5723 scan(&n);
5724 return closingParen();
5726 /* # pragma pack ( )
5727 * Sets alignment to default
5729 if (n.value == TOK.rightParenthesis)
5731 packalign.setDefault();
5732 return closingParen();
5735 error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
5736 if (n.value != TOK.endOfLine)
5737 skipToNextLine();
5742 /******************************************************************************/
5743 /********************************* #define Parser *****************************/
5747 * Go through the #define's in the defines buffer and see what we can convert
5748 * to Dsymbols, which are then appended to symbols[]
5750 void addDefines()
5752 if (!defines || defines.length < 10) // minimum length of a #define line
5753 return;
5754 OutBuffer* buf = defines;
5755 defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5756 // from appending to slice[]
5757 const length = buf.length;
5758 buf.writeByte(0);
5759 auto slice = buf.peekChars()[0 .. length];
5760 resetDefineLines(slice); // reset lexer
5762 const(char)* endp = &slice[length - 7];
5764 size_t[void*] defineTab; // hash table of #define's turned into Symbol's
5765 // indexed by Identifier, returns index into symbols[]
5766 // The memory for this is leaked
5768 void addVar(AST.VarDeclaration v)
5770 //printf("addVar() %s\n", v.toChars());
5771 v.isCmacro(true); // mark it as coming from a C #define
5772 /* If it's already defined, replace the earlier
5773 * definition
5775 if (size_t* pd = cast(void*)v.ident in defineTab)
5777 //printf("replacing %s\n", v.toChars());
5778 (*symbols)[*pd] = v;
5779 return;
5781 defineTab[cast(void*)v.ident] = symbols.length;
5782 symbols.push(v);
5785 Token n;
5787 while (p < endp)
5789 if (p[0 .. 7] == "#define")
5791 p += 7;
5792 scan(&n);
5793 //printf("%s\n", n.toChars());
5794 if (n.value == TOK.identifier)
5796 auto id = n.ident;
5797 scan(&n);
5799 AST.Type t;
5801 switch (n.value)
5803 case TOK.endOfLine: // #define identifier
5804 nextDefineLine();
5805 continue;
5807 case TOK.int32Literal:
5808 case TOK.charLiteral: t = AST.Type.tint32; goto Linteger;
5809 case TOK.uns32Literal: t = AST.Type.tuns32; goto Linteger;
5810 case TOK.int64Literal: t = AST.Type.tint64; goto Linteger;
5811 case TOK.uns64Literal: t = AST.Type.tuns64; goto Linteger;
5813 Linteger:
5814 const intvalue = n.intvalue;
5815 scan(&n);
5816 if (n.value == TOK.endOfLine)
5818 /* Declare manifest constant:
5819 * enum id = intvalue;
5821 AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
5822 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5823 addVar(v);
5824 nextDefineLine();
5825 continue;
5827 break;
5829 case TOK.float32Literal: t = AST.Type.tfloat32; goto Lfloat;
5830 case TOK.float64Literal: t = AST.Type.tfloat64; goto Lfloat;
5831 case TOK.float80Literal: t = AST.Type.tfloat80; goto Lfloat;
5832 case TOK.imaginary32Literal: t = AST.Type.timaginary32; goto Lfloat;
5833 case TOK.imaginary64Literal: t = AST.Type.timaginary64; goto Lfloat;
5834 case TOK.imaginary80Literal: t = AST.Type.timaginary80; goto Lfloat;
5836 Lfloat:
5837 const floatvalue = n.floatvalue;
5838 scan(&n);
5839 if (n.value == TOK.endOfLine)
5841 /* Declare manifest constant:
5842 * enum id = floatvalue;
5844 AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
5845 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5846 addVar(v);
5847 nextDefineLine();
5848 continue;
5850 break;
5852 case TOK.string_:
5853 const str = n.ustring;
5854 const len = n.len;
5855 const postfix = n.postfix;
5856 scan(&n);
5857 if (n.value == TOK.endOfLine)
5859 /* Declare manifest constant:
5860 * enum id = "string";
5862 AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
5863 auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5864 addVar(v);
5865 nextDefineLine();
5866 continue;
5868 break;
5870 default:
5871 break;
5874 skipToNextLine();
5876 else
5878 scan(&n);
5879 if (n.value != TOK.endOfLine)
5881 skipToNextLine();
5884 nextDefineLine();
5887 defines = buf;