d: Merge upstream dmd, druntime f1a045928e
[official-gcc.git] / gcc / d / dmd / cparse.d
blob4c0b96a4c8cfc1a6b9d99100bf3548f07cc9fceb
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.wcharLiteral:
251 case TOK.dcharLiteral:
252 case TOK.int32Literal:
253 case TOK.uns32Literal:
254 case TOK.int64Literal:
255 case TOK.uns64Literal:
256 case TOK.int128Literal:
257 case TOK.uns128Literal:
258 case TOK.float32Literal:
259 case TOK.float64Literal:
260 case TOK.float80Literal:
261 case TOK.imaginary32Literal:
262 case TOK.imaginary64Literal:
263 case TOK.imaginary80Literal:
264 case TOK.leftParenthesis:
265 case TOK.and:
266 case TOK.mul:
267 case TOK.min:
268 case TOK.add:
269 case TOK.tilde:
270 case TOK.not:
271 case TOK.plusPlus:
272 case TOK.minusMinus:
273 case TOK.sizeof_:
274 case TOK._Generic:
275 case TOK._assert:
276 Lexp:
277 auto exp = cparseExpression();
278 if (token.value == TOK.identifier && exp.op == EXP.identifier)
280 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());
281 nextToken();
283 else
284 check(TOK.semicolon, "statement");
285 s = new AST.ExpStatement(loc, exp);
286 break;
288 // type-specifiers
289 case TOK.void_:
290 case TOK.char_:
291 case TOK.int16:
292 case TOK.int32:
293 case TOK.int64:
294 case TOK.__int128:
295 case TOK.float32:
296 case TOK.float64:
297 case TOK.signed:
298 case TOK.unsigned:
299 case TOK._Bool:
300 //case TOK._Imaginary:
301 case TOK._Complex:
302 case TOK.struct_:
303 case TOK.union_:
304 case TOK.enum_:
305 case TOK.typeof_:
307 // storage-class-specifiers
308 case TOK.typedef_:
309 case TOK.extern_:
310 case TOK.static_:
311 case TOK._Thread_local:
312 case TOK.__thread:
313 case TOK.auto_:
314 case TOK.register:
316 // function-specifiers
317 case TOK.inline:
318 case TOK._Noreturn:
320 // type-qualifiers
321 case TOK.const_:
322 case TOK.volatile:
323 case TOK.restrict:
324 case TOK.__stdcall:
326 // alignment-specifier
327 case TOK._Alignas:
329 // atomic-type-specifier or type_qualifier
330 case TOK._Atomic:
332 case TOK.__attribute__:
333 case TOK.__declspec:
335 Ldeclaration:
337 cparseDeclaration(LVL.local);
338 if (symbols.length > 1)
340 auto as = new AST.Statements();
341 as.reserve(symbols.length);
342 foreach (d; (*symbols)[])
344 s = new AST.ExpStatement(loc, d);
345 as.push(s);
347 s = new AST.CompoundDeclarationStatement(loc, as);
348 symbols.setDim(0);
350 else if (symbols.length == 1)
352 auto d = (*symbols)[0];
353 s = new AST.ExpStatement(loc, d);
354 symbols.setDim(0);
356 else
357 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
358 if (flags & ParseStatementFlags.scope_)
359 s = new AST.ScopeStatement(loc, s, token.loc);
360 break;
363 case TOK._Static_assert: // _Static_assert ( constant-expression, string-literal ) ;
364 s = new AST.StaticAssertStatement(cparseStaticAssert());
365 break;
367 case TOK.leftCurly:
369 /* C11 6.8.2
370 * compound-statement:
371 * { block-item-list (opt) }
373 * block-item-list:
374 * block-item
375 * block-item-list block-item
377 * block-item:
378 * declaration
379 * statement
381 nextToken();
382 auto statements = new AST.Statements();
383 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
385 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
387 if (endPtr)
388 *endPtr = token.ptr;
389 endloc = token.loc;
390 if (pEndloc)
392 *pEndloc = token.loc;
393 pEndloc = null; // don't set it again
395 s = new AST.CompoundStatement(loc, statements);
396 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
397 s = new AST.ScopeStatement(loc, s, token.loc);
398 check(TOK.rightCurly, "compound statement");
399 break;
402 case TOK.while_:
404 nextToken();
405 check(TOK.leftParenthesis);
406 auto condition = cparseExpression();
407 check(TOK.rightParenthesis);
408 Loc endloc;
409 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
410 s = new AST.WhileStatement(loc, condition, _body, endloc, null);
411 break;
414 case TOK.semicolon:
415 /* C11 6.8.3 null statement
417 nextToken();
418 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
419 break;
421 case TOK.do_:
423 nextToken();
424 auto _body = cparseStatement(ParseStatementFlags.scope_);
425 check(TOK.while_);
426 check(TOK.leftParenthesis);
427 auto condition = cparseExpression();
428 check(TOK.rightParenthesis);
429 check(TOK.semicolon, "terminating `;` required after do-while statement");
430 s = new AST.DoStatement(loc, _body, condition, token.loc);
431 break;
434 case TOK.for_:
436 AST.Statement _init;
437 AST.Expression condition;
438 AST.Expression increment;
440 nextToken();
441 check(TOK.leftParenthesis);
442 if (token.value == TOK.semicolon)
444 _init = null;
445 nextToken();
447 else
449 _init = cparseStatement(0);
451 if (token.value == TOK.semicolon)
453 condition = null;
454 nextToken();
456 else
458 condition = cparseExpression();
459 check(TOK.semicolon, "`for` condition");
461 if (token.value == TOK.rightParenthesis)
463 increment = null;
464 nextToken();
466 else
468 increment = cparseExpression();
469 check(TOK.rightParenthesis);
471 Loc endloc;
472 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
473 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
474 break;
477 case TOK.if_:
479 nextToken();
480 check(TOK.leftParenthesis);
481 auto condition = cparseExpression();
482 check(TOK.rightParenthesis);
483 auto ifbody = cparseStatement(ParseStatementFlags.scope_);
484 AST.Statement elsebody;
485 if (token.value == TOK.else_)
487 nextToken();
488 elsebody = cparseStatement(ParseStatementFlags.scope_);
490 else
491 elsebody = null;
492 if (condition && ifbody)
493 s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc);
494 else
495 s = null; // don't propagate parsing errors
496 break;
499 case TOK.else_:
500 error("found `else` without a corresponding `if` statement");
501 goto Lerror;
503 case TOK.switch_:
505 nextToken();
506 check(TOK.leftParenthesis);
507 auto condition = cparseExpression();
508 check(TOK.rightParenthesis);
509 auto _body = cparseStatement(ParseStatementFlags.scope_);
510 s = new AST.SwitchStatement(loc, null, condition, _body, false, token.loc);
511 break;
514 case TOK.case_:
517 nextToken();
518 auto exp = cparseAssignExp();
519 AST.Expression expHigh;
520 if (token.value == TOK.dotDotDot)
522 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
524 nextToken();
525 expHigh = cparseAssignExp();
527 check(TOK.colon);
529 if (flags & ParseStatementFlags.curlyScope)
531 auto statements = new AST.Statements();
532 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
534 auto cur = cparseStatement(ParseStatementFlags.curlyScope);
535 statements.push(cur);
537 // https://issues.dlang.org/show_bug.cgi?id=21739
538 // Stop at the last break s.t. the following non-case statements are
539 // not merged into the current case. This can happen for
540 // case 1: ... break;
541 // debug { case 2: ... }
542 if (cur && cur.isBreakStatement())
543 break;
545 s = new AST.CompoundStatement(loc, statements);
547 else
549 s = cparseStatement(0);
551 s = new AST.ScopeStatement(loc, s, token.loc);
552 if (expHigh)
553 s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
554 else
555 s = new AST.CaseStatement(loc, exp, s);
556 break;
559 case TOK.default_:
561 nextToken();
562 check(TOK.colon);
564 if (flags & ParseStatementFlags.curlyScope)
566 auto statements = new AST.Statements();
567 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
569 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
571 s = new AST.CompoundStatement(loc, statements);
573 else
574 s = cparseStatement(0);
575 s = new AST.ScopeStatement(loc, s, token.loc);
576 s = new AST.DefaultStatement(loc, s);
577 break;
580 case TOK.return_:
582 /* return ;
583 * return expression ;
585 nextToken();
586 auto exp = token.value == TOK.semicolon ? null : cparseExpression();
587 check(TOK.semicolon, "`return` statement");
588 s = new AST.ReturnStatement(loc, exp);
589 break;
592 case TOK.break_:
593 nextToken();
594 check(TOK.semicolon, "`break` statement");
595 s = new AST.BreakStatement(loc, null);
596 break;
598 case TOK.continue_:
599 nextToken();
600 check(TOK.semicolon, "`continue` statement");
601 s = new AST.ContinueStatement(loc, null);
602 break;
604 case TOK.goto_:
606 Identifier ident;
607 nextToken();
608 if (token.value != TOK.identifier)
610 error("identifier expected following `goto`");
611 ident = null;
613 else
615 ident = token.ident;
616 nextToken();
618 s = new AST.GotoStatement(loc, ident);
619 check(TOK.semicolon, "`goto` statement");
620 break;
623 case TOK.asm_:
624 switch (peekNext())
626 case TOK.goto_:
627 case TOK.inline:
628 case TOK.volatile:
629 case TOK.leftParenthesis:
630 s = cparseGnuAsm();
631 break;
633 default:
634 // ImportC extensions: parse as a D asm block.
635 s = parseAsm(compileEnv.masm);
636 break;
638 break;
640 default:
641 error("found `%s` instead of statement", token.toChars());
642 goto Lerror;
644 Lerror:
645 panic();
646 if (token.value == TOK.semicolon)
647 nextToken();
648 s = null;
649 break;
651 if (pEndloc)
652 *pEndloc = prevloc;
653 symbols = symbolsSave;
654 typedefTab.setDim(typedefTabLengthSave);
655 return s;
659 /*******************************************************************************/
660 /********************************* Expression Parser ***************************/
663 /**************
664 * C11 6.5.17
665 * expression:
666 * assignment-expression
667 * expression , assignment-expression
669 AST.Expression cparseExpression()
671 auto loc = token.loc;
673 //printf("cparseExpression() loc = %d\n", loc.linnum);
674 auto e = cparseAssignExp();
675 while (token.value == TOK.comma)
677 nextToken();
678 auto e2 = cparseAssignExp();
679 e = new AST.CommaExp(loc, e, e2, false);
680 loc = token.loc;
682 return e;
686 /*********************
687 * C11 6.5.1
688 * primary-expression:
689 * identifier
690 * constant
691 * string-literal
692 * ( expression )
693 * generic-selection
694 * __builtin_va_arg(assign_expression, type)
696 AST.Expression cparsePrimaryExp()
698 AST.Expression e;
699 const loc = token.loc;
701 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
702 switch (token.value)
704 case TOK.identifier:
705 const id = token.ident.toString();
706 if (id.length > 2 && id[0] == '_' && id[1] == '_') // leading double underscore
708 if (token.ident is Id.__func__)
710 addFuncName = true; // implicitly declare __func__
712 else if (token.ident is Id.builtin_va_arg)
714 e = cparseBuiltin_va_arg();
715 break;
717 else
718 importBuiltins = true; // probably one of those compiler extensions
720 e = new AST.IdentifierExp(loc, token.ident);
721 nextToken();
722 break;
724 case TOK.charLiteral:
725 case TOK.int32Literal:
726 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
727 nextToken();
728 break;
730 case TOK.wcharLiteral:
731 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tuns16);
732 nextToken();
733 break;
735 case TOK.dcharLiteral:
736 case TOK.uns32Literal:
737 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
738 nextToken();
739 break;
741 case TOK.int64Literal:
742 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
743 nextToken();
744 break;
746 case TOK.uns64Literal:
747 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
748 nextToken();
749 break;
751 case TOK.float32Literal:
752 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
753 nextToken();
754 break;
756 case TOK.float64Literal:
757 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
758 nextToken();
759 break;
761 case TOK.float80Literal:
762 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
763 nextToken();
764 break;
766 case TOK.imaginary32Literal:
767 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
768 nextToken();
769 break;
771 case TOK.imaginary64Literal:
772 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
773 nextToken();
774 break;
776 case TOK.imaginary80Literal:
777 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
778 nextToken();
779 break;
781 case TOK.string_:
783 // cat adjacent strings
784 auto s = token.ustring;
785 auto len = token.len;
786 auto postfix = token.postfix;
787 while (1)
789 nextToken();
790 if (token.value == TOK.string_)
792 if (token.postfix)
794 if (token.postfix != postfix)
795 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
796 postfix = token.postfix;
799 const len1 = len;
800 const len2 = token.len;
801 len = len1 + len2;
802 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
803 memcpy(s2, s, len1 * char.sizeof);
804 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
805 s = s2;
807 else
808 break;
810 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
811 break;
814 case TOK.leftParenthesis:
815 nextToken();
816 if (token.value == TOK.leftCurly)
817 e = cparseStatementExpression(); // gcc extension
818 else
819 e = cparseExpression();
820 check(TOK.rightParenthesis);
821 break;
823 case TOK._Generic:
824 e = cparseGenericSelection();
825 break;
827 case TOK._assert: // __check(assign-exp) extension
828 nextToken();
829 check(TOK.leftParenthesis, "`__check`");
830 e = parseAssignExp();
831 check(TOK.rightParenthesis);
832 e = new AST.AssertExp(loc, e, null);
833 break;
835 default:
836 error("expression expected, not `%s`", token.toChars());
837 // Anything for e, as long as it's not NULL
838 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
839 nextToken();
840 break;
842 return e;
845 /*********************************
846 * C11 6.5.2
847 * postfix-expression:
848 * primary-expression
849 * postfix-expression [ expression ]
850 * postfix-expression ( argument-expression-list (opt) )
851 * postfix-expression . identifier
852 * postfix-expression -> identifier
853 * postfix-expression ++
854 * postfix-expression --
855 * ( type-name ) { initializer-list }
856 * ( type-name ) { initializer-list , }
858 * argument-expression-list:
859 * assignment-expression
860 * argument-expression-list , assignment-expression
862 private AST.Expression cparsePostfixExp(AST.Expression e)
864 e = cparsePrimaryExp();
865 return cparsePostfixOperators(e);
868 /********************************
869 * C11 6.5.2
870 * Parse a series of operators for a postfix expression after already parsing
871 * a primary-expression or compound literal expression.
872 * Params:
873 * e = parsed primary or compound literal expression
874 * Returns:
875 * parsed postfix expression
877 private AST.Expression cparsePostfixOperators(AST.Expression e)
879 while (1)
881 const loc = token.loc;
882 switch (token.value)
884 case TOK.dot:
885 nextToken();
886 if (token.value == TOK.identifier)
888 Identifier id = token.ident;
889 e = new AST.DotIdExp(loc, e, id);
890 break;
892 error("identifier expected following `.`, not `%s`", token.toChars());
893 break;
895 case TOK.arrow:
896 nextToken();
897 if (token.value == TOK.identifier)
899 Identifier id = token.ident;
900 auto die = new AST.DotIdExp(loc, e, id);
901 die.arrow = true;
902 e = die;
903 break;
905 error("identifier expected following `->`, not `%s`", token.toChars());
906 break;
908 case TOK.plusPlus:
909 e = new AST.PostExp(EXP.plusPlus, loc, e);
910 break;
912 case TOK.minusMinus:
913 e = new AST.PostExp(EXP.minusMinus, loc, e);
914 break;
916 case TOK.leftParenthesis:
917 e = new AST.CallExp(loc, e, cparseArguments());
918 continue;
920 case TOK.leftBracket:
922 // array dereferences:
923 // array[index]
924 AST.Expression index;
925 auto arguments = new AST.Expressions();
927 inBrackets++;
928 nextToken();
929 index = cparseAssignExp();
930 arguments.push(index);
931 check(TOK.rightBracket);
932 inBrackets--;
933 e = new AST.ArrayExp(loc, e, arguments);
934 continue;
936 default:
937 return e;
939 nextToken();
943 /************************
944 * C11 6.5.3
945 * unary-expression:
946 * postfix-expression
947 * ++ unary-expression
948 * -- unary-expression
949 * unary-operator cast-expression
950 * sizeof unary-expression
951 * sizeof ( type-name )
952 * _Alignof ( type-name )
954 * unary-operator:
955 * & * + - ~ !
957 private AST.Expression cparseUnaryExp()
959 AST.Expression e;
960 const loc = token.loc;
962 switch (token.value)
964 case TOK.plusPlus:
965 nextToken();
966 // Parse `++` as an unary operator so that cast expressions only give
967 // an error for being non-lvalues.
968 e = cparseCastExp();
969 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
970 break;
972 case TOK.minusMinus:
973 nextToken();
974 // Parse `--` as an unary operator, same as prefix increment.
975 e = cparseCastExp();
976 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
977 break;
979 case TOK.and:
980 nextToken();
981 e = cparseCastExp();
982 e = new AST.AddrExp(loc, e);
983 break;
985 case TOK.mul:
986 nextToken();
987 e = cparseCastExp();
988 e = new AST.PtrExp(loc, e);
989 break;
991 case TOK.min:
992 nextToken();
993 e = cparseCastExp();
994 e = new AST.NegExp(loc, e);
995 break;
997 case TOK.add:
998 nextToken();
999 e = cparseCastExp();
1000 e = new AST.UAddExp(loc, e);
1001 break;
1003 case TOK.not:
1004 nextToken();
1005 e = cparseCastExp();
1006 e = new AST.NotExp(loc, e);
1007 break;
1009 case TOK.tilde:
1010 nextToken();
1011 e = cparseCastExp();
1012 e = new AST.ComExp(loc, e);
1013 break;
1015 case TOK.sizeof_:
1017 nextToken();
1018 if (token.value == TOK.leftParenthesis)
1020 auto tk = peek(&token);
1021 if (isTypeName(tk))
1023 /* Expression may be either be requesting the sizeof a type-name
1024 * or a compound literal, which requires checking whether
1025 * the next token is leftCurly
1027 nextToken();
1028 auto t = cparseTypeName();
1029 check(TOK.rightParenthesis);
1030 if (token.value == TOK.leftCurly)
1032 // ( type-name ) { initializer-list }
1033 auto ci = cparseInitializer();
1034 e = new AST.CompoundLiteralExp(loc, t, ci);
1035 e = cparsePostfixOperators(e);
1037 else
1039 // ( type-name )
1040 e = new AST.TypeExp(loc, t);
1043 else
1045 // must be an expression
1046 e = cparseUnaryExp();
1049 else
1051 //C11 6.5.3
1052 e = cparseUnaryExp();
1055 e = new AST.DotIdExp(loc, e, Id.__sizeof);
1056 break;
1059 case TOK._Alignof:
1061 nextToken();
1062 check(TOK.leftParenthesis);
1063 auto t = cparseTypeName();
1064 check(TOK.rightParenthesis);
1065 e = new AST.TypeExp(loc, t);
1066 e = new AST.DotIdExp(loc, e, Id.__xalignof);
1067 break;
1070 default:
1071 e = cparsePostfixExp(e);
1072 break;
1074 assert(e);
1075 return e;
1078 /**************
1079 * C11 6.5.4
1080 * cast-expression
1081 * unary-expression
1082 * ( type-name ) cast-expression
1084 private AST.Expression cparseCastExp()
1086 if (token.value == TOK.leftParenthesis)
1088 //printf("cparseCastExp()\n");
1089 auto tk = peek(&token);
1090 bool iscast;
1091 bool isexp;
1092 if (tk.value == TOK.identifier)
1094 iscast = isTypedef(tk.ident);
1095 isexp = !iscast;
1097 if (isexp)
1099 // ( identifier ) is an expression
1100 return cparseUnaryExp();
1103 // If ( type-name )
1104 auto pt = &token;
1106 if (isCastExpression(pt))
1108 // Expression may be either a cast or a compound literal, which
1109 // requires checking whether the next token is leftCurly
1110 const loc = token.loc;
1111 nextToken();
1112 auto t = cparseTypeName();
1113 check(TOK.rightParenthesis);
1114 pt = &token;
1116 if (token.value == TOK.leftCurly)
1118 // C11 6.5.2.5 ( type-name ) { initializer-list }
1119 auto ci = cparseInitializer();
1120 auto ce = new AST.CompoundLiteralExp(loc, t, ci);
1121 return cparsePostfixOperators(ce);
1124 if (iscast)
1126 // ( type-name ) cast-expression
1127 auto ce = cparseCastExp();
1128 return new AST.CastExp(loc, ce, t);
1131 if (t.isTypeIdentifier() &&
1132 isexp &&
1133 token.value == TOK.leftParenthesis &&
1134 !isCastExpression(pt))
1136 /* (t)(...)... might be a cast expression or a function call,
1137 * with different grammars: a cast would be cparseCastExp(),
1138 * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1139 * We can't know until t is known. So, parse it as a function call
1140 * and let semantic() rewrite the AST as a CastExp if it turns out
1141 * to be a type.
1143 auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
1144 ie.parens = true; // let semantic know it might be a CastExp
1145 AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
1146 return cparsePostfixOperators(e);
1149 // ( type-name ) cast-expression
1150 auto ce = cparseCastExp();
1151 return new AST.CastExp(loc, ce, t);
1154 return cparseUnaryExp();
1157 /**************
1158 * C11 6.5.5
1159 * multiplicative-expression
1160 * cast-expression
1161 * multiplicative-expression * cast-expression
1162 * multiplicative-expression / cast-expression
1163 * multiplicative-expression % cast-expression
1165 private AST.Expression cparseMulExp()
1167 const loc = token.loc;
1168 auto e = cparseCastExp();
1170 while (1)
1172 switch (token.value)
1174 case TOK.mul:
1175 nextToken();
1176 auto e2 = cparseCastExp();
1177 e = new AST.MulExp(loc, e, e2);
1178 continue;
1180 case TOK.div:
1181 nextToken();
1182 auto e2 = cparseCastExp();
1183 e = new AST.DivExp(loc, e, e2);
1184 continue;
1186 case TOK.mod:
1187 nextToken();
1188 auto e2 = cparseCastExp();
1189 e = new AST.ModExp(loc, e, e2);
1190 continue;
1192 default:
1193 break;
1195 break;
1197 return e;
1200 /**************
1201 * C11 6.5.6
1202 * additive-expression
1203 * multiplicative-expression
1204 * additive-expression + multiplicative-expression
1205 * additive-expression - multiplicative-expression
1207 private AST.Expression cparseAddExp()
1209 const loc = token.loc;
1210 auto e = cparseMulExp();
1212 while (1)
1214 switch (token.value)
1216 case TOK.add:
1217 nextToken();
1218 auto e2 = cparseMulExp();
1219 e = new AST.AddExp(loc, e, e2);
1220 continue;
1222 case TOK.min:
1223 nextToken();
1224 auto e2 = cparseMulExp();
1225 e = new AST.MinExp(loc, e, e2);
1226 continue;
1228 default:
1229 break;
1231 break;
1233 return e;
1236 /**************
1237 * C11 6.5.7
1238 * shift-expression
1239 * additive-expression
1240 * shift-expression << additive-expression
1241 * shift-expression >> additive-expression
1243 private AST.Expression cparseShiftExp()
1245 const loc = token.loc;
1246 auto e = cparseAddExp();
1248 while (1)
1250 switch (token.value)
1252 case TOK.leftShift:
1253 nextToken();
1254 auto e2 = cparseAddExp();
1255 e = new AST.ShlExp(loc, e, e2);
1256 continue;
1258 case TOK.rightShift:
1259 nextToken();
1260 auto e2 = cparseAddExp();
1261 e = new AST.ShrExp(loc, e, e2);
1262 continue;
1264 default:
1265 break;
1267 break;
1269 return e;
1272 /**************
1273 * C11 6.5.8
1274 * relational-expression
1275 * shift-expression
1276 * relational-expression < shift-expression
1277 * relational-expression > shift-expression
1278 * relational-expression <= shift-expression
1279 * relational-expression >= shift-expression
1281 private AST.Expression cparseRelationalExp()
1283 const loc = token.loc;
1285 auto e = cparseShiftExp();
1287 EXP op = EXP.reserved;
1288 switch (token.value)
1290 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
1291 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
1292 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
1293 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
1294 Lcmp:
1295 nextToken();
1296 auto e2 = cparseShiftExp();
1297 e = new AST.CmpExp(op, loc, e, e2);
1298 break;
1300 default:
1301 break;
1303 return e;
1306 /**************
1307 * C11 6.5.9
1308 * equality-expression
1309 * relational-expression
1310 * equality-expression == relational-expression
1311 * equality-expression != relational-expression
1313 private AST.Expression cparseEqualityExp()
1315 const loc = token.loc;
1317 auto e = cparseRelationalExp();
1319 EXP op = EXP.reserved;
1320 switch (token.value)
1322 case TOK.equal: op = EXP.equal; goto Lequal;
1323 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
1324 Lequal:
1325 nextToken();
1326 auto e2 = cparseRelationalExp();
1327 e = new AST.EqualExp(op, loc, e, e2);
1328 break;
1330 default:
1331 break;
1333 return e;
1336 /**************
1337 * C11 6.5.10
1338 * AND-expression
1339 * equality-expression
1340 * AND-expression & equality-expression
1342 private AST.Expression cparseAndExp()
1344 Loc loc = token.loc;
1345 auto e = cparseEqualityExp();
1346 while (token.value == TOK.and)
1348 nextToken();
1349 auto e2 = cparseEqualityExp();
1350 e = new AST.AndExp(loc, e, e2);
1351 loc = token.loc;
1353 return e;
1356 /**************
1357 * C11 6.5.11
1358 * exclusive-OR-expression
1359 * AND-expression
1360 * exclusive-OR-expression ^ AND-expression
1362 private AST.Expression cparseXorExp()
1364 const loc = token.loc;
1366 auto e = cparseAndExp();
1367 while (token.value == TOK.xor)
1369 nextToken();
1370 auto e2 = cparseAndExp();
1371 e = new AST.XorExp(loc, e, e2);
1373 return e;
1376 /**************
1377 * C11 6.5.12
1378 * inclusive-OR-expression
1379 * exclusive-OR-expression
1380 * inclusive-OR-expression | exclusive-OR-expression
1382 private AST.Expression cparseOrExp()
1384 const loc = token.loc;
1386 auto e = cparseXorExp();
1387 while (token.value == TOK.or)
1389 nextToken();
1390 auto e2 = cparseXorExp();
1391 e = new AST.OrExp(loc, e, e2);
1393 return e;
1396 /**************
1397 * C11 6.5.13
1398 * logical-AND-expression
1399 * inclusive-OR-expression
1400 * logical-AND-expression && inclusive-OR-expression
1402 private AST.Expression cparseAndAndExp()
1404 const loc = token.loc;
1406 auto e = cparseOrExp();
1407 while (token.value == TOK.andAnd)
1409 nextToken();
1410 auto e2 = cparseOrExp();
1411 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
1413 return e;
1416 /**************
1417 * C11 6.5.14
1418 * logical-OR-expression
1419 * logical-AND-expression
1420 * logical-OR-expression || logical-AND-expression
1422 private AST.Expression cparseOrOrExp()
1424 const loc = token.loc;
1426 auto e = cparseAndAndExp();
1427 while (token.value == TOK.orOr)
1429 nextToken();
1430 auto e2 = cparseAndAndExp();
1431 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
1433 return e;
1436 /**************
1437 * C11 6.5.15
1438 * conditional-expression:
1439 * logical-OR-expression
1440 * logical-OR-expression ? expression : conditional-expression
1442 private AST.Expression cparseCondExp()
1444 const loc = token.loc;
1446 auto e = cparseOrOrExp();
1447 if (token.value == TOK.question)
1449 nextToken();
1450 auto e1 = cparseExpression();
1451 check(TOK.colon);
1452 auto e2 = cparseCondExp();
1453 e = new AST.CondExp(loc, e, e1, e2);
1455 return e;
1458 /**************
1459 * C11 6.5.16
1460 * assignment-expression:
1461 * conditional-expression
1462 * unary-expression assignment-operator assignment-expression
1464 * assignment-operator:
1465 * = *= /= %= += -= <<= >>= &= ^= |=
1467 AST.Expression cparseAssignExp()
1469 AST.Expression e;
1470 e = cparseCondExp(); // constrain it to being unary-expression in semantic pass
1471 if (e is null)
1472 return e;
1474 const loc = token.loc;
1475 switch (token.value)
1477 case TOK.assign:
1478 nextToken();
1479 auto e2 = cparseAssignExp();
1480 e = new AST.AssignExp(loc, e, e2);
1481 break;
1483 case TOK.addAssign:
1484 nextToken();
1485 auto e2 = cparseAssignExp();
1486 e = new AST.AddAssignExp(loc, e, e2);
1487 break;
1489 case TOK.minAssign:
1490 nextToken();
1491 auto e2 = cparseAssignExp();
1492 e = new AST.MinAssignExp(loc, e, e2);
1493 break;
1495 case TOK.mulAssign:
1496 nextToken();
1497 auto e2 = cparseAssignExp();
1498 e = new AST.MulAssignExp(loc, e, e2);
1499 break;
1501 case TOK.divAssign:
1502 nextToken();
1503 auto e2 = cparseAssignExp();
1504 e = new AST.DivAssignExp(loc, e, e2);
1505 break;
1507 case TOK.modAssign:
1508 nextToken();
1509 auto e2 = cparseAssignExp();
1510 e = new AST.ModAssignExp(loc, e, e2);
1511 break;
1513 case TOK.andAssign:
1514 nextToken();
1515 auto e2 = cparseAssignExp();
1516 e = new AST.AndAssignExp(loc, e, e2);
1517 break;
1519 case TOK.orAssign:
1520 nextToken();
1521 auto e2 = cparseAssignExp();
1522 e = new AST.OrAssignExp(loc, e, e2);
1523 break;
1525 case TOK.xorAssign:
1526 nextToken();
1527 auto e2 = cparseAssignExp();
1528 e = new AST.XorAssignExp(loc, e, e2);
1529 break;
1531 case TOK.leftShiftAssign:
1532 nextToken();
1533 auto e2 = cparseAssignExp();
1534 e = new AST.ShlAssignExp(loc, e, e2);
1535 break;
1537 case TOK.rightShiftAssign:
1538 nextToken();
1539 auto e2 = cparseAssignExp();
1540 e = new AST.ShrAssignExp(loc, e, e2);
1541 break;
1543 default:
1544 break;
1547 return e;
1550 /***********************
1551 * C11 6.5.1.1
1552 * _Generic ( assignment-expression, generic-assoc-list )
1554 * generic-assoc-list:
1555 * generic-association
1556 * generic-assoc-list generic-association
1558 * generic-association:
1559 * type-name : assignment-expression
1560 * default : assignment-expression
1562 private AST.Expression cparseGenericSelection()
1564 const loc = token.loc;
1565 nextToken();
1566 check(TOK.leftParenthesis);
1567 auto cntlExp = cparseAssignExp();
1568 check(TOK.comma);
1569 auto types = new AST.Types();
1570 auto exps = new AST.Expressions();
1571 bool sawDefault;
1572 while (1)
1574 AST.Type t;
1575 if (token.value == TOK.default_)
1577 nextToken();
1578 if (sawDefault)
1579 error("only one `default` allowed in generic-assoc-list");
1580 sawDefault = true;
1581 t = null;
1583 else
1584 t = cparseTypeName();
1585 types.push(t);
1587 check(TOK.colon);
1588 auto e = cparseAssignExp();
1589 exps.push(e);
1590 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
1591 break;
1592 check(TOK.comma);
1594 check(TOK.rightParenthesis);
1595 return new AST.GenericExp(loc, cntlExp, types, exps);
1598 /***********************
1599 * C11 6.6 Constant expressions
1600 * constant-expression:
1601 * conditional-expression
1603 private AST.Expression cparseConstantExp()
1605 return cparseAssignExp();
1608 /*****************************
1609 * gcc extension:
1610 * type __builtin_va_arg(assign-expression, type)
1611 * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1612 * va_arg!(type)(assign-expression);
1613 * Lexer is on `__builtin_va_arg`
1615 private AST.Expression cparseBuiltin_va_arg()
1617 importBuiltins = true; // need core.stdc.stdarg
1619 nextToken();
1620 check(TOK.leftParenthesis);
1622 auto arguments = new AST.Expressions();
1623 auto arg = cparseAssignExp();
1624 arguments.push(arg);
1626 check(TOK.comma);
1628 auto t = cparseTypeName();
1629 auto tiargs = new AST.Objects();
1630 tiargs.push(t);
1632 const loc = loc;
1633 auto ti = new AST.TemplateInstance(loc, Id.va_arg, tiargs);
1634 auto tie = new AST.ScopeExp(loc, ti);
1636 AST.Expression e = new AST.CallExp(loc, tie, arguments);
1638 check(TOK.rightParenthesis);
1639 return e;
1642 /*****************************
1643 * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1644 * Represent as a function literal, then call the function literal.
1645 * Parser is on opening curly brace.
1647 private AST.Expression cparseStatementExpression()
1649 AST.ParameterList parameterList;
1650 StorageClass stc = 0;
1651 const loc = token.loc;
1652 typedefTab.push(null);
1653 auto fbody = cparseStatement(ParseStatementFlags.scope_);
1654 typedefTab.pop(); // end of function scope
1656 // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1657 auto ss = fbody.isScopeStatement();
1658 auto cs = ss.statement.isCompoundStatement();
1659 assert(cs);
1660 if (const len = (*cs.statements).length)
1662 auto s = (*cs.statements)[len - 1];
1663 if (auto es = s.isExpStatement())
1664 (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
1667 auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
1668 auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
1669 fd.fbody = fbody;
1671 auto fe = new AST.FuncExp(loc, fd);
1672 auto args = new AST.Expressions();
1673 auto e = new AST.CallExp(loc, fe, args); // call the function literal
1674 return e;
1678 /********************************************************************************/
1679 /********************************* Declaration Parser ***************************/
1682 /*************************************
1683 * C11 6.7
1684 * declaration:
1685 * declaration-specifiers init-declarator-list (opt) ;
1686 * static_assert-declaration
1688 * init-declarator-list:
1689 * init-declarator
1690 * init-declarator-list , init-declarator
1692 * init-declarator:
1693 * declarator
1694 * declarator = initializer
1696 * Params:
1697 * level = declaration context
1699 void cparseDeclaration(LVL level)
1701 //printf("cparseDeclaration(level = %d)\n", level);
1702 if (token.value == TOK._Static_assert)
1704 auto s = cparseStaticAssert();
1705 symbols.push(s);
1706 return;
1709 if (token.value == TOK.__pragma)
1711 uupragmaDirective(scanloc);
1712 return;
1715 if (token.value == TOK._import) // import declaration extension
1717 auto a = parseImport();
1718 if (a && a.length)
1719 symbols.append(a);
1720 return;
1723 const typedefTabLengthSave = typedefTab.length;
1724 auto symbolsSave = symbols;
1725 Specifier specifier;
1726 specifier.packalign = this.packalign;
1727 auto tspec = cparseDeclarationSpecifiers(level, specifier);
1729 AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
1731 /* `struct tag;` and `struct tag { ... };`
1732 * always result in a declaration in the current scope
1734 auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
1735 (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
1736 new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
1737 if (!tt.packalign.isUnknown())
1739 // saw `struct __declspec(align(N)) Tag ...`
1740 auto st = stag.isStructDeclaration();
1741 st.alignment = tt.packalign;
1743 stag.members = tt.members;
1744 tt.members = null;
1745 if (!symbols)
1746 symbols = new AST.Dsymbols();
1747 auto stags = applySpecifier(stag, specifier);
1748 symbols.push(stags);
1749 return stags;
1752 /* If a declarator does not follow, it is unnamed
1754 if (token.value == TOK.semicolon)
1756 if (!tspec)
1758 nextToken();
1759 return; // accept empty declaration as an extension
1762 if (auto ti = tspec.isTypeIdentifier())
1764 // C11 6.7.2-2
1765 error("type-specifier missing for declaration of `%s`", ti.ident.toChars());
1766 nextToken();
1767 return;
1770 nextToken();
1771 auto tt = tspec.isTypeTag();
1772 if (!tt ||
1773 !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
1774 return; // legal but meaningless empty declaration, ignore it
1776 auto stags = declareTag(tt, specifier);
1778 if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it
1780 if (!tt.members)
1781 error(tt.loc, "`enum %s` has no members", stags.toChars());
1783 return;
1786 if (!tspec)
1788 error("no type for declarator before `%s`", token.toChars());
1789 panic();
1790 nextToken();
1791 return;
1794 if (tspec && specifier.mod & MOD.xconst)
1796 tspec = toConst(tspec);
1797 specifier.mod &= ~MOD.xnone; // 'used' it
1800 void scanPastSemicolon()
1802 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1803 nextToken();
1804 nextToken();
1807 if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier())
1809 /* C11 6.7.2-2
1810 * Special check for `const b = 1;` because some compilers allow it
1812 error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars());
1813 return scanPastSemicolon();
1816 bool first = true;
1817 while (1)
1819 Identifier id;
1820 AST.StringExp asmName;
1821 auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
1822 if (!dt)
1824 panic();
1825 nextToken();
1826 break; // error recovery
1829 /* GNU Extensions
1830 * init-declarator:
1831 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1832 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1834 switch (token.value)
1836 case TOK.assign:
1837 case TOK.comma:
1838 case TOK.semicolon:
1839 case TOK.asm_:
1840 case TOK.__attribute__:
1841 if (token.value == TOK.asm_)
1842 asmName = cparseGnuAsmLabel();
1843 if (token.value == TOK.__attribute__)
1845 cparseGnuAttributes(specifier);
1846 if (token.value == TOK.leftCurly)
1847 break; // function definition
1849 /* This is a data definition, there cannot now be a
1850 * function definition.
1852 first = false;
1853 break;
1855 default:
1856 break;
1859 if (specifier.alignExps && dt.isTypeFunction())
1860 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1861 if (specifier.alignExps && specifier.scw == SCW.xregister)
1862 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1864 /* C11 6.9.1 Function Definitions
1865 * function-definition:
1866 * declaration-specifiers declarator declaration-list (opt) compound-statement
1868 * declaration-list:
1869 * declaration
1870 * declaration-list declaration
1872 auto t = &token;
1873 if (first && // first declarator
1874 id &&
1875 dt.isTypeFunction() && // function type not inherited from a typedef
1876 isDeclarationList(t) && // optional declaration-list
1877 level == LVL.global && // function definitions only at global scope
1878 t.value == TOK.leftCurly) // start of compound-statement
1880 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
1881 typedefTab.setDim(typedefTabLengthSave);
1882 symbols = symbolsSave;
1883 symbols.push(s);
1884 return;
1886 AST.Dsymbol s = null;
1887 typedefTab.setDim(typedefTabLengthSave);
1888 symbols = symbolsSave;
1889 if (!symbols)
1890 symbols = new AST.Dsymbols; // lazilly create it
1892 if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
1893 error("declaration-specifier-seq required");
1894 else if (specifier.scw == SCW.xtypedef)
1896 if (token.value == TOK.assign)
1897 error("no initializer for typedef declaration");
1898 if (specifier.alignExps)
1899 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1901 if (specifier.vector_size)
1903 auto length = new AST.IntegerExp(token.loc, specifier.vector_size / dt.size(), AST.Type.tuns32);
1904 auto tsa = new AST.TypeSArray(dt, length);
1905 dt = new AST.TypeVector(tsa);
1906 specifier.vector_size = 0; // used it up
1909 bool isalias = true;
1910 Identifier idt;
1911 if (auto ts = dt.isTypeStruct())
1913 if (ts.sym.isAnonymous())
1915 // This is a typedef for an anonymous struct-or-union.
1916 // Directly set the ident for the struct-or-union.
1917 ts.sym.ident = id;
1918 isalias = false;
1920 idt = ts.sym.ident;
1922 else if (auto te = dt.isTypeEnum())
1924 if (te.sym.isAnonymous())
1926 // This is a typedef for an anonymous enum.
1927 te.sym.ident = id;
1928 isalias = false;
1930 idt = te.sym.ident;
1932 else if (auto tt = dt.isTypeTag())
1934 if (tt.id || tt.tok == TOK.enum_)
1936 if (!tt.id && id)
1937 /* This applies for enums declared as
1938 * typedef enum {A} E;
1940 tt.id = id;
1941 Specifier spec;
1942 declareTag(tt, spec);
1944 idt = tt.id;
1946 if (isalias)
1948 auto ad = new AST.AliasDeclaration(token.loc, id, dt);
1949 if (id == idt)
1950 ad.adFlags |= ad.hidden; // do not print when generating .di files
1951 s = ad;
1954 insertTypedefToTypedefTab(id, dt); // remember typedefs
1956 else if (id)
1958 if (auto tt = dt.isTypeTag())
1960 if (tt.members && (tt.id || tt.tok == TOK.enum_))
1962 Specifier spec;
1963 declareTag(tt, spec);
1967 if (level == LVL.prototype)
1968 break; // declared later as Parameter, not VarDeclaration
1970 if (dt.ty == AST.Tvoid)
1971 error("`void` has no value");
1973 AST.Initializer initializer;
1974 bool hasInitializer;
1975 if (token.value == TOK.assign)
1977 nextToken();
1978 hasInitializer = true;
1979 initializer = cparseInitializer();
1981 // declare the symbol
1982 assert(id);
1984 if (isFunctionTypedef(dt))
1986 if (hasInitializer)
1987 error("no initializer for function declaration");
1988 if (specifier.scw & SCW.x_Thread_local)
1989 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1990 StorageClass stc = specifiersToSTC(level, specifier);
1991 stc &= ~STC.gshared; // no gshared functions
1992 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn);
1993 specifiersToFuncDeclaration(fd, specifier);
1994 s = fd;
1996 else
1998 // Give non-extern variables an implicit void initializer
1999 // if one has not been explicitly set.
2000 if (!hasInitializer &&
2001 !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
2002 initializer = new AST.VoidInitializer(token.loc);
2003 auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
2004 specifiersToVarDeclaration(vd, specifier);
2005 s = vd;
2007 if (level != LVL.global)
2008 insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes
2010 if (s !is null)
2012 // Saw `asm("name")` in the function, type, or variable definition.
2013 // This is equivalent to `pragma(mangle, "name")` in D
2014 if (asmName)
2017 https://issues.dlang.org/show_bug.cgi?id=23012
2018 Ideally this would be translated to a pragma(mangle)
2019 decl. This is not possible because ImportC symbols are
2020 (currently) merged before semantic analysis is performed,
2021 so the pragma(mangle) never effects any change on the declarations
2022 it pertains too.
2024 Writing to mangleOverride directly avoids this, and is possible
2025 because C only a StringExp is allowed unlike a full fat pragma(mangle)
2026 which is more liberal.
2028 if (auto p = s.isDeclaration())
2030 auto str = asmName.peekString();
2031 p.mangleOverride = str;
2032 // p.adFlags |= AST.VarDeclaration.nounderscore;
2033 p.adFlags |= 4; // cannot get above line to compile on Ubuntu
2036 s = applySpecifier(s, specifier);
2037 if (level == LVL.local)
2039 // Wrap the declaration in `extern (C) { declaration }`
2040 // Necessary for function pointers, but harmless to apply to all.
2041 auto decls = new AST.Dsymbols(1);
2042 (*decls)[0] = s;
2043 s = new AST.LinkDeclaration(s.loc, linkage, decls);
2045 symbols.push(s);
2047 if (level == LVL.global && !id)
2048 error("expected identifier for declaration");
2050 first = false;
2052 switch (token.value)
2054 case TOK.identifier:
2055 if (s)
2057 error(token.loc, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
2058 goto Lend;
2060 goto default;
2062 case TOK.semicolon:
2063 nextToken();
2064 return;
2066 case TOK.comma:
2067 if (!symbolsSave)
2068 symbolsSave = symbols;
2069 nextToken();
2070 break;
2072 default:
2073 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars());
2074 Lend:
2075 return scanPastSemicolon();
2080 /***************************************
2081 * C11 Function Definitions
2082 * function-definition
2083 * declaration-specifiers declarator declaration-list (opt) compound-statement
2085 * declaration-list:
2086 * declaration
2087 * declaration-list declaration
2089 * It's already been parsed up to the declaration-list (opt).
2090 * Pick it up from there.
2091 * Params:
2092 * id = function identifier
2093 * ft = function type
2094 * specifier = function specifiers
2095 * Returns:
2096 * Dsymbol for the function
2098 AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
2100 /* Start function scope
2102 typedefTab.push(null);
2104 if (token.value != TOK.leftCurly) // if not start of a compound-statement
2106 // Do declaration-list
2109 cparseDeclaration(LVL.parameter);
2110 } while (token.value != TOK.leftCurly);
2112 /* Since there were declarations, the parameter-list must have been
2113 * an identifier-list.
2115 ft.parameterList.hasIdentifierList = true; // semantic needs to know to adjust parameter types
2116 auto pl = ft.parameterList;
2117 if (pl.varargs != AST.VarArg.none && pl.length)
2118 error("function identifier-list cannot end with `...`");
2119 ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments
2120 auto plLength = pl.length;
2121 if (symbols.length != plLength)
2122 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
2124 /* Transfer the types and storage classes from symbols[] to pl[]
2126 foreach (i; 0 .. plLength)
2128 auto p = pl[i]; // yes, quadratic
2130 // Convert typedef-identifier to identifier
2131 if (p.type)
2133 if (auto t = p.type.isTypeIdentifier())
2135 p.ident = t.ident;
2136 p.type = null;
2140 if (p.type || !(p.storageClass & STC.parameter))
2141 error("storage class and type are not allowed in identifier-list");
2142 foreach (s; (*symbols)[]) // yes, quadratic
2144 auto ad = s.isAttribDeclaration();
2145 if (ad)
2146 s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration
2148 auto d = s.isDeclaration();
2149 if (d && p.ident == d.ident && d.type)
2151 p.type = d.type;
2152 p.storageClass = d.storage_class;
2153 d.type = null; // don't reuse
2154 break;
2157 if (!p.type)
2159 error("no declaration for identifier `%s`", p.ident.toChars());
2160 p.type = AST.Type.terror;
2165 addFuncName = false; // gets set to true if somebody references __func__ in this function
2166 const locFunc = token.loc;
2168 auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope
2169 typedefTab.pop(); // end of function scope
2171 StorageClass stc = specifiersToSTC(LVL.global, specifier);
2172 stc &= ~STC.gshared; // no gshared functions
2173 auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn);
2174 specifiersToFuncDeclaration(fd, specifier);
2176 if (addFuncName)
2178 auto s = createFuncName(locFunc, id);
2179 body = new AST.CompoundStatement(locFunc, s, body);
2181 fd.fbody = body;
2183 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2185 return fd;
2188 /***************************************
2189 * C11 Initialization
2190 * initializer:
2191 * assignment-expression
2192 * { } // C23 6.7.10 addition
2193 * { initializer-list }
2194 * { initializer-list , }
2196 * initializer-list:
2197 * designation (opt) initializer
2198 * initializer-list , designation (opt) initializer
2200 * designation:
2201 * designator-list =
2203 * designator-list:
2204 * designator
2205 * designator-list designator
2207 * designator:
2208 * [ constant-expression ]
2209 * . identifier
2210 * Returns:
2211 * initializer
2213 AST.Initializer cparseInitializer()
2215 if (token.value != TOK.leftCurly)
2217 auto ae = cparseAssignExp(); // assignment-expression
2218 return new AST.ExpInitializer(token.loc, ae);
2220 nextToken();
2221 const loc = token.loc;
2223 if (token.value == TOK.rightCurly) // { }
2225 nextToken();
2226 return new AST.DefaultInitializer(loc);
2229 /* Collect one or more `designation (opt) initializer`
2230 * into ci.initializerList, but lazily create ci
2232 AST.CInitializer ci;
2233 while (1)
2235 /* There can be 0 or more designators preceding an initializer.
2236 * Collect them in desigInit
2238 AST.DesigInit desigInit;
2239 while (1)
2241 if (token.value == TOK.leftBracket) // [ constant-expression ]
2243 nextToken();
2244 auto e = cparseConstantExp();
2245 check(TOK.rightBracket);
2246 if (!desigInit.designatorList)
2247 desigInit.designatorList = new AST.Designators;
2248 desigInit.designatorList.push(AST.Designator(e));
2250 else if (token.value == TOK.dot) // . identifier
2252 nextToken();
2253 if (token.value != TOK.identifier)
2255 error("identifier expected following `.` designator");
2256 break;
2258 if (!desigInit.designatorList)
2259 desigInit.designatorList = new AST.Designators;
2260 desigInit.designatorList.push(AST.Designator(token.ident));
2261 nextToken();
2263 else
2265 if (desigInit.designatorList)
2266 check(TOK.assign);
2267 break;
2271 desigInit.initializer = cparseInitializer();
2272 if (!ci)
2273 ci = new AST.CInitializer(loc);
2274 ci.initializerList.push(desigInit);
2275 if (token.value == TOK.comma)
2277 nextToken();
2278 if (token.value != TOK.rightCurly)
2279 continue;
2281 break;
2283 check(TOK.rightCurly);
2284 //printf("ci: %s\n", ci.toChars());
2285 return ci;
2288 /*************************************
2289 * C11 6.7
2290 * declaration-specifier:
2291 * storage-class-specifier declaration-specifiers (opt)
2292 * type-specifier declaration-specifiers (opt)
2293 * type-qualifier declaration-specifiers (opt)
2294 * function-specifier declaration-specifiers (opt)
2295 * alignment-specifier declaration-specifiers (opt)
2296 * Params:
2297 * level = declaration context
2298 * specifier = specifiers in and out
2299 * Returns:
2300 * resulting type, null if not specified
2302 private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier)
2304 enum TKW : uint
2306 xnone = 0,
2307 xchar = 1,
2308 xsigned = 2,
2309 xunsigned = 4,
2310 xshort = 8,
2311 xint = 0x10,
2312 xlong = 0x20,
2313 xllong = 0x40,
2314 xfloat = 0x80,
2315 xdouble = 0x100,
2316 xldouble = 0x200,
2317 xtag = 0x400,
2318 xident = 0x800,
2319 xvoid = 0x1000,
2320 xbool = 0x4000,
2321 ximaginary = 0x8000,
2322 xcomplex = 0x10000,
2323 x_Atomic = 0x20000,
2324 xint128 = 0x40000,
2327 AST.Type t;
2328 Loc loc;
2329 //printf("parseDeclarationSpecifiers()\n");
2331 TKW tkw;
2332 SCW scw = specifier.scw & SCW.xtypedef;
2333 MOD mod;
2334 Identifier id;
2335 Identifier previd;
2337 Lwhile:
2338 while (1)
2340 //printf("token %s\n", token.toChars());
2341 TKW tkwx;
2342 SCW scwx;
2343 MOD modx;
2344 switch (token.value)
2346 // Storage class specifiers
2347 case TOK.static_: scwx = SCW.xstatic; break;
2348 case TOK.extern_: scwx = SCW.xextern; break;
2349 case TOK.auto_: scwx = SCW.xauto; break;
2350 case TOK.register: scwx = SCW.xregister; break;
2351 case TOK.typedef_: scwx = SCW.xtypedef; break;
2352 case TOK.inline: scwx = SCW.xinline; break;
2353 case TOK._Noreturn: scwx = SCW.x_Noreturn; break;
2354 case TOK.__thread:
2355 case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
2357 // Type qualifiers
2358 case TOK.const_: modx = MOD.xconst; break;
2359 case TOK.volatile: modx = MOD.xvolatile; break;
2360 case TOK.restrict: modx = MOD.xrestrict; break;
2361 case TOK.__stdcall: modx = MOD.x__stdcall; break;
2363 // Type specifiers
2364 case TOK.char_: tkwx = TKW.xchar; break;
2365 case TOK.signed: tkwx = TKW.xsigned; break;
2366 case TOK.unsigned: tkwx = TKW.xunsigned; break;
2367 case TOK.int16: tkwx = TKW.xshort; break;
2368 case TOK.int32: tkwx = TKW.xint; break;
2369 case TOK.int64: tkwx = TKW.xlong; break;
2370 case TOK.__int128: tkwx = TKW.xint128; break;
2371 case TOK.float32: tkwx = TKW.xfloat; break;
2372 case TOK.float64: tkwx = TKW.xdouble; break;
2373 case TOK.void_: tkwx = TKW.xvoid; break;
2374 case TOK._Bool: tkwx = TKW.xbool; break;
2375 case TOK._Imaginary: tkwx = TKW.ximaginary; break;
2376 case TOK._Complex: tkwx = TKW.xcomplex; break;
2378 case TOK.identifier:
2379 tkwx = TKW.xident;
2380 id = token.ident;
2381 break;
2383 case TOK.struct_:
2384 case TOK.union_:
2386 const structOrUnion = token.value;
2387 const sloc = token.loc;
2388 nextToken();
2390 Specifier tagSpecifier;
2392 /* GNU Extensions
2393 * struct-or-union-specifier:
2394 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2395 * struct-or-union gnu-attribute (opt) identifier
2397 while (1)
2399 if (token.value == TOK.__attribute__)
2400 cparseGnuAttributes(tagSpecifier);
2401 else if (token.value == TOK.__declspec)
2402 cparseDeclspec(tagSpecifier);
2403 else if (token.value == TOK.__pragma)
2404 uupragmaDirective(sloc);
2405 else
2406 break;
2408 t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols);
2409 tkwx = TKW.xtag;
2410 break;
2413 case TOK.enum_:
2414 t = cparseEnum(symbols);
2415 tkwx = TKW.xtag;
2416 break;
2418 case TOK._Atomic:
2420 // C11 6.7.2.4
2421 // type-specifier if followed by `( type-name )`
2422 auto tk = peek(&token);
2423 if (tk.value == TOK.leftParenthesis)
2425 tk = peek(tk);
2426 if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
2428 nextToken();
2429 nextToken();
2430 t = cparseTypeName();
2431 tkwx = TKW.x_Atomic;
2432 break;
2435 // C11 6.7.3 type-qualifier if not
2436 modx = MOD.x_Atomic;
2437 break;
2440 case TOK._Alignas:
2442 /* C11 6.7.5
2443 * _Alignas ( type-name )
2444 * _Alignas ( constant-expression )
2447 if (level & (LVL.parameter | LVL.prototype))
2448 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2450 nextToken();
2451 check(TOK.leftParenthesis);
2452 AST.Expression exp;
2453 auto tk = &token;
2454 if (isTypeName(tk)) // _Alignas ( type-name )
2456 auto talign = cparseTypeName();
2457 /* Convert type to expression: `talign.alignof`
2459 auto e = new AST.TypeExp(loc, talign);
2460 exp = new AST.DotIdExp(loc, e, Id.__xalignof);
2462 else // _Alignas ( constant-expression )
2464 exp = cparseConstantExp();
2467 if (!specifier.alignExps)
2468 specifier.alignExps = new AST.Expressions(0);
2469 specifier.alignExps.push(exp);
2471 check(TOK.rightParenthesis);
2472 break;
2475 case TOK.__attribute__:
2477 /* GNU Extensions
2478 * declaration-specifiers:
2479 * gnu-attributes declaration-specifiers (opt)
2481 cparseGnuAttributes(specifier);
2482 break;
2485 case TOK.__declspec:
2487 /* Microsoft extension
2489 cparseDeclspec(specifier);
2490 break;
2493 case TOK.typeof_:
2495 nextToken();
2496 check(TOK.leftParenthesis);
2498 auto tk = &token;
2499 AST.Expression e;
2500 if (isTypeName(tk))
2501 e = new AST.TypeExp(loc, cparseTypeName());
2502 else
2503 e = cparseExpression();
2504 t = new AST.TypeTypeof(loc, e);
2506 if(token.value == TOK.rightParenthesis)
2507 nextToken();
2508 else
2510 t = AST.Type.terror;
2511 error("`typeof` operator expects an expression or type name in parentheses");
2513 // skipParens et. al expect to be on the opening parenthesis
2514 int parens;
2515 loop: while(1)
2517 switch(token.value)
2519 case TOK.leftParenthesis:
2520 parens++;
2521 break;
2522 case TOK.rightParenthesis:
2523 parens--;
2524 if(parens < 0)
2525 goto case;
2526 break;
2527 case TOK.endOfFile:
2528 break loop;
2529 default:
2531 nextToken();
2535 tkwx = TKW.xtag;
2536 break;
2539 default:
2540 break Lwhile;
2543 if (tkwx)
2545 if (tkw & TKW.xlong && tkwx & TKW.xlong)
2547 tkw &= ~TKW.xlong;
2548 tkwx = TKW.xllong;
2550 if (tkw && tkwx & TKW.xident)
2552 // 2nd identifier can't be a typedef
2553 break Lwhile; // leave parser on the identifier for the following declarator
2555 else if (tkwx & TKW.xident)
2557 // 1st identifier, save it for TypeIdentifier
2558 previd = id;
2560 if (tkw & TKW.xident && tkwx || // typedef-name followed by type-specifier
2561 tkw & tkwx) // duplicate type-specifiers
2563 error("illegal combination of type specifiers");
2564 tkwx = TKW.init;
2566 tkw |= tkwx;
2567 if (!(tkwx & TKW.xtag)) // if parser already advanced
2568 nextToken();
2569 continue;
2572 if (modx)
2574 mod |= modx;
2575 nextToken();
2576 continue;
2579 if (scwx)
2581 if (scw & scwx)
2582 error("duplicate storage class");
2583 scw |= scwx;
2584 // C11 6.7.1-2 At most one storage-class may be given, except that
2585 // _Thread_local may appear with static or extern.
2586 const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef);
2587 if (scw2 & (scw2 - 1) ||
2588 scw & (SCW.x_Thread_local) && scw & (SCW.xauto | SCW.xregister | SCW.xtypedef))
2590 error("multiple storage classes in declaration specifiers");
2591 scw &= ~scwx;
2593 if (level == LVL.local &&
2594 scw & (SCW.x_Thread_local) && scw & (SCW.xinline | SCW.x_Noreturn))
2596 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2597 scw &= ~scwx;
2599 if (level == LVL.local &&
2600 scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
2602 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2603 scw &= ~scwx;
2605 if (level & (LVL.parameter | LVL.prototype) &&
2606 scw & ~SCW.xregister)
2608 error("only `register` storage class allowed for function parameters");
2609 scw &= ~scwx;
2611 if (level == LVL.global &&
2612 scw & (SCW.xauto | SCW.xregister))
2614 error("`auto` and `register` storage class not allowed for global");
2615 scw &= ~scwx;
2617 nextToken();
2618 continue;
2622 specifier.scw = scw;
2623 specifier.mod = mod;
2625 // Convert TKW bits to type t
2626 switch (tkw)
2628 case TKW.xnone: t = null; break;
2630 case TKW.xchar: t = AST.Type.tchar; break;
2631 case TKW.xsigned | TKW.xchar: t = AST.Type.tint8; break;
2632 case TKW.xunsigned | TKW.xchar: t = AST.Type.tuns8; break;
2634 case TKW.xshort:
2635 case TKW.xsigned | TKW.xshort:
2636 case TKW.xsigned | TKW.xshort | TKW.xint:
2637 case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break;
2639 case TKW.xunsigned | TKW.xshort | TKW.xint:
2640 case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break;
2642 case TKW.xint:
2643 case TKW.xsigned:
2644 case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break;
2646 case TKW.xunsigned:
2647 case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break;
2649 case TKW.xlong:
2650 case TKW.xsigned | TKW.xlong:
2651 case TKW.xsigned | TKW.xlong | TKW.xint:
2652 case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break;
2654 case TKW.xunsigned | TKW.xlong | TKW.xint:
2655 case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break;
2657 case TKW.xllong:
2658 case TKW.xsigned | TKW.xllong:
2659 case TKW.xsigned | TKW.xllong | TKW.xint:
2660 case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break;
2662 case TKW.xunsigned | TKW.xllong | TKW.xint:
2663 case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break;
2665 case TKW.xint128:
2666 case TKW.xsigned | TKW.xint128: t = integerTypeForSize(16); break;
2668 case TKW.xunsigned | TKW.xint128: t = unsignedTypeForSize(16); break;
2670 case TKW.xvoid: t = AST.Type.tvoid; break;
2671 case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break;
2673 case TKW.xfloat: t = AST.Type.tfloat32; break;
2674 case TKW.xdouble: t = AST.Type.tfloat64; break;
2675 case TKW.xlong | TKW.xdouble: t = realType(RTFlags.realfloat); break;
2677 case TKW.ximaginary | TKW.xfloat: t = AST.Type.timaginary32; break;
2678 case TKW.ximaginary | TKW.xdouble: t = AST.Type.timaginary64; break;
2679 case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break;
2681 case TKW.xcomplex | TKW.xfloat: t = AST.Type.tcomplex32; break;
2682 case TKW.xcomplex | TKW.xdouble: t = AST.Type.tcomplex64; break;
2683 case TKW.xcomplex | TKW.xlong | TKW.xdouble: t = realType(RTFlags.complex); break;
2685 case TKW.xident:
2687 const idx = previd.toString();
2688 if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore
2689 importBuiltins = true; // probably one of those compiler extensions
2690 t = null;
2692 /* Punch through to what the typedef is, to support things like:
2693 * typedef T* T;
2695 auto pt = lookupTypedef(previd);
2696 if (pt && *pt) // if previd is a known typedef
2697 t = *pt;
2699 if (!t)
2700 t = new AST.TypeIdentifier(loc, previd);
2701 break;
2704 case TKW.xtag:
2705 case TKW.x_Atomic: // no atomics for you
2706 break; // t is already set
2708 default:
2709 error("illegal type combination");
2710 t = AST.Type.terror;
2711 break;
2714 return t;
2717 /********************************
2718 * C11 6.7.6
2719 * Parse a declarator (including function definitions).
2720 * declarator:
2721 * pointer (opt) direct-declarator
2723 * direct-declarator :
2724 * identifier
2725 * ( declarator )
2726 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2727 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2728 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2729 * direct-declarator [ type-qualifier-list (opt) * ]
2730 * direct-declarator ( parameter-type-list )
2731 * direct-declarator ( identifier-list (opt) )
2733 * pointer :
2734 * * type-qualifier-list (opt)
2735 * * type-qualifier-list (opt) pointer
2737 * type-qualifier-list :
2738 * type-qualifier
2739 * type-qualifier-list type-qualifier
2741 * parameter-type-list :
2742 * parameter-list
2743 * parameter-list , ...
2745 * parameter-list :
2746 * parameter-declaration
2747 * parameter-list , parameter-declaration
2749 * parameter-declaration :
2750 * declaration-specifiers declarator
2751 * declaration-specifiers abstract-declarator (opt)
2753 * identifier-list :
2754 * identifier
2755 * identifier-list , identifier
2757 * Params:
2758 * declarator = declarator kind
2759 * tbase = base type to start with
2760 * pident = set to Identifier if there is one, null if not
2761 * specifier = specifiers in and out
2762 * Returns:
2763 * type declared. If a TypeFunction is returned, this.symbols is the
2764 * symbol table for the parameter-type-list, which will contain any
2765 * declared struct, union or enum tags.
2767 private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
2768 out Identifier pident, ref Specifier specifier)
2770 //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
2771 AST.Types constTypes; // all the Types that will need `const` applied to them
2773 /* Insert tx -> t into
2774 * ts -> ... -> t
2775 * so that
2776 * ts -> ... -> tx -> t
2778 static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t)
2780 AST.Type* pt;
2781 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
2784 *pt = tx;
2787 AST.Type parseDecl(AST.Type t)
2789 //printf("parseDecl() t: %s\n", t.toChars());
2790 AST.Type ts;
2791 while (1)
2793 switch (token.value)
2795 case TOK.identifier: // identifier
2796 //printf("identifier %s\n", token.ident.toChars());
2797 if (declarator == DTR.xabstract)
2798 error("identifier not allowed in abstract-declarator");
2799 pident = token.ident;
2800 ts = t;
2801 nextToken();
2802 break;
2804 case TOK.leftParenthesis: // ( declarator )
2805 //printf("leftParen\n");
2806 /* like: T (*fp)();
2807 * T ((*fp))();
2809 auto tk = &token;
2810 if (!isCDeclarator(tk, declarator))
2812 /* Not ( declarator ), might be parameter-list
2814 ts = t;
2815 break;
2817 nextToken();
2819 if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
2821 specifier.mod |= MOD.x__stdcall;
2822 nextToken();
2825 ts = parseDecl(t);
2826 check(TOK.rightParenthesis);
2827 break;
2829 case TOK.mul: // pointer
2830 //printf("star\n");
2831 t = new AST.TypePointer(t);
2832 nextToken();
2833 // add post fixes const/volatile/restrict/_Atomic
2834 const mod = cparseTypeQualifierList();
2835 if (mod & MOD.xconst)
2836 constTypes.push(t);
2837 if (token.value == TOK.__attribute__)
2838 cparseGnuAttributes(specifier);
2839 continue;
2841 default:
2842 //printf("default %s\n", token.toChars());
2843 if (declarator == DTR.xdirect)
2845 if (!t || t.isTypeIdentifier())
2847 // const arr[1];
2848 error("no type-specifier for declarator");
2849 t = AST.Type.tint32;
2851 else
2852 error("identifier or `(` expected"); // )
2853 panic();
2855 ts = t;
2856 break;
2858 break;
2861 // parse DeclaratorSuffixes
2862 while (1)
2864 switch (token.value)
2866 case TOK.leftBracket:
2868 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2869 AST.Type ta;
2870 nextToken();
2872 auto mod = cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2874 bool isStatic;
2875 bool isVLA;
2876 if (token.value == TOK.static_)
2878 isStatic = true; // `static`
2879 nextToken();
2880 if (!mod) // type qualifiers after `static`
2881 mod = cparseTypeQualifierList();
2883 else if (token.value == TOK.mul)
2885 if (peekNext() == TOK.rightBracket)
2887 isVLA = true; // `*`
2888 nextToken();
2892 if (isStatic || token.value != TOK.rightBracket)
2894 //printf("It's a static array\n");
2895 AST.Expression e = cparseAssignExp(); // [ expression ]
2896 ta = new AST.TypeSArray(t, e);
2898 else
2900 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2902 ta = new AST.TypeSArray(t);
2904 check(TOK.rightBracket);
2906 // Issue errors for unsupported types.
2907 if (isVLA) // C11 6.7.6.2
2909 error("variable length arrays are not supported");
2911 if (isStatic) // C11 6.7.6.3
2913 error("static array parameters are not supported");
2915 if (declarator != DTR.xparameter)
2917 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2919 if (isVLA)
2920 error("variable length array used outside of function prototype");
2921 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2922 * in a declaration of a function parameter with an array type.
2924 if (isStatic || mod)
2925 error("static or type qualifier used outside of function prototype");
2927 if (ts.isTypeSArray() || ts.isTypeDArray())
2929 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2930 * in the outermost array type derivation.
2932 if (isStatic || mod)
2933 error("static or type qualifier used in non-outermost array type derivation");
2934 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2935 * function type.
2937 if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
2938 error("array type has incomplete element type `%s`", ta.toChars());
2941 // Apply type qualifiers to the constructed type.
2942 if (mod & MOD.xconst) // ignore the other bits
2943 ta = toConst(ta);
2944 insertTx(ts, ta, t); // ts -> ... -> ta -> t
2945 continue;
2948 case TOK.leftParenthesis:
2950 // New symbol table for parameter-list
2951 auto symbolsSave = this.symbols;
2952 this.symbols = null;
2954 auto parameterList = cparseParameterList();
2955 const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage;
2956 StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
2957 if (specifier._pure)
2958 stc |= STC.pure_;
2959 AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
2960 //tf = tf.addSTC(storageClass); // TODO
2961 insertTx(ts, tf, t); // ts -> ... -> tf -> t
2963 if (ts != tf)
2964 this.symbols = symbolsSave;
2965 break;
2968 default:
2969 break;
2971 break;
2973 if (declarator == DTR.xdirect && !pident)
2974 error("expected identifier for declarator");
2975 return ts;
2978 auto t = parseDecl(tbase);
2980 if (specifier.vector_size)
2982 auto length = new AST.IntegerExp(token.loc, specifier.vector_size / tbase.size(), AST.Type.tuns32);
2983 auto tsa = new AST.TypeSArray(tbase, length);
2984 AST.Type tv = new AST.TypeVector(tsa);
2985 specifier.vector_size = 0; // used it up
2987 insertTx(t, tv, tbase); // replace tbase with tv
2990 /* Because const is transitive, cannot assemble types from
2991 * fragments. Instead, types to be annotated with const are put
2992 * in constTypes[], and a bottom up scan of t is done to apply
2993 * const
2995 if (constTypes.length)
2997 AST.Type constApply(AST.Type t)
2999 if (t.nextOf())
3001 auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this
3002 tn.next = constApply(tn.next);
3004 foreach (tc; constTypes[])
3006 if (tc is t)
3008 return toConst(t);
3011 return t;
3014 if (declarator == DTR.xparameter &&
3015 t.isTypePointer())
3017 /* Because there are instances in .h files of "const pointer to mutable",
3018 * skip applying transitive `const`
3019 * https://issues.dlang.org/show_bug.cgi?id=22534
3021 auto tn = cast(AST.TypeNext)t;
3022 tn.next = constApply(tn.next);
3024 else
3025 t = constApply(t);
3028 //printf("result: %s\n", t.toChars());
3029 return t;
3032 /******************************
3033 * C11 6.7.3
3034 * type-qualifier:
3035 * const
3036 * restrict
3037 * volatile
3038 * _Atomic
3039 * __stdcall
3041 MOD cparseTypeQualifierList()
3043 MOD mod;
3044 while (1)
3046 switch (token.value)
3048 case TOK.const_: mod |= MOD.xconst; break;
3049 case TOK.volatile: mod |= MOD.xvolatile; break;
3050 case TOK.restrict: mod |= MOD.xrestrict; break;
3051 case TOK._Atomic: mod |= MOD.x_Atomic; break;
3052 case TOK.__stdcall: mod |= MOD.x__stdcall; break;
3054 default:
3055 return mod;
3057 nextToken();
3061 /***********************************
3062 * C11 6.7.7
3064 AST.Type cparseTypeName()
3066 Specifier specifier;
3067 specifier.packalign.setDefault();
3068 auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
3069 if (!tspec)
3071 error("type-specifier is missing");
3072 tspec = AST.Type.tint32;
3074 if (tspec && specifier.mod & MOD.xconst)
3076 tspec = toConst(tspec);
3077 specifier.mod = MOD.xnone; // 'used' it
3079 Identifier id;
3080 return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
3083 /***********************************
3084 * C11 6.7.2.1
3085 * specifier-qualifier-list:
3086 * type-specifier specifier-qualifier-list (opt)
3087 * type-qualifier specifier-qualifier-list (opt)
3088 * Params:
3089 * level = declaration context
3090 * specifier = specifiers in and out
3091 * Returns:
3092 * resulting type, null if not specified
3094 AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier)
3096 auto t = cparseDeclarationSpecifiers(level, specifier);
3097 if (specifier.scw)
3098 error("storage class not allowed in specifier-qualified-list");
3099 return t;
3102 /***********************************
3103 * C11 6.7.6.3
3104 * ( parameter-type-list )
3105 * ( identifier-list (opt) )
3107 AST.ParameterList cparseParameterList()
3109 auto parameters = new AST.Parameters();
3110 AST.VarArg varargs = AST.VarArg.none;
3111 StorageClass varargsStc;
3113 check(TOK.leftParenthesis);
3114 if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
3116 nextToken();
3117 nextToken();
3118 return AST.ParameterList(parameters, varargs, varargsStc);
3121 if (token.value == TOK.rightParenthesis) // func()
3123 nextToken();
3124 return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc);
3127 /* Create function prototype scope
3129 typedefTab.push(null);
3131 AST.ParameterList finish()
3133 typedefTab.pop();
3134 return AST.ParameterList(parameters, varargs, varargsStc);
3137 /* The check for identifier-list comes later,
3138 * when doing the trailing declaration-list (opt)
3140 while (1)
3142 if (token.value == TOK.rightParenthesis)
3143 break;
3144 if (token.value == TOK.dotDotDot)
3146 if (parameters.length == 0) // func(...)
3147 error("named parameter required before `...`");
3148 importBuiltins = true; // will need __va_list_tag
3149 varargs = AST.VarArg.variadic; // C-style variadics
3150 nextToken();
3151 check(TOK.rightParenthesis);
3152 return finish();
3155 Specifier specifier;
3156 specifier.packalign.setDefault();
3157 auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
3158 if (!tspec)
3160 error("no type-specifier for parameter");
3161 tspec = AST.Type.tint32;
3164 if (specifier.mod & MOD.xconst)
3166 if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) &&
3167 tspec.isTypeIdentifier())
3168 error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars());
3170 tspec = toConst(tspec);
3171 specifier.mod = MOD.xnone; // 'used' it
3174 Identifier id;
3175 const paramLoc = token.loc;
3176 auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
3177 if (token.value == TOK.__attribute__)
3178 cparseGnuAttributes(specifier);
3179 if (specifier.mod & MOD.xconst)
3180 t = toConst(t);
3181 auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
3182 t, id, null, null);
3183 parameters.push(param);
3184 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
3185 break;
3186 check(TOK.comma);
3188 check(TOK.rightParenthesis);
3189 return finish();
3192 /***********************************
3193 * C11 6.7.10
3194 * _Static_assert ( constant-expression , string-literal ) ;
3196 private AST.StaticAssert cparseStaticAssert()
3198 const loc = token.loc;
3200 //printf("cparseStaticAssert()\n");
3201 nextToken();
3202 check(TOK.leftParenthesis);
3203 auto exp = cparseConstantExp();
3204 check(TOK.comma);
3205 if (token.value != TOK.string_)
3206 error("string literal expected");
3207 auto msg = cparsePrimaryExp();
3208 check(TOK.rightParenthesis);
3209 check(TOK.semicolon);
3210 return new AST.StaticAssert(loc, exp, msg);
3213 /*************************
3214 * Collect argument list.
3215 * Parser is on opening parenthesis.
3216 * Returns:
3217 * the arguments
3219 private AST.Expressions* cparseArguments()
3221 nextToken();
3222 auto arguments = new AST.Expressions();
3223 while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile)
3225 auto arg = cparseAssignExp();
3226 arguments.push(arg);
3227 if (token.value != TOK.comma)
3228 break;
3230 nextToken(); // consume comma
3233 check(TOK.rightParenthesis);
3235 return arguments;
3238 /*************************
3239 * __declspec parser
3240 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3241 * decl-specifier:
3242 * __declspec ( extended-decl-modifier-seq )
3244 * extended-decl-modifier-seq:
3245 * extended-decl-modifier (opt)
3246 * extended-decl-modifier extended-decl-modifier-seq
3248 * extended-decl-modifier:
3249 * align(number)
3250 * deprecated(depMsg)
3251 * dllimport
3252 * dllexport
3253 * naked
3254 * noinline
3255 * noreturn
3256 * nothrow
3257 * thread
3258 * Params:
3259 * specifier = filled in with the attribute(s)
3261 private void cparseDeclspec(ref Specifier specifier)
3263 //printf("cparseDeclspec()\n");
3264 /* Check for dllexport, dllimport
3265 * Ignore the rest
3267 nextToken(); // move past __declspec
3268 check(TOK.leftParenthesis);
3269 while (1)
3271 if (token.value == TOK.rightParenthesis)
3273 nextToken();
3274 break;
3276 else if (token.value == TOK.endOfFile)
3277 break;
3278 else if (token.value == TOK.identifier)
3280 if (token.ident == Id.dllimport)
3282 specifier.dllimport = true;
3283 nextToken();
3285 else if (token.ident == Id.dllexport)
3287 specifier.dllexport = true;
3288 nextToken();
3290 else if (token.ident == Id.naked)
3292 specifier.naked = true;
3293 nextToken();
3295 else if (token.ident == Id.noinline)
3297 specifier.scw |= SCW.xnoinline;
3298 nextToken();
3300 else if (token.ident == Id.noreturn)
3302 specifier.noreturn = true;
3303 nextToken();
3305 else if (token.ident == Id._nothrow)
3307 specifier._nothrow = true;
3308 nextToken();
3310 else if (token.ident == Id.thread)
3312 specifier.scw |= SCW.x_Thread_local;
3313 nextToken();
3315 else if (token.ident == Id._align)
3317 // Microsoft spec is very imprecise as to how this actually works
3318 nextToken();
3319 check(TOK.leftParenthesis);
3320 if (token.value == TOK.int32Literal)
3322 const n = token.unsvalue;
3323 if (n < 1 || n & (n - 1) || 8192 < n)
3324 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
3325 specifier.packalign.set(cast(uint)n);
3326 specifier.packalign.setPack(true);
3327 nextToken();
3329 else
3331 error("alignment value expected, not `%s`", token.toChars());
3332 nextToken();
3335 check(TOK.rightParenthesis);
3337 else if (token.ident == Id._deprecated)
3339 specifier._deprecated = true;
3340 nextToken();
3341 if (token.value == TOK.leftParenthesis) // optional deprecation message
3343 nextToken();
3344 specifier.depMsg = cparseExpression();
3345 check(TOK.rightParenthesis);
3348 else
3350 nextToken();
3351 if (token.value == TOK.leftParenthesis)
3352 cparseParens();
3355 else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3356 nextToken();
3357 else
3359 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token.toChars());
3360 nextToken();
3361 if (token.value != TOK.rightParenthesis)
3362 break;
3367 /*************************
3368 * Parser for asm label. It appears after the declarator, and has apparently
3369 * nothing to do with inline assembler.
3370 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3371 * simple-asm-expr:
3372 * asm ( asm-string-literal )
3374 * asm-string-literal:
3375 * string-literal
3377 private AST.StringExp cparseGnuAsmLabel()
3379 nextToken(); // move past asm
3380 check(TOK.leftParenthesis);
3381 if (token.value != TOK.string_)
3382 error("string literal expected for Asm Label, not `%s`", token.toChars());
3383 auto label = cparsePrimaryExp();
3384 check(TOK.rightParenthesis);
3385 return cast(AST.StringExp) label;
3388 /********************
3389 * Parse C inline assembler statement in Gnu format.
3390 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3391 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3392 * Current token is on the `asm`.
3393 * Returns:
3394 * inline assembler expression as a Statement
3396 private AST.Statement cparseGnuAsm()
3398 // Defer parsing of AsmStatements until semantic processing.
3399 const loc = token.loc;
3401 nextToken();
3403 // Consume all asm-qualifiers. As a future optimization, we could record
3404 // the `inline` and `volatile` storage classes against the statement.
3405 while (token.value == TOK.goto_ ||
3406 token.value == TOK.inline ||
3407 token.value == TOK.volatile)
3408 nextToken();
3410 check(TOK.leftParenthesis);
3411 if (token.value != TOK.string_)
3412 error("string literal expected for Assembler Template, not `%s`", token.toChars());
3413 Token* toklist = null;
3414 Token** ptoklist = &toklist;
3415 //Identifier label = null;
3416 auto statements = new AST.Statements();
3418 int parens;
3419 while (1)
3421 switch (token.value)
3423 case TOK.leftParenthesis:
3424 ++parens;
3425 goto default;
3427 case TOK.rightParenthesis:
3428 --parens;
3429 if (parens >= 0)
3430 goto default;
3431 break;
3433 case TOK.semicolon:
3434 error("matching `)` expected, not `;`");
3435 break;
3437 case TOK.endOfFile:
3438 /* ( */
3439 error("matching `)` expected, not end of file");
3440 break;
3442 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
3443 *ptoklist = allocateToken();
3444 memcpy(*ptoklist, &token, Token.sizeof);
3445 (*ptoklist).value = TOK.colon;
3446 ptoklist = &(*ptoklist).next;
3448 *ptoklist = allocateToken();
3449 memcpy(*ptoklist, &token, Token.sizeof);
3450 (*ptoklist).value = TOK.colon;
3451 ptoklist = &(*ptoklist).next;
3453 *ptoklist = null;
3454 nextToken();
3455 continue;
3457 default:
3458 *ptoklist = allocateToken();
3459 memcpy(*ptoklist, &token, Token.sizeof);
3460 ptoklist = &(*ptoklist).next;
3461 *ptoklist = null;
3462 nextToken();
3463 continue;
3465 if (toklist)
3467 // Create AsmStatement from list of tokens we've saved
3468 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
3469 statements.push(s);
3471 break;
3473 nextToken();
3474 auto s = new AST.CompoundAsmStatement(loc, statements, 0);
3475 return s;
3478 /*************************
3479 * __attribute__ parser
3480 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3481 * gnu-attributes:
3482 * gnu-attributes gnu-attribute-specifier
3484 * gnu-attribute-specifier:
3485 * __attribute__ (( gnu-attribute-list ))
3487 * gnu-attribute-list:
3488 * gnu-attribute (opt)
3489 * gnu-attribute-list , gnu-attribute
3491 * Params:
3492 * specifier = filled in with the attribute(s)
3494 private void cparseGnuAttributes(ref Specifier specifier)
3496 while (token.value == TOK.__attribute__)
3498 nextToken(); // move past __attribute__
3499 check(TOK.leftParenthesis);
3500 check(TOK.leftParenthesis);
3502 if (token.value != TOK.rightParenthesis)
3504 while (1)
3506 cparseGnuAttribute(specifier);
3507 if (token.value != TOK.comma)
3508 break;
3509 nextToken();
3513 check(TOK.rightParenthesis);
3514 check(TOK.rightParenthesis);
3518 /*************************
3519 * Parse a single GNU attribute
3520 * gnu-attribute:
3521 * gnu-attribute-name
3522 * gnu-attribute-name ( identifier )
3523 * gnu-attribute-name ( identifier , expression-list )
3524 * gnu-attribute-name ( expression-list (opt) )
3526 * gnu-attribute-name:
3527 * keyword
3528 * identifier
3530 * expression-list:
3531 * constant-expression
3532 * expression-list , constant-expression
3534 * Params:
3535 * specifier = filled in with the attribute(s)
3537 private void cparseGnuAttribute(ref Specifier specifier)
3539 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3540 * Ignore the rest
3542 if (!isGnuAttributeName())
3543 return;
3545 if (token.value == TOK.identifier)
3547 if (token.ident == Id.aligned)
3549 nextToken();
3550 if (token.value == TOK.leftParenthesis)
3552 nextToken();
3553 if (token.value == TOK.int32Literal)
3555 const n = token.unsvalue;
3556 if (n < 1 || n & (n - 1) || ushort.max < n)
3557 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3558 specifier.packalign.set(cast(uint)n);
3559 specifier.packalign.setPack(true);
3560 nextToken();
3562 else
3564 error("alignment value expected, not `%s`", token.toChars());
3565 nextToken();
3568 check(TOK.rightParenthesis);
3570 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3571 * type on the target machine. It's the opposite of __attribute__((packed))
3574 else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3576 specifier.scw |= SCW.xinline;
3577 nextToken();
3579 else if (token.ident == Id._deprecated)
3581 specifier._deprecated = true;
3582 nextToken();
3583 if (token.value == TOK.leftParenthesis) // optional deprecation message
3585 nextToken();
3586 specifier.depMsg = cparseExpression();
3587 check(TOK.rightParenthesis);
3590 else if (token.ident == Id.dllimport)
3592 specifier.dllimport = true;
3593 nextToken();
3595 else if (token.ident == Id.dllexport)
3597 specifier.dllexport = true;
3598 nextToken();
3600 else if (token.ident == Id.naked)
3602 specifier.naked = true;
3603 nextToken();
3605 else if (token.ident == Id.noinline)
3607 specifier.scw |= SCW.xnoinline;
3608 nextToken();
3610 else if (token.ident == Id.noreturn)
3612 specifier.noreturn = true;
3613 nextToken();
3615 else if (token.ident == Id._nothrow)
3617 specifier._nothrow = true;
3618 nextToken();
3620 else if (token.ident == Id._pure)
3622 specifier._pure = true;
3623 nextToken();
3625 else if (token.ident == Id.vector_size)
3627 nextToken();
3628 check(TOK.leftParenthesis);
3629 if (token.value == TOK.int32Literal)
3631 const n = token.unsvalue;
3632 if (n < 1 || n & (n - 1) || ushort.max < n)
3633 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3634 specifier.vector_size = cast(uint) n;
3635 nextToken();
3637 else
3639 error("value for vector_size expected, not `%s`", token.toChars());
3640 nextToken();
3642 check(TOK.rightParenthesis);
3644 else
3646 nextToken();
3647 if (token.value == TOK.leftParenthesis)
3648 cparseParens();
3651 else
3653 nextToken();
3654 if (token.value == TOK.leftParenthesis)
3655 cparseParens();
3659 /*************************
3660 * See if match for GNU attribute name, which may be any identifier,
3661 * storage-class-specifier, type-specifier, or type-qualifier.
3662 * Returns:
3663 * true if a valid GNU attribute name
3665 private bool isGnuAttributeName()
3667 switch (token.value)
3669 case TOK.identifier:
3670 case TOK.static_:
3671 case TOK.unsigned:
3672 case TOK.int64:
3673 case TOK.const_:
3674 case TOK.extern_:
3675 case TOK.register:
3676 case TOK.typedef_:
3677 case TOK.int16:
3678 case TOK.inline:
3679 case TOK._Noreturn:
3680 case TOK.volatile:
3681 case TOK.signed:
3682 case TOK.auto_:
3683 case TOK.restrict:
3684 case TOK._Complex:
3685 case TOK._Thread_local:
3686 case TOK.int32:
3687 case TOK.__int128:
3688 case TOK.char_:
3689 case TOK.float32:
3690 case TOK.float64:
3691 case TOK.void_:
3692 case TOK._Bool:
3693 case TOK._Atomic:
3694 return true;
3696 default:
3697 return false;
3701 /***************************
3702 * Like skipParens(), but consume the tokens.
3704 private void cparseParens()
3706 check(TOK.leftParenthesis);
3707 int parens = 1;
3709 while (1)
3711 switch (token.value)
3713 case TOK.leftParenthesis:
3714 ++parens;
3715 break;
3717 case TOK.rightParenthesis:
3718 --parens;
3719 if (parens < 0)
3721 error("extra right parenthesis");
3722 return;
3724 if (parens == 0)
3726 nextToken();
3727 return;
3729 break;
3731 case TOK.endOfFile:
3732 error("end of file found before right parenthesis");
3733 return;
3735 default:
3736 break;
3738 nextToken();
3743 /******************************************************************************/
3744 /***************************** Struct & Enum Parser ***************************/
3747 /*************************************
3748 * C11 6.7.2.2
3749 * enum-specifier:
3750 * enum identifier (opt) { enumerator-list }
3751 * enum identifier (opt) { enumerator-list , }
3752 * enum identifier
3754 * enumerator-list:
3755 * enumerator
3756 * enumerator-list , enumerator
3758 * enumerator:
3759 * enumeration-constant
3760 * enumeration-constant = constant-expression
3762 * enumeration-constant:
3763 * identifier
3765 * Params:
3766 * symbols = symbols to add enum declaration to
3767 * Returns:
3768 * type of the enum
3770 private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
3772 const loc = token.loc;
3773 nextToken();
3775 /* GNU Extensions
3776 * enum-specifier:
3777 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3778 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3779 * enum gnu-attributes (opt) identifier
3781 Specifier specifier;
3782 specifier.packalign.setDefault();
3783 if (token.value == TOK.__attribute__)
3784 cparseGnuAttributes(specifier);
3786 Identifier tag;
3787 if (token.value == TOK.identifier)
3789 tag = token.ident;
3790 nextToken();
3793 /* clang extension: add optional base type after the identifier
3794 * https://en.cppreference.com/w/cpp/language/enum
3795 * enum Identifier : Type
3797 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3798 AST.Type base = null; // C23 says base type is determined by enum member values
3799 if (token.value == TOK.colon)
3801 nextToken();
3802 base = cparseTypeName();
3805 AST.Dsymbols* members;
3806 if (token.value == TOK.leftCurly)
3808 nextToken();
3809 members = new AST.Dsymbols();
3811 if (token.value == TOK.rightCurly) // C11 6.7.2.2-1
3813 if (tag)
3814 error("no members for `enum %s`", tag.toChars());
3815 else
3816 error("no members for anonymous enum");
3819 while (token.value == TOK.identifier)
3821 auto ident = token.ident; // enumeration-constant
3822 nextToken();
3823 auto mloc = token.loc;
3825 if (token.value == TOK.__attribute__)
3827 /* gnu-attributes can appear here, but just scan and ignore them
3828 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3830 Specifier specifierx;
3831 specifierx.packalign.setDefault();
3832 cparseGnuAttributes(specifierx);
3835 AST.Expression value;
3836 if (token.value == TOK.assign)
3838 nextToken();
3839 value = cparseConstantExp();
3840 // TODO C11 6.7.2.2-2 value must fit into an int
3843 if (token.value == TOK.__attribute__)
3845 /* gnu-attributes can appear here, but just scan and ignore them
3846 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3848 Specifier specifierx;
3849 specifierx.packalign.setDefault();
3850 cparseGnuAttributes(specifierx);
3853 auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
3854 members.push(em);
3856 if (token.value == TOK.comma)
3858 nextToken();
3859 continue;
3861 break;
3863 check(TOK.rightCurly);
3865 /* GNU Extensions
3866 * Parse the postfix gnu-attributes (opt)
3868 if (token.value == TOK.__attribute__)
3869 cparseGnuAttributes(specifier);
3871 else if (!tag)
3872 error("missing `identifier` after `enum`");
3874 /* Need semantic information to determine if this is a declaration,
3875 * redeclaration, or reference to existing declaration.
3876 * Defer to the semantic() pass with a TypeTag.
3878 return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
3881 /*************************************
3882 * C11 6.7.2.1
3883 * Parse struct and union specifiers.
3884 * Parser is advanced to the tag identifier or brace.
3885 * struct-or-union-specifier:
3886 * struct-or-union identifier (opt) { struct-declaration-list }
3887 * struct-or-union identifier
3889 * struct-or-union:
3890 * struct
3891 * union
3893 * struct-declaration-list:
3894 * struct-declaration
3895 * struct-declaration-list struct-declaration
3897 * Params:
3898 * loc = location of `struct` or `union`
3899 * structOrUnion = TOK.struct_ or TOK.union_
3900 * packalign = alignment to use for struct members
3901 * symbols = symbols to add struct-or-union declaration to
3902 * Returns:
3903 * type of the struct
3905 private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
3907 Identifier tag;
3909 if (token.value == TOK.identifier)
3911 tag = token.ident;
3912 nextToken();
3915 AST.Dsymbols* members;
3916 if (token.value == TOK.leftCurly)
3918 nextToken();
3919 members = new AST.Dsymbols(); // so `members` will be non-null even with 0 members
3920 while (token.value != TOK.rightCurly)
3922 cparseStructDeclaration(members);
3924 if (token.value == TOK.endOfFile)
3925 break;
3927 check(TOK.rightCurly);
3929 if ((*members).length == 0) // C11 6.7.2.1-8
3931 /* allow empty structs as an extension
3932 * struct-declarator-list:
3933 * struct-declarator (opt)
3937 else if (!tag)
3938 error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
3940 // many ways and places to declare alignment
3941 if (packalign.isUnknown() && !this.packalign.isUnknown())
3942 packalign.set(this.packalign.get());
3944 /* Need semantic information to determine if this is a declaration,
3945 * redeclaration, or reference to existing declaration.
3946 * Defer to the semantic() pass with a TypeTag.
3948 return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
3951 /*************************************
3952 * C11 6.7.2.1
3953 * Parse a struct declaration member.
3954 * struct-declaration:
3955 * specifier-qualifier-list struct-declarator-list (opt) ;
3956 * static_assert-declaration
3958 * struct-declarator-list:
3959 * struct-declarator
3960 * struct-declarator-list , struct-declarator
3962 * struct-declarator:
3963 * declarator
3964 * declarator (opt) : constant-expression
3965 * Params:
3966 * members = where to put the fields (members)
3968 void cparseStructDeclaration(AST.Dsymbols* members)
3970 //printf("cparseStructDeclaration()\n");
3971 if (token.value == TOK._Static_assert)
3973 auto s = cparseStaticAssert();
3974 members.push(s);
3975 return;
3978 Specifier specifier;
3979 specifier.packalign = this.packalign;
3980 auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
3981 if (!tspec)
3983 error("no type-specifier for struct member");
3984 tspec = AST.Type.tint32;
3986 if (specifier.mod & MOD.xconst)
3988 tspec = toConst(tspec);
3989 specifier.mod = MOD.xnone; // 'used' it
3992 /* If a declarator does not follow, it is unnamed
3994 if (token.value == TOK.semicolon && tspec)
3996 nextToken();
3997 auto tt = tspec.isTypeTag();
3998 if (!tt)
4000 if (auto ti = tspec.isTypeIdentifier())
4002 error("type-specifier omitted before declaration of `%s`", ti.ident.toChars());
4004 return; // legal but meaningless empty declaration
4007 /* If anonymous struct declaration
4008 * struct { ... members ... };
4009 * C11 6.7.2.1-13
4011 if (!tt.id && tt.members)
4013 /* members of anonymous struct are considered members of
4014 * the containing struct
4016 auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
4017 auto s = applySpecifier(ad, specifier);
4018 members.push(s);
4019 return;
4021 if (!tt.id && !tt.members)
4022 return; // already gave error in cparseStruct()
4024 /* `struct tag;` and `struct tag { ... };`
4025 * always result in a declaration in the current scope
4027 // TODO: merge in specifier
4028 auto stag = (tt.tok == TOK.struct_)
4029 ? new AST.StructDeclaration(tt.loc, tt.id, false)
4030 : new AST.UnionDeclaration(tt.loc, tt.id);
4031 stag.members = tt.members;
4032 if (!symbols)
4033 symbols = new AST.Dsymbols();
4034 auto s = applySpecifier(stag, specifier);
4035 symbols.push(s);
4036 return;
4039 while (1)
4041 Identifier id;
4042 AST.Type dt;
4043 if (token.value == TOK.colon)
4045 if (auto ti = tspec.isTypeIdentifier())
4047 error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars());
4048 tspec = AST.Type.tint32;
4051 // C11 6.7.2.1-12 unnamed bit-field
4052 id = Identifier.generateAnonymousId("BitField");
4053 dt = tspec;
4055 else
4057 dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
4058 if (!dt)
4060 panic();
4061 nextToken();
4062 break; // error recovery
4066 AST.Expression width;
4067 if (token.value == TOK.colon)
4069 // C11 6.7.2.1-10 bit-field
4070 nextToken();
4071 width = cparseConstantExp();
4074 /* GNU Extensions
4075 * struct-declarator:
4076 * declarator gnu-attributes (opt)
4077 * declarator (opt) : constant-expression gnu-attributes (opt)
4079 if (token.value == TOK.__attribute__)
4080 cparseGnuAttributes(specifier);
4082 if (!tspec && !specifier.scw && !specifier.mod)
4083 error("specifier-qualifier-list required");
4084 else if (width)
4086 if (specifier.alignExps)
4087 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4088 auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
4089 members.push(s);
4091 else if (id)
4093 if (dt.ty == AST.Tvoid)
4094 error("`void` has no value");
4096 // declare the symbol
4097 // Give member variables an implicit void initializer
4098 auto initializer = new AST.VoidInitializer(token.loc);
4099 AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
4100 s = applySpecifier(s, specifier);
4101 members.push(s);
4104 switch (token.value)
4106 case TOK.identifier:
4107 error("missing comma");
4108 goto default;
4110 case TOK.semicolon:
4111 nextToken();
4112 return;
4114 case TOK.comma:
4115 nextToken();
4116 break;
4118 default:
4119 error("`;` or `,` expected");
4120 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
4121 nextToken();
4122 nextToken();
4123 return;
4129 /******************************************************************************/
4130 /********************************* Lookahead Parser ***************************/
4133 /************************************
4134 * Determine if the scanner is sitting on the start of a declaration.
4135 * Params:
4136 * t = current token of the scanner
4137 * needId = flag with additional requirements for a declaration
4138 * endtok = ending token
4139 * pt = will be set ending token (if not null)
4140 * Returns:
4141 * true at start of a declaration
4143 private bool isCDeclaration(ref Token* pt)
4145 auto t = pt;
4146 //printf("isCDeclaration() %s\n", t.toChars());
4147 if (!isDeclarationSpecifiers(t))
4148 return false;
4150 while (1)
4152 if (t.value == TOK.semicolon)
4154 t = peek(t);
4155 pt = t;
4156 return true;
4158 if (!isCDeclarator(t, DTR.xdirect))
4159 return false;
4160 if (t.value == TOK.asm_)
4162 t = peek(t);
4163 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4164 return false;
4166 if (t.value == TOK.__attribute__)
4168 t = peek(t);
4169 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4170 return false;
4172 if (t.value == TOK.assign)
4174 t = peek(t);
4175 if (!isInitializer(t))
4176 return false;
4178 switch (t.value)
4180 case TOK.comma:
4181 t = peek(t);
4182 break;
4184 case TOK.semicolon:
4185 t = peek(t);
4186 pt = t;
4187 return true;
4189 default:
4190 return false;
4195 /********************************
4196 * See if match for initializer.
4197 * Params:
4198 * pt = starting token, updated to one past end of initializer if true
4199 * Returns:
4200 * true if initializer
4202 private bool isInitializer(ref Token* pt)
4204 //printf("isInitializer()\n");
4205 auto t = pt;
4207 if (t.value == TOK.leftCurly)
4209 if (!skipBraces(t))
4210 return false;
4211 pt = t;
4212 return true;
4215 // skip over assignment-expression, ending before comma or semiColon or EOF
4216 if (!isAssignmentExpression(t))
4217 return false;
4218 pt = t;
4219 return true;
4222 /********************************
4223 * See if match for:
4224 * postfix-expression ( argument-expression-list(opt) )
4225 * Params:
4226 * pt = starting token, updated to one past end of initializer if true
4227 * Returns:
4228 * true if function call
4230 private bool isFunctionCall(ref Token* pt)
4232 //printf("isFunctionCall()\n");
4233 auto t = pt;
4235 if (!isPrimaryExpression(t))
4236 return false;
4237 if (t.value != TOK.leftParenthesis)
4238 return false;
4239 t = peek(t);
4240 while (1)
4242 if (!isAssignmentExpression(t))
4243 return false;
4244 if (t.value == TOK.comma)
4246 t = peek(t);
4247 continue;
4249 if (t.value == TOK.rightParenthesis)
4251 t = peek(t);
4252 break;
4254 return false;
4256 if (t.value != TOK.semicolon)
4257 return false;
4258 pt = t;
4259 return true;
4262 /********************************
4263 * See if match for assignment-expression.
4264 * Params:
4265 * pt = starting token, updated to one past end of assignment-expression if true
4266 * Returns:
4267 * true if assignment-expression
4269 private bool isAssignmentExpression(ref Token* pt)
4271 auto t = pt;
4272 //printf("isAssignmentExpression() %s\n", t.toChars());
4274 /* This doesn't actually check for grammar matching an
4275 * assignment-expression. It just matches ( ) [ ] looking for
4276 * an ending token that would terminate one.
4278 bool any;
4279 while (1)
4281 switch (t.value)
4283 case TOK.comma:
4284 case TOK.semicolon:
4285 case TOK.rightParenthesis:
4286 case TOK.rightBracket:
4287 case TOK.endOfFile:
4288 case TOK.endOfLine:
4289 if (!any)
4290 return false;
4291 break;
4293 case TOK.leftParenthesis:
4294 if (!skipParens(t, &t))
4295 return false;
4297 https://issues.dlang.org/show_bug.cgi?id=22267
4298 If the parser encounters the following
4299 `identifier variableName = (expression);`
4300 the initializer is not identified as such since the parentheses
4301 cause the parser to keep walking indefinitely
4302 (whereas `(1) + 1` would not be affected.).
4304 any = true;
4305 continue;
4307 case TOK.leftBracket:
4308 if (!skipBrackets(t))
4309 return false;
4310 continue;
4312 case TOK.leftCurly:
4313 if (!skipBraces(t))
4314 return false;
4315 continue;
4317 default:
4318 any = true; // assume token was part of an a-e
4319 t = peek(t);
4320 continue;
4322 pt = t;
4323 return true;
4327 /********************************
4328 * See if match for constant-expression.
4329 * Params:
4330 * pt = starting token, updated to one past end of constant-expression if true
4331 * Returns:
4332 * true if constant-expression
4334 private bool isConstantExpression(ref Token* pt)
4336 return isAssignmentExpression(pt);
4339 /********************************
4340 * See if match for declaration-specifiers.
4341 * No errors are diagnosed.
4342 * Params:
4343 * pt = starting token, updated to one past end of declaration-specifiers if true
4344 * Returns:
4345 * true if declaration-specifiers
4347 private bool isDeclarationSpecifiers(ref Token* pt)
4349 //printf("isDeclarationSpecifiers()\n");
4351 auto t = pt;
4353 bool seenType;
4354 bool any;
4355 while (1)
4357 switch (t.value)
4359 // type-specifiers
4360 case TOK.void_:
4361 case TOK.char_:
4362 case TOK.int16:
4363 case TOK.int32:
4364 case TOK.int64:
4365 case TOK.__int128:
4366 case TOK.float32:
4367 case TOK.float64:
4368 case TOK.signed:
4369 case TOK.unsigned:
4370 case TOK._Bool:
4371 //case TOK._Imaginary:
4372 case TOK._Complex:
4373 t = peek(t);
4374 seenType = true;
4375 any = true;
4376 continue;
4378 case TOK.identifier: // typedef-name
4379 if (!seenType)
4381 t = peek(t);
4382 seenType = true;
4383 any = true;
4384 continue;
4386 break;
4388 case TOK.struct_:
4389 case TOK.union_:
4390 case TOK.enum_:
4391 t = peek(t);
4392 if (t.value == TOK.__attribute__ ||
4393 t.value == TOK.__declspec)
4395 t = peek(t);
4396 if (!skipParens(t, &t))
4397 return false;
4399 if (t.value == TOK.identifier)
4401 t = peek(t);
4402 if (t.value == TOK.leftCurly)
4404 if (!skipBraces(t))
4405 return false;
4408 else if (t.value == TOK.leftCurly)
4410 if (!skipBraces(t))
4411 return false;
4413 else
4414 return false;
4415 any = true;
4416 continue;
4418 // storage-class-specifiers
4419 case TOK.typedef_:
4420 case TOK.extern_:
4421 case TOK.static_:
4422 case TOK.__thread:
4423 case TOK._Thread_local:
4424 case TOK.auto_:
4425 case TOK.register:
4427 // function-specifiers
4428 case TOK.inline:
4429 case TOK._Noreturn:
4431 // type-qualifiers
4432 case TOK.const_:
4433 case TOK.volatile:
4434 case TOK.restrict:
4435 case TOK.__stdcall:
4436 t = peek(t);
4437 any = true;
4438 continue;
4440 case TOK._Alignas: // alignment-specifier
4441 case TOK.__declspec: // decl-specifier
4442 case TOK.__attribute__: // attribute-specifier
4443 t = peek(t);
4444 if (!skipParens(t, &t))
4445 return false;
4446 any = true;
4447 continue;
4449 // either atomic-type-specifier or type_qualifier
4450 case TOK._Atomic: // TODO _Atomic ( type-name )
4451 t = peek(t);
4452 if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
4454 auto tsave = t;
4455 t = peek(t);
4456 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4457 { // it's a type-qualifier
4458 t = tsave; // back up parser
4459 any = true;
4460 continue;
4462 t = peek(t); // move past right parenthesis of atomic-type-specifier
4464 any = true;
4465 continue;
4467 default:
4468 break;
4470 break;
4473 if (any)
4475 pt = t;
4476 return true;
4478 return false;
4481 /**************************************
4482 * See if declaration-list is present.
4483 * Returns:
4484 * true if declaration-list is present, even an empty one
4486 bool isDeclarationList(ref Token* pt)
4488 auto t = pt;
4489 while (1)
4491 if (t.value == TOK.leftCurly)
4493 pt = t;
4494 return true;
4496 if (!isCDeclaration(t))
4497 return false;
4501 /*******************************************
4502 * Skip braces.
4503 * Params:
4504 * pt = enters on left brace, set to token past right bracket on true
4505 * Returns:
4506 * true if successful
4508 private bool skipBraces(ref Token* pt)
4510 auto t = pt;
4511 if (t.value != TOK.leftCurly)
4512 return false;
4514 int braces = 0;
4516 while (1)
4518 switch (t.value)
4520 case TOK.leftCurly:
4521 ++braces;
4522 t = peek(t);
4523 continue;
4525 case TOK.rightCurly:
4526 --braces;
4527 if (braces == 0)
4529 pt = peek(t);
4530 return true;
4532 if (braces < 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 * Skip brackets.
4550 * Params:
4551 * pt = enters on left bracket, set to token past right bracket on true
4552 * Returns:
4553 * true if successful
4555 private bool skipBrackets(ref Token* pt)
4557 auto t = pt;
4558 if (t.value != TOK.leftBracket)
4559 return false;
4561 int brackets = 0;
4563 while (1)
4565 switch (t.value)
4567 case TOK.leftBracket:
4568 ++brackets;
4569 t = peek(t);
4570 continue;
4572 case TOK.rightBracket:
4573 --brackets;
4574 if (brackets == 0)
4576 pt = peek(t);
4577 return true;
4579 if (brackets < 0)
4580 return false;
4582 t = peek(t);
4583 continue;
4585 case TOK.endOfFile:
4586 return false;
4588 default:
4589 t = peek(t);
4590 continue;
4595 /*********************************
4596 * Check to see if tokens starting with *pt form a declarator.
4597 * Params:
4598 * pt = pointer to starting token, updated to point past declarator if true is returned
4599 * declarator = declarator kind
4600 * Returns:
4601 * true if it does
4603 private bool isCDeclarator(ref Token* pt, DTR declarator)
4605 //printf("isCDeclarator()\n");
4606 auto t = pt;
4607 while (1)
4609 if (t.value == TOK.mul) // pointer
4611 t = peek(t);
4612 if (!isTypeQualifierList(t))
4613 return false;
4615 else
4616 break;
4619 if (t.value == TOK.identifier)
4621 if (declarator == DTR.xabstract)
4622 return false;
4623 t = peek(t);
4625 else if (t.value == TOK.leftParenthesis)
4627 t = peek(t);
4628 if (t.value == TOK.__stdcall)
4629 t = peek(t);
4630 if (!isCDeclarator(t, declarator))
4631 return false;
4632 if (t.value != TOK.rightParenthesis)
4633 return false;
4634 t = peek(t);
4636 else if (declarator == DTR.xdirect)
4638 return false;
4641 while (1)
4643 if (t.value == TOK.leftBracket)
4645 if (!skipBrackets(t))
4646 return false;
4648 else if (t.value == TOK.leftParenthesis)
4650 if (!skipParens(t, &t))
4651 return false;
4653 else
4654 break;
4656 pt = t;
4657 return true;
4660 /***************************
4661 * Is this the start of a type-qualifier-list?
4662 * (Can be empty.)
4663 * Params:
4664 * pt = first token; updated with past end of type-qualifier-list if true
4665 * Returns:
4666 * true if start of type-qualifier-list
4668 private bool isTypeQualifierList(ref Token* pt)
4670 auto t = pt;
4671 while (1)
4673 switch (t.value)
4675 case TOK.const_:
4676 case TOK.restrict:
4677 case TOK.volatile:
4678 case TOK._Atomic:
4679 case TOK.__stdcall:
4680 t = peek(t);
4681 continue;
4683 default:
4684 break;
4686 break;
4688 pt = t;
4689 return true;
4692 /***************************
4693 * Is this the start of a type-name?
4694 * Params:
4695 * pt = first token; updated with past end of type-name if true
4696 * Returns:
4697 * true if start of type-name
4699 private bool isTypeName(ref Token* pt)
4701 auto t = pt;
4702 //printf("isTypeName() %s\n", t.toChars());
4703 if (!isSpecifierQualifierList(t))
4704 return false;
4705 if (!isCDeclarator(t, DTR.xabstract))
4706 return false;
4707 if (t.value != TOK.rightParenthesis)
4708 return false;
4709 pt = t;
4710 return true;
4713 /***************************
4714 * Is this the start of a specifier-qualifier-list?
4715 * Params:
4716 * pt = first token; updated with past end of specifier-qualifier-list if true
4717 * Returns:
4718 * true if start of specifier-qualifier-list
4720 private bool isSpecifierQualifierList(ref Token* pt)
4722 auto t = pt;
4723 bool result;
4724 while (1)
4726 switch (t.value)
4728 // Type Qualifiers
4729 case TOK.const_:
4730 case TOK.restrict:
4731 case TOK.volatile:
4732 case TOK.__stdcall:
4734 // Type Specifiers
4735 case TOK.char_:
4736 case TOK.signed:
4737 case TOK.unsigned:
4738 case TOK.int16:
4739 case TOK.int32:
4740 case TOK.int64:
4741 case TOK.__int128:
4742 case TOK.float32:
4743 case TOK.float64:
4744 case TOK.void_:
4745 case TOK._Bool:
4746 //case TOK._Imaginary: // ? missing in Spec
4747 case TOK._Complex:
4748 t = peek(t);
4749 break;
4751 case TOK.identifier:
4752 // Use typedef table to disambiguate
4753 if (isTypedef(t.ident))
4755 t = peek(t);
4756 break;
4758 else
4760 return false;
4763 // struct-or-union-specifier
4764 // enum-specifier
4765 case TOK.struct_:
4766 case TOK.union_:
4767 case TOK.enum_:
4768 t = peek(t);
4769 if (t.value == TOK.identifier)
4771 t = peek(t);
4772 if (t.value == TOK.leftCurly)
4774 if (!skipBraces(t))
4775 return false;
4778 else if (t.value == TOK.leftCurly)
4780 if (!skipBraces(t))
4781 return false;
4783 else
4784 return false;
4785 break;
4787 // atomic-type-specifier
4788 case TOK._Atomic:
4789 case TOK.typeof_:
4790 case TOK.__attribute__:
4791 t = peek(t);
4792 if (t.value != TOK.leftParenthesis ||
4793 !skipParens(t, &t))
4794 return false;
4795 break;
4797 default:
4798 if (result)
4799 pt = t;
4800 return result;
4802 result = true;
4806 /************************************
4807 * Looking at the leading left parenthesis, and determine if it is
4808 * either of the following:
4809 * ( type-name ) cast-expression
4810 * ( type-name ) { initializer-list }
4811 * as opposed to:
4812 * ( expression )
4813 * Params:
4814 * pt = starting token, updated to one past end of constant-expression if true
4815 * afterParenType = true if already seen `( type-name )`
4816 * Returns:
4817 * true if matches ( type-name ) ...
4819 private bool isCastExpression(ref Token* pt, bool afterParenType = false)
4821 enum log = false;
4822 if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
4823 auto t = pt;
4824 switch (t.value)
4826 case TOK.leftParenthesis:
4827 auto tk = peek(t); // move past left parenthesis
4828 if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
4830 if (afterParenType)
4831 goto default; // could be ( type-name ) ( unary-expression )
4832 return false;
4834 tk = peek(tk); // move past right parenthesis
4836 if (tk.value == TOK.leftCurly)
4838 // ( type-name ) { initializer-list }
4839 if (!isInitializer(tk))
4841 return false;
4843 t = tk;
4844 break;
4847 if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
4849 return false; // (type-name)() is not a cast (it might be a function call)
4852 if (!isCastExpression(tk, true))
4854 if (afterParenType) // could be ( type-name ) ( unary-expression )
4855 goto default; // where unary-expression also matched type-name
4856 return true;
4858 // ( type-name ) cast-expression
4859 t = tk;
4860 break;
4862 default:
4863 if (!afterParenType || !isUnaryExpression(t, afterParenType))
4865 return false;
4867 // if we've already seen ( type-name ), then this is a cast
4868 break;
4870 pt = t;
4871 if (log) printf("isCastExpression true\n");
4872 return true;
4875 /********************************
4876 * See if match for unary-expression.
4877 * Params:
4878 * pt = starting token, updated to one past end of constant-expression if true
4879 * afterParenType = true if already seen ( type-name ) of a cast-expression
4880 * Returns:
4881 * true if unary-expression
4883 private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
4885 auto t = pt;
4886 switch (t.value)
4888 case TOK.plusPlus:
4889 case TOK.minusMinus:
4890 t = peek(t);
4891 if (!isUnaryExpression(t, afterParenType))
4892 return false;
4893 break;
4895 case TOK.and:
4896 case TOK.mul:
4897 case TOK.min:
4898 case TOK.add:
4899 case TOK.not:
4900 case TOK.tilde:
4901 t = peek(t);
4902 if (!isCastExpression(t, afterParenType))
4903 return false;
4904 break;
4906 case TOK.sizeof_:
4907 t = peek(t);
4908 if (t.value == TOK.leftParenthesis)
4910 auto tk = peek(t);
4911 if (isTypeName(tk))
4913 if (tk.value != TOK.rightParenthesis)
4914 return false;
4915 t = peek(tk);
4916 break;
4919 if (!isUnaryExpression(t, afterParenType))
4920 return false;
4921 break;
4923 case TOK._Alignof:
4924 t = peek(t);
4925 if (t.value != TOK.leftParenthesis)
4926 return false;
4927 t = peek(t);
4928 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4929 return false;
4930 break;
4932 default:
4933 // Compound literals are handled by cast and sizeof expressions,
4934 // so be content with just seeing a primary expression.
4935 if (!isPrimaryExpression(t))
4936 return false;
4937 break;
4939 pt = t;
4940 return true;
4943 /********************************
4944 * See if match for primary-expression.
4945 * Params:
4946 * pt = starting token, updated to one past end of constant-expression if true
4947 * Returns:
4948 * true if primary-expression
4950 private bool isPrimaryExpression(ref Token* pt)
4952 auto t = pt;
4953 switch (t.value)
4955 case TOK.identifier:
4956 case TOK.charLiteral:
4957 case TOK.wcharLiteral:
4958 case TOK.dcharLiteral:
4959 case TOK.int32Literal:
4960 case TOK.uns32Literal:
4961 case TOK.int64Literal:
4962 case TOK.uns64Literal:
4963 case TOK.float32Literal:
4964 case TOK.float64Literal:
4965 case TOK.float80Literal:
4966 case TOK.imaginary32Literal:
4967 case TOK.imaginary64Literal:
4968 case TOK.imaginary80Literal:
4969 case TOK.string_:
4970 t = peek(t);
4971 break;
4973 case TOK.leftParenthesis:
4974 // ( expression )
4975 if (!skipParens(t, &t))
4976 return false;
4977 break;
4979 case TOK._Generic:
4980 t = peek(t);
4981 if (!skipParens(t, &t))
4982 return false;
4983 break;
4985 default:
4986 return false;
4988 pt = t;
4989 return true;
4993 /******************************************************************************/
4994 /********************************* More ***************************************/
4997 /**************
4998 * Declaration context
5000 enum LVL
5002 global = 1, /// global
5003 parameter = 2, /// function parameter (declarations for function identifier-list)
5004 prototype = 4, /// function prototype
5005 local = 8, /// local
5006 member = 0x10, /// struct member
5009 /// Types of declarator to parse
5010 enum DTR
5012 xdirect = 1, /// C11 6.7.6 direct-declarator
5013 xabstract = 2, /// C11 6.7.7 abstract-declarator
5014 xparameter = 3, /// parameter declarator may be either direct or abstract
5017 /// C11 6.7.1 Storage-class specifiers
5018 enum SCW : uint
5020 xnone = 0,
5021 xtypedef = 1,
5022 xextern = 2,
5023 xstatic = 4,
5024 x_Thread_local = 8,
5025 xauto = 0x10,
5026 xregister = 0x20,
5027 // C11 6.7.4 Function specifiers
5028 xinline = 0x40,
5029 x_Noreturn = 0x80,
5031 xnoinline = 0x100,
5034 /// C11 6.7.3 Type qualifiers
5035 enum MOD : uint
5037 xnone = 0,
5038 xconst = 1,
5039 xvolatile = 2,
5040 xrestrict = 4,
5041 x_Atomic = 8,
5042 x__stdcall = 0x10, // Windows linkage extension
5045 /**********************************
5046 * Aggregate for all the various specifiers
5048 struct Specifier
5050 bool noreturn; /// noreturn attribute
5051 bool naked; /// naked attribute
5052 bool _nothrow; /// nothrow attribute
5053 bool _pure; /// pure attribute
5054 bool dllimport; /// dllimport attribute
5055 bool dllexport; /// dllexport attribute
5056 bool _deprecated; /// deprecated attribute
5057 AST.Expression depMsg; /// deprecated message
5058 uint vector_size; /// positive power of 2 multipe of base type size
5060 SCW scw; /// storage-class specifiers
5061 MOD mod; /// type qualifiers
5062 AST.Expressions* alignExps; /// alignment
5063 structalign_t packalign; /// #pragma pack alignment value
5066 /***********************
5067 * Convert from C specifiers to D storage class
5068 * Params:
5069 * level = declaration context
5070 * specifier = specifiers, context, etc.
5071 * Returns:
5072 * corresponding D storage class
5074 StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
5076 StorageClass stc;
5077 if (specifier.scw & SCW.x_Thread_local)
5079 if (level == LVL.global)
5081 if (specifier.scw & SCW.xextern)
5082 stc = AST.STC.extern_;
5083 else if (specifier.scw & SCW.xstatic)
5084 stc = AST.STC.static_;
5086 else if (level == LVL.local)
5088 if (specifier.scw & SCW.xextern)
5089 stc = AST.STC.extern_;
5090 else if (specifier.scw & SCW.xstatic)
5091 stc = AST.STC.static_;
5093 else if (level == LVL.member)
5095 if (specifier.scw & SCW.xextern)
5096 stc = AST.STC.extern_;
5097 else if (specifier.scw & SCW.xstatic)
5098 stc = AST.STC.static_;
5101 else
5103 if (level == LVL.global)
5105 if (specifier.scw & SCW.xextern)
5106 stc = AST.STC.extern_ | AST.STC.gshared;
5107 else if (specifier.scw & SCW.xstatic)
5108 stc = AST.STC.gshared | AST.STC.static_;
5109 else
5110 stc = AST.STC.gshared;
5112 else if (level == LVL.local)
5114 if (specifier.scw & SCW.xextern)
5115 stc = AST.STC.extern_ | AST.STC.gshared;
5116 else if (specifier.scw & SCW.xstatic)
5117 stc = AST.STC.gshared;
5118 else if (specifier.scw & SCW.xregister)
5119 stc = AST.STC.register;
5121 else if (level == LVL.parameter)
5123 if (specifier.scw & SCW.xregister)
5124 stc = AST.STC.register | AST.STC.parameter;
5125 else
5126 stc = AST.STC.parameter;
5128 else if (level == LVL.member)
5130 if (specifier.scw & SCW.xextern)
5131 stc = AST.STC.extern_ | AST.STC.gshared;
5132 else if (specifier.scw & SCW.xstatic)
5133 stc = AST.STC.gshared;
5136 if (specifier._deprecated && !specifier.depMsg)
5137 stc |= AST.STC.deprecated_;
5138 return stc;
5141 /***********************
5142 * Add attributes from Specifier to function
5143 * Params:
5144 * fd = function to apply them to
5145 * specifier = specifiers
5147 void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
5149 fd.isNaked = specifier.naked;
5150 fd.dllImport = specifier.dllimport;
5151 fd.dllExport = specifier.dllexport;
5153 if (specifier.scw & SCW.xnoinline)
5154 fd.inlining = PINLINE.never;
5155 else if (specifier.scw & SCW.xinline)
5156 fd.inlining = PINLINE.always;
5159 /***********************
5160 * Add attributes from Specifier to variable
5161 * Params:
5162 * vd = function to apply them to
5163 * specifier = specifiers
5165 void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
5167 vd.dllImport = specifier.dllimport;
5168 vd.dllExport = specifier.dllexport;
5171 /***********************
5172 * Return suitable signed integer type for the given size
5173 * Params:
5174 * size = size of type
5175 * Returns:
5176 * corresponding signed D integer type
5178 private AST.Type integerTypeForSize(ubyte size)
5180 if (size <= 1)
5181 return AST.Type.tint8;
5182 if (size <= 2)
5183 return AST.Type.tint16;
5184 if (size <= 4)
5185 return AST.Type.tint32;
5186 if (size <= 8)
5187 return AST.Type.tint64;
5188 if (size == 16)
5190 error("__int128 not supported");
5191 return AST.Type.terror;
5193 error("unsupported integer type");
5194 return AST.Type.terror;
5197 /***********************
5198 * Return suitable unsigned integer type for the given size
5199 * Params:
5200 * size = size of type
5201 * Returns:
5202 * corresponding unsigned D integer type
5204 private AST.Type unsignedTypeForSize(ubyte size)
5206 if (size <= 1)
5207 return AST.Type.tuns8;
5208 if (size <= 2)
5209 return AST.Type.tuns16;
5210 if (size <= 4)
5211 return AST.Type.tuns32;
5212 if (size <= 8)
5213 return AST.Type.tuns64;
5214 if (size == 16)
5216 error("unsigned __int128 not supported");
5217 return AST.Type.terror;
5219 error("unsupported integer type");
5220 return AST.Type.terror;
5223 /***********************
5224 * Return suitable D float type for C `long double`
5225 * Params:
5226 * flags = kind of float to return (real, imaginary, complex).
5227 * Returns:
5228 * corresponding D type
5230 private AST.Type realType(RTFlags flags)
5232 if (long_doublesize == AST.Type.tfloat80.size())
5234 // On GDC and LDC, D `real` types map to C `long double`, so never
5235 // return a double type when real.sizeof == double.sizeof.
5236 final switch (flags)
5238 case RTFlags.realfloat: return AST.Type.tfloat80;
5239 case RTFlags.imaginary: return AST.Type.timaginary80;
5240 case RTFlags.complex: return AST.Type.tcomplex80;
5243 else
5245 final switch (flags)
5247 case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
5248 case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
5249 case RTFlags.complex: return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
5254 /**************
5255 * Flags for realType
5257 private enum RTFlags
5259 realfloat,
5260 imaginary,
5261 complex,
5264 /********************
5265 * C11 6.4.2.2 Create declaration to predefine __func__
5266 * `static const char __func__[] = " function-name ";`
5267 * Params:
5268 * loc = location for this declaration
5269 * id = identifier of function
5270 * Returns:
5271 * statement representing the declaration of __func__
5273 private AST.Statement createFuncName(Loc loc, Identifier id)
5275 const fn = id.toString(); // function-name
5276 auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
5277 auto ifn = new AST.ExpInitializer(loc, efn);
5278 auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
5279 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
5280 efn.type = tfn.immutableOf();
5281 efn.committed = true;
5282 auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
5283 auto e = new AST.DeclarationExp(loc, sfn);
5284 return new AST.ExpStatement(loc, e);
5287 /************************
5288 * After encountering an error, scan forward until a right brace or ; is found
5289 * or the end of the file.
5291 void panic()
5293 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
5294 nextToken();
5297 /**************************
5298 * Apply `const` to a type.
5299 * Params:
5300 * t = type to add const to
5301 * Returns:
5302 * resulting type
5304 private AST.Type toConst(AST.Type t)
5306 // `const` is always applied to the return type, not the
5307 // type function itself.
5308 if (auto tf = t.isTypeFunction())
5309 tf.next = tf.next.addSTC(STC.const_);
5310 else if (auto tt = t.isTypeTag())
5311 tt.mod |= MODFlags.const_;
5312 else
5314 /* Ignore const if the result would be const pointer to mutable
5316 auto tn = t.nextOf();
5317 if (!tn || tn.isConst())
5318 t = t.addSTC(STC.const_);
5320 return t;
5323 /***************************
5324 * Apply specifier to a Dsymbol.
5325 * Params:
5326 * s = Dsymbol
5327 * specifier = specifiers to apply
5328 * Returns:
5329 * Dsymbol with specifiers applied
5331 private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
5333 //printf("applySpecifier() %s\n", s.toChars());
5334 if (specifier._deprecated)
5336 if (specifier.depMsg)
5338 // Wrap declaration in a DeprecatedDeclaration
5339 auto decls = new AST.Dsymbols(1);
5340 (*decls)[0] = s;
5341 s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
5345 if (specifier.alignExps)
5347 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5348 // Wrap declaration in an AlignDeclaration
5349 auto decls = new AST.Dsymbols(1);
5350 (*decls)[0] = s;
5351 s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
5353 else if (!specifier.packalign.isDefault() && !specifier.packalign.isUnknown())
5355 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5356 // Wrap #pragma pack in an AlignDeclaration
5357 auto decls = new AST.Dsymbols(1);
5358 (*decls)[0] = s;
5359 s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
5361 return s;
5366 /******************************************************************************/
5367 /************************** typedefTab symbol table ***************************/
5370 /********************************
5371 * Determines if type t is a function type.
5372 * Params:
5373 * t = type to test
5374 * Returns:
5375 * true if it represents a function
5377 bool isFunctionTypedef(AST.Type t)
5379 //printf("isFunctionTypedef() %s\n", t.toChars());
5380 if (t.isTypeFunction())
5381 return true;
5382 if (auto tid = t.isTypeIdentifier())
5384 auto pt = lookupTypedef(tid.ident);
5385 if (pt && *pt)
5387 return (*pt).isTypeFunction() !is null;
5390 return false;
5393 /********************************
5394 * Determine if `id` is a symbol for a Typedef.
5395 * Params:
5396 * id = possible typedef
5397 * Returns:
5398 * true if id is a Type
5400 bool isTypedef(Identifier id)
5402 auto pt = lookupTypedef(id);
5403 return (pt && *pt);
5406 /*******************************
5407 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5408 * Params: id = identifier for non-typedef symbol
5410 void insertIdToTypedefTab(Identifier id)
5412 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5413 if (isTypedef(id)) // if existing typedef
5415 /* Add id as null, so we can later distinguish it from a non-null typedef
5417 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5418 tab[cast(void*)id] = cast(void*)null;
5422 /*******************************
5423 * Add `id` to typedefTab[]
5424 * Params:
5425 * id = identifier for typedef symbol
5426 * t = type of the typedef symbol
5428 void insertTypedefToTypedefTab(Identifier id, AST.Type t)
5430 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5431 if (auto tid = t.isTypeIdentifier())
5433 // Try to resolve the TypeIdentifier to its type
5434 auto pt = lookupTypedef(tid.ident);
5435 if (pt && *pt)
5436 t = *pt;
5438 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5439 tab[cast(void*)id] = cast(void*)t;
5440 typedefTab[$ - 1] = cast(void*)tab;
5443 /*********************************
5444 * Lookup id in typedefTab[].
5445 * Returns:
5446 * if not found, then null.
5447 * if found, then Type*. Deferencing it will yield null if it is not
5448 * a typedef, and a type if it is a typedef.
5450 AST.Type* lookupTypedef(Identifier id)
5452 foreach_reverse (tab; typedefTab[])
5454 if (auto pt = cast(void*)id in cast(void*[void*])tab)
5456 return cast(AST.Type*)pt;
5459 return null; // not found
5464 /******************************************************************************/
5465 /********************************* Directive Parser ***************************/
5468 override bool parseSpecialTokenSequence()
5470 Token n;
5471 scan(&n);
5472 if (n.value == TOK.int32Literal)
5474 poundLine(n, true);
5475 return true;
5477 if (n.value == TOK.identifier)
5479 if (n.ident == Id.line)
5481 poundLine(n, false);
5482 return true;
5484 else if (defines && (n.ident == Id.define || n.ident == Id.undef))
5486 /* Append this line to `defines`.
5487 * Not canonicalizing it - assume it already is
5489 defines.writeByte('#');
5490 defines.writestring(n.ident.toString());
5491 skipToNextLine(defines);
5492 defines.writeByte('\n');
5493 return true;
5495 else if (n.ident == Id.__pragma)
5497 pragmaDirective(scanloc);
5498 return true;
5500 else if (n.ident == Id.ident) // #ident "string"
5502 scan(&n);
5503 if (n.value == TOK.string_ && n.ptr[0] == '"' && n.postfix == 0)
5505 /* gcc inserts string into the .comment section in the object file.
5506 * Just ignore it for now, but can support it later by writing
5507 * the string to obj_exestr()
5509 //auto comment = n.ustring;
5511 scan(&n);
5512 if (n.value == TOK.endOfFile || n.value == TOK.endOfLine)
5513 return true;
5515 error("\"string\" expected after `#ident`");
5516 return false;
5519 if (n.ident != Id.undef)
5520 error("C preprocessor directive `#%s` is not supported", n.toChars());
5521 return false;
5524 /*********************************************
5525 * VC __pragma
5526 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5527 * Scanner is on the `__pragma`
5528 * Params:
5529 * startloc = location to use for error messages
5531 private void uupragmaDirective(const ref Loc startloc)
5533 const loc = startloc;
5534 nextToken(); // move past __pragma
5535 if (token.value != TOK.leftParenthesis)
5537 error(loc, "left parenthesis expected to follow `__pragma` instead of `%s`", token.toChars());
5538 nextToken();
5539 return;
5541 nextToken();
5543 if (token.value == TOK.identifier)
5545 if (token.ident == Id.pack)
5546 pragmaPack(startloc, false);
5547 else
5549 nextToken();
5550 if (token.value == TOK.leftParenthesis)
5551 cparseParens();
5555 else if (token.value == TOK.endOfFile)
5558 else if (token.value == TOK.rightParenthesis)
5561 else
5562 error(loc, "unrecognized `__pragma(%s)`", token.toChars());
5564 if (token.value != TOK.rightParenthesis)
5566 error(loc, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token.toChars());
5567 return;
5569 nextToken();
5572 /*********************************************
5573 * C11 6.10.6 Pragma directive
5574 * # pragma pp-tokens(opt) new-line
5575 * The C preprocessor sometimes leaves pragma directives in
5576 * the preprocessed output. Ignore them.
5577 * Upon return, p is at start of next line.
5579 private void pragmaDirective(const ref Loc loc)
5581 Token n;
5582 scan(&n);
5583 if (n.value == TOK.identifier && n.ident == Id.pack)
5584 return pragmaPack(loc, true);
5585 if (n.value != TOK.endOfLine)
5586 skipToNextLine();
5589 /*********
5590 * # pragma pack
5591 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5592 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5593 * Scanner is on the `pack`
5594 * Params:
5595 * startloc = location to use for error messages
5596 * useScan = use scan() to retrieve next token, instead of nextToken()
5598 private void pragmaPack(const ref Loc startloc, bool useScan)
5600 const loc = startloc;
5602 /* Pull tokens from scan() or nextToken()
5604 void scan(Token* t)
5606 if (useScan)
5608 Lexer.scan(t);
5610 else
5612 nextToken();
5613 *t = token;
5617 Token n;
5618 scan(&n);
5619 if (n.value != TOK.leftParenthesis)
5621 error(loc, "left parenthesis expected to follow `#pragma pack`");
5622 if (n.value != TOK.endOfLine)
5623 skipToNextLine();
5624 return;
5627 void closingParen()
5629 if (n.value != TOK.rightParenthesis)
5631 error(loc, "right parenthesis expected to close `#pragma pack(`");
5633 if (n.value != TOK.endOfLine)
5634 skipToNextLine();
5637 void setPackAlign(ref const Token t)
5639 const n = t.unsvalue;
5640 if (n < 1 || n & (n - 1) || ushort.max < n)
5641 error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
5642 packalign.set(cast(uint)n);
5643 packalign.setPack(true);
5646 scan(&n);
5648 if (!records)
5650 records = new Array!Identifier;
5651 packs = new Array!structalign_t;
5654 /* # pragma pack ( show )
5656 if (n.value == TOK.identifier && n.ident == Id.show)
5658 if (packalign.isDefault())
5659 eSink.warning(startloc, "current pack attribute is default");
5660 else
5661 eSink.warning(startloc, "current pack attribute is %d", packalign.get());
5662 scan(&n);
5663 return closingParen();
5665 /* # pragma pack ( push )
5666 * # pragma pack ( push , identifier )
5667 * # pragma pack ( push , integer )
5668 * # pragma pack ( push , identifier , integer )
5670 if (n.value == TOK.identifier && n.ident == Id.push)
5672 scan(&n);
5673 Identifier record = null;
5674 if (n.value == TOK.comma)
5676 scan(&n);
5677 if (n.value == TOK.identifier)
5679 record = n.ident;
5680 scan(&n);
5681 if (n.value == TOK.comma)
5683 scan(&n);
5684 if (n.value == TOK.int32Literal)
5686 setPackAlign(n);
5687 scan(&n);
5689 else
5690 error(loc, "alignment value expected, not `%s`", n.toChars());
5693 else if (n.value == TOK.int32Literal)
5695 setPackAlign(n);
5696 scan(&n);
5698 else
5699 error(loc, "alignment value expected, not `%s`", n.toChars());
5701 this.records.push(record);
5702 this.packs.push(packalign);
5703 return closingParen();
5705 /* # pragma pack ( pop )
5706 * # pragma pack ( pop PopList )
5707 * PopList :
5708 * , IdentifierOrInteger
5709 * , IdentifierOrInteger PopList
5710 * IdentifierOrInteger:
5711 * identifier
5712 * integer
5714 if (n.value == TOK.identifier && n.ident == Id.pop)
5716 scan(&n);
5717 size_t len = this.records.length;
5718 if (n.value == TOK.rightParenthesis) // #pragma pack ( pop )
5720 if (len == 0) // nothing to pop
5721 return closingParen();
5723 this.records.setDim(len - 1);
5724 this.packs.setDim(len - 1);
5725 if (len == 1) // stack is now empty
5726 packalign.setDefault();
5727 else
5728 packalign = (*this.packs)[len - 1];
5729 return closingParen();
5731 while (n.value == TOK.comma) // #pragma pack ( pop ,
5733 scan(&n);
5734 if (n.value == TOK.identifier)
5736 /* pragma pack(pop, identifier
5737 * Pop until identifier is found, pop that one too, and set
5738 * alignment to the new top of the stack.
5739 * If identifier is not found, do nothing.
5741 for ( ; len; --len)
5743 if ((*this.records)[len - 1] == n.ident)
5745 this.records.setDim(len - 1);
5746 this.packs.setDim(len - 1);
5747 if (len > 1)
5748 packalign = (*this.packs)[len - 2];
5749 else
5750 packalign.setDefault(); // stack empty, use default
5751 break;
5754 scan(&n);
5756 else if (n.value == TOK.int32Literal)
5758 setPackAlign(n);
5759 scan(&n);
5761 else
5763 error(loc, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n.toChars());
5764 scan(&n);
5767 return closingParen();
5769 /* # pragma pack ( integer )
5770 * Sets alignment to integer
5772 if (n.value == TOK.int32Literal)
5774 setPackAlign(n);
5775 scan(&n);
5776 return closingParen();
5778 /* # pragma pack ( )
5779 * Sets alignment to default
5781 if (n.value == TOK.rightParenthesis)
5783 packalign.setDefault();
5784 return closingParen();
5787 error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
5788 if (n.value != TOK.endOfLine)
5789 skipToNextLine();
5794 /******************************************************************************/
5795 /********************************* #define Parser *****************************/
5799 * Go through the #define's in the defines buffer and see what we can convert
5800 * to Dsymbols, which are then appended to symbols[]
5802 void addDefines()
5804 if (!defines || defines.length < 10) // minimum length of a #define line
5805 return;
5806 OutBuffer* buf = defines;
5807 defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5808 // from appending to slice[]
5809 const length = buf.length;
5810 buf.writeByte(0);
5811 auto slice = buf.peekChars()[0 .. length];
5812 resetDefineLines(slice); // reset lexer
5813 auto save = eSink;
5814 auto eLatch = new ErrorSinkLatch();
5815 eSink = eLatch;
5817 const(char)* endp = &slice[length - 7];
5819 size_t[void*] defineTab; // hash table of #define's turned into Symbol's
5820 // indexed by Identifier, returns index into symbols[]
5821 // The memory for this is leaked
5823 void addVar(AST.Dsymbol s)
5825 //printf("addVar() %s\n", s.toChars());
5826 if (auto v = s.isVarDeclaration())
5827 v.isCmacro(true); // mark it as coming from a C #define
5828 /* If it's already defined, replace the earlier
5829 * definition
5831 if (size_t* pd = cast(void*)s.ident in defineTab)
5833 //printf("replacing %s\n", v.toChars());
5834 (*symbols)[*pd] = s;
5835 return;
5837 defineTab[cast(void*)s.ident] = symbols.length;
5838 symbols.push(s);
5841 while (p < endp)
5843 if (p[0 .. 7] == "#define")
5845 p += 7;
5846 nextToken();
5847 //printf("define %s\n", token.toChars());
5848 if (token.value == TOK.identifier)
5850 auto id = token.ident;
5851 const params = *p == '(';
5852 nextToken();
5854 AST.Type t;
5856 switch (token.value)
5858 case TOK.endOfLine: // #define identifier
5859 nextDefineLine();
5860 continue;
5862 case TOK.int32Literal:
5863 case TOK.charLiteral: t = AST.Type.tint32; goto Linteger;
5864 case TOK.wcharLiteral: t = AST.Type.tuns16; goto Linteger;
5865 case TOK.dcharLiteral:
5866 case TOK.uns32Literal: t = AST.Type.tuns32; goto Linteger;
5867 case TOK.int64Literal: t = AST.Type.tint64; goto Linteger;
5868 case TOK.uns64Literal: t = AST.Type.tuns64; goto Linteger;
5870 Linteger:
5871 const intvalue = token.intvalue;
5872 nextToken();
5873 if (token.value == TOK.endOfLine)
5875 /* Declare manifest constant:
5876 * enum id = intvalue;
5878 AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
5879 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5880 addVar(v);
5881 nextDefineLine();
5882 continue;
5884 break;
5886 case TOK.float32Literal: t = AST.Type.tfloat32; goto Lfloat;
5887 case TOK.float64Literal: t = AST.Type.tfloat64; goto Lfloat;
5888 case TOK.float80Literal: t = AST.Type.tfloat80; goto Lfloat;
5889 case TOK.imaginary32Literal: t = AST.Type.timaginary32; goto Lfloat;
5890 case TOK.imaginary64Literal: t = AST.Type.timaginary64; goto Lfloat;
5891 case TOK.imaginary80Literal: t = AST.Type.timaginary80; goto Lfloat;
5893 Lfloat:
5894 const floatvalue = token.floatvalue;
5895 nextToken();
5896 if (token.value == TOK.endOfLine)
5898 /* Declare manifest constant:
5899 * enum id = floatvalue;
5901 AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
5902 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5903 addVar(v);
5904 nextDefineLine();
5905 continue;
5907 break;
5909 case TOK.string_:
5910 const str = token.ustring;
5911 const len = token.len;
5912 const postfix = token.postfix;
5913 nextToken();
5914 if (token.value == TOK.endOfLine)
5916 /* Declare manifest constant:
5917 * enum id = "string";
5919 AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
5920 auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5921 addVar(v);
5922 nextDefineLine();
5923 continue;
5925 break;
5927 case TOK.leftParenthesis:
5928 /* Look for:
5929 * #define ID ( expression )
5930 * and rewrite it to a template function:
5931 * auto ID()() { return expression; }
5933 if (params)
5934 break; // no parameters
5935 nextToken();
5936 eLatch.sawErrors = false;
5937 auto exp = cparseExpression();
5938 if (eLatch.sawErrors) // parsing errors
5939 break; // abandon this #define
5940 if (token.value != TOK.rightParenthesis)
5941 break;
5942 nextToken();
5943 if (token.value != TOK.endOfLine)
5944 break;
5945 auto ret = new AST.ReturnStatement(exp.loc, exp);
5946 auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, 0);
5947 StorageClass stc = STC.auto_;
5948 auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
5949 auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0);
5950 fd.fbody = ret;
5951 AST.Dsymbols* decldefs = new AST.Dsymbols();
5952 decldefs.push(fd);
5953 AST.TemplateParameters* tpl = new AST.TemplateParameters();
5954 AST.Expression constraint = null;
5955 auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, constraint, decldefs, false);
5956 addVar(tempdecl);
5957 nextDefineLine();
5958 continue;
5960 default:
5961 break;
5964 skipToNextLine();
5966 else
5968 scan(&token);
5969 if (token.value != TOK.endOfLine)
5971 skipToNextLine();
5974 nextDefineLine();
5977 eSink = save;
5978 defines = buf;