2 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
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
16 import core
.stdc
.stdio
;
17 import core
.stdc
.string
: memcpy
;
22 import dmd
.identifier
;
26 import dmd
.root
.array
;
27 import dmd
.common
.outbuffer
;
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;
42 structalign_t packalign
; // current state of #pragma pack alignment
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().
68 extern (D
) this(TARGET
)(AST
.Module _module
, const(char)[] input
, bool doDocComment
,
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");
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.
98 * external-declaration
99 * translation-unit external-declaration
101 * external-declaration:
102 * function-definition
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"
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
);
125 /* Seen references to C builtin functions.
126 * Import their definitions
128 auto s
= new AST
.Import(Loc
.initial
, null, Id
.builtins
, null, false);
134 assert(typedefTab
.length
== 0);
139 cparseDeclaration(LVL
.global
);
143 /******************************************************************************/
144 /********************************* Statement Parser ***************************/
147 /**********************
152 * expression-statement
153 * selection-statement
154 * iteration-statement
159 * endPtr = store location of closing brace
160 * pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
164 AST
.Statement
cparseStatement(int flags
, const(char)** endPtr
= null, Loc
* pEndloc
= null)
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();
186 /* A leading identifier can be a declaration, label, or expression.
187 * A quick check of the next token can disambiguate most cases.
194 auto ident
= token
.ident
;
195 nextToken(); // advance to `:`
196 nextToken(); // advance past `:`
197 if (token
.value
== TOK
.rightCurly
)
199 else if (token
.value
== TOK
.leftCurly
)
200 s
= cparseStatement(ParseStatementFlags
.curly | ParseStatementFlags
.scope_
);
202 s
= cparseStatement(0);
203 s
= new AST
.LabelStatement(loc
, ident
, s
);
211 case TOK
.leftBracket
:
222 case TOK
.leftShiftAssign
:
223 case TOK
.rightShiftAssign
:
226 case TOK
.leftParenthesis
:
227 if (auto pt
= lookupTypedef(token
.ident
))
232 goto Lexp
; // function call
239 /* If tokens look like a declaration, assume it is one
242 if (isCDeclaration(tk
))
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
:
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());
284 check(TOK
.semicolon
, "statement");
285 s
= new AST
.ExpStatement(loc
, exp
);
300 //case TOK._Imaginary:
307 // storage-class-specifiers
311 case TOK
._Thread_local
:
316 // function-specifiers
326 // alignment-specifier
329 // atomic-type-specifier or type_qualifier
332 case TOK
.__attribute__
:
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
);
347 s
= new AST
.CompoundDeclarationStatement(loc
, as
);
350 else if (symbols
.length
== 1)
352 auto d
= (*symbols
)[0];
353 s
= new AST
.ExpStatement(loc
, d
);
357 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
358 if (flags
& ParseStatementFlags
.scope_
)
359 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
363 case TOK
._Static_assert
: // _Static_assert ( constant-expression, string-literal ) ;
364 s
= new AST
.StaticAssertStatement(cparseStaticAssert());
370 * compound-statement:
371 * { block-item-list (opt) }
375 * block-item-list block-item
382 auto statements
= new AST
.Statements();
383 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.endOfFile
)
385 statements
.push(cparseStatement(ParseStatementFlags
.curlyScope
));
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");
405 check(TOK
.leftParenthesis
);
406 auto condition
= cparseExpression();
407 check(TOK
.rightParenthesis
);
409 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
410 s
= new AST
.WhileStatement(loc
, condition
, _body
, endloc
, null);
415 /* C11 6.8.3 null statement
418 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
424 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
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
);
437 AST
.Expression condition
;
438 AST
.Expression increment
;
441 check(TOK
.leftParenthesis
);
442 if (token
.value
== TOK
.semicolon
)
449 _init
= cparseStatement(0);
451 if (token
.value
== TOK
.semicolon
)
458 condition
= cparseExpression();
459 check(TOK
.semicolon
, "`for` condition");
461 if (token
.value
== TOK
.rightParenthesis
)
468 increment
= cparseExpression();
469 check(TOK
.rightParenthesis
);
472 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
473 s
= new AST
.ForStatement(loc
, _init
, condition
, increment
, _body
, endloc
);
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_
)
488 elsebody
= cparseStatement(ParseStatementFlags
.scope_
);
492 if (condition
&& ifbody
)
493 s
= new AST
.IfStatement(loc
, null, condition
, ifbody
, elsebody
, token
.loc
);
495 s
= null; // don't propagate parsing errors
500 error("found `else` without a corresponding `if` statement");
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
);
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
525 expHigh
= cparseAssignExp();
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())
545 s
= new AST
.CompoundStatement(loc
, statements
);
549 s
= cparseStatement(0);
551 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
553 s
= new AST
.CaseRangeStatement(loc
, exp
, expHigh
, s
);
555 s
= new AST
.CaseStatement(loc
, exp
, s
);
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
);
574 s
= cparseStatement(0);
575 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
576 s
= new AST
.DefaultStatement(loc
, s
);
583 * return expression ;
586 auto exp
= token
.value
== TOK
.semicolon ?
null : cparseExpression();
587 check(TOK
.semicolon
, "`return` statement");
588 s
= new AST
.ReturnStatement(loc
, exp
);
594 check(TOK
.semicolon
, "`break` statement");
595 s
= new AST
.BreakStatement(loc
, null);
600 check(TOK
.semicolon
, "`continue` statement");
601 s
= new AST
.ContinueStatement(loc
, null);
608 if (token
.value
!= TOK
.identifier
)
610 error("identifier expected following `goto`");
618 s
= new AST
.GotoStatement(loc
, ident
);
619 check(TOK
.semicolon
, "`goto` statement");
629 case TOK
.leftParenthesis
:
634 // ImportC extensions: parse as a D asm block.
635 s
= parseAsm(compileEnv
.masm
);
641 error("found `%s` instead of statement", token
.toChars());
646 if (token
.value
== TOK
.semicolon
)
653 symbols
= symbolsSave
;
654 typedefTab
.setDim(typedefTabLengthSave
);
659 /*******************************************************************************/
660 /********************************* Expression Parser ***************************/
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
)
678 auto e2
= cparseAssignExp();
679 e
= new AST
.CommaExp(loc
, e
, e2
, false);
686 /*********************
688 * primary-expression:
694 * __builtin_va_arg(assign_expression, type)
696 AST
.Expression
cparsePrimaryExp()
699 const loc
= token
.loc
;
701 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
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();
718 importBuiltins
= true; // probably one of those compiler extensions
720 e
= new AST
.IdentifierExp(loc
, token
.ident
);
724 case TOK
.charLiteral
:
725 case TOK
.int32Literal
:
726 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint32
);
730 case TOK
.wcharLiteral
:
731 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tuns16
);
735 case TOK
.dcharLiteral
:
736 case TOK
.uns32Literal
:
737 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns32
);
741 case TOK
.int64Literal
:
742 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint64
);
746 case TOK
.uns64Literal
:
747 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns64
);
751 case TOK
.float32Literal
:
752 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat32
);
756 case TOK
.float64Literal
:
757 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat64
);
761 case TOK
.float80Literal
:
762 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat80
);
766 case TOK
.imaginary32Literal
:
767 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary32
);
771 case TOK
.imaginary64Literal
:
772 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary64
);
776 case TOK
.imaginary80Literal
:
777 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary80
);
783 // cat adjacent strings
784 auto s
= token
.ustring
;
785 auto len
= token
.len
;
786 auto postfix
= token
.postfix
;
790 if (token
.value
== TOK
.string_
)
794 if (token
.postfix
!= postfix
)
795 error(token
.loc
, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix
, token
.postfix
);
796 postfix
= token
.postfix
;
800 const len2
= token
.len
;
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
);
810 e
= new AST
.StringExp(loc
, s
[0 .. len
], len
, 1, postfix
);
814 case TOK
.leftParenthesis
:
816 if (token
.value
== TOK
.leftCurly
)
817 e
= cparseStatementExpression(); // gcc extension
819 e
= cparseExpression();
820 check(TOK
.rightParenthesis
);
824 e
= cparseGenericSelection();
827 case TOK
._assert
: // __check(assign-exp) extension
829 check(TOK
.leftParenthesis
, "`__check`");
830 e
= parseAssignExp();
831 check(TOK
.rightParenthesis
);
832 e
= new AST
.AssertExp(loc
, e
, null);
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
);
845 /*********************************
847 * postfix-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 /********************************
870 * Parse a series of operators for a postfix expression after already parsing
871 * a primary-expression or compound literal expression.
873 * e = parsed primary or compound literal expression
875 * parsed postfix expression
877 private AST
.Expression
cparsePostfixOperators(AST
.Expression e
)
881 const loc
= token
.loc
;
886 if (token
.value
== TOK
.identifier
)
888 Identifier id
= token
.ident
;
889 e
= new AST
.DotIdExp(loc
, e
, id
);
892 error("identifier expected following `.`, not `%s`", token
.toChars());
897 if (token
.value
== TOK
.identifier
)
899 Identifier id
= token
.ident
;
900 auto die
= new AST
.DotIdExp(loc
, e
, id
);
905 error("identifier expected following `->`, not `%s`", token
.toChars());
909 e
= new AST
.PostExp(EXP
.plusPlus
, loc
, e
);
913 e
= new AST
.PostExp(EXP
.minusMinus
, loc
, e
);
916 case TOK
.leftParenthesis
:
917 e
= new AST
.CallExp(loc
, e
, cparseArguments());
920 case TOK
.leftBracket
:
922 // array dereferences:
924 AST
.Expression index
;
925 auto arguments
= new AST
.Expressions();
929 index
= cparseAssignExp();
930 arguments
.push(index
);
931 check(TOK
.rightBracket
);
933 e
= new AST
.ArrayExp(loc
, e
, arguments
);
943 /************************
947 * ++ unary-expression
948 * -- unary-expression
949 * unary-operator cast-expression
950 * sizeof unary-expression
951 * sizeof ( type-name )
952 * _Alignof ( type-name )
957 private AST
.Expression
cparseUnaryExp()
960 const loc
= token
.loc
;
966 // Parse `++` as an unary operator so that cast expressions only give
967 // an error for being non-lvalues.
969 e
= new AST
.PreExp(EXP
.prePlusPlus
, loc
, e
);
974 // Parse `--` as an unary operator, same as prefix increment.
976 e
= new AST
.PreExp(EXP
.preMinusMinus
, loc
, e
);
982 e
= new AST
.AddrExp(loc
, e
);
988 e
= new AST
.PtrExp(loc
, e
);
994 e
= new AST
.NegExp(loc
, e
);
1000 e
= new AST
.UAddExp(loc
, e
);
1005 e
= cparseCastExp();
1006 e
= new AST
.NotExp(loc
, e
);
1011 e
= cparseCastExp();
1012 e
= new AST
.ComExp(loc
, e
);
1018 if (token
.value
== TOK
.leftParenthesis
)
1020 auto tk
= peek(&token
);
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
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
);
1040 e
= new AST
.TypeExp(loc
, t
);
1045 // must be an expression
1046 e
= cparseUnaryExp();
1052 e
= cparseUnaryExp();
1055 e
= new AST
.DotIdExp(loc
, e
, Id
.__sizeof
);
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
);
1071 e
= cparsePostfixExp(e
);
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
);
1092 if (tk
.value
== TOK
.identifier
)
1094 iscast
= isTypedef(tk
.ident
);
1099 // ( identifier ) is an expression
1100 return cparseUnaryExp();
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
;
1112 auto t
= cparseTypeName();
1113 check(TOK
.rightParenthesis
);
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
);
1126 // ( type-name ) cast-expression
1127 auto ce
= cparseCastExp();
1128 return new AST
.CastExp(loc
, ce
, t
);
1131 if (t
.isTypeIdentifier() &&
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
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();
1159 * multiplicative-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();
1172 switch (token
.value
)
1176 auto e2
= cparseCastExp();
1177 e
= new AST
.MulExp(loc
, e
, e2
);
1182 auto e2
= cparseCastExp();
1183 e
= new AST
.DivExp(loc
, e
, e2
);
1188 auto e2
= cparseCastExp();
1189 e
= new AST
.ModExp(loc
, e
, e2
);
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();
1214 switch (token
.value
)
1218 auto e2
= cparseMulExp();
1219 e
= new AST
.AddExp(loc
, e
, e2
);
1224 auto e2
= cparseMulExp();
1225 e
= new AST
.MinExp(loc
, e
, e2
);
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();
1250 switch (token
.value
)
1254 auto e2
= cparseAddExp();
1255 e
= new AST
.ShlExp(loc
, e
, e2
);
1258 case TOK
.rightShift
:
1260 auto e2
= cparseAddExp();
1261 e
= new AST
.ShrExp(loc
, e
, e2
);
1274 * relational-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
;
1296 auto e2
= cparseShiftExp();
1297 e
= new AST
.CmpExp(op
, loc
, e
, e2
);
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
;
1326 auto e2
= cparseRelationalExp();
1327 e
= new AST
.EqualExp(op
, loc
, e
, e2
);
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)
1349 auto e2
= cparseEqualityExp();
1350 e
= new AST
.AndExp(loc
, e
, e2
);
1358 * exclusive-OR-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)
1370 auto e2
= cparseAndExp();
1371 e
= new AST
.XorExp(loc
, e
, e2
);
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)
1390 auto e2
= cparseXorExp();
1391 e
= new AST
.OrExp(loc
, e
, e2
);
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
)
1410 auto e2
= cparseOrExp();
1411 e
= new AST
.LogicalExp(loc
, EXP
.andAnd
, e
, e2
);
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
)
1430 auto e2
= cparseAndAndExp();
1431 e
= new AST
.LogicalExp(loc
, EXP
.orOr
, e
, e2
);
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
)
1450 auto e1
= cparseExpression();
1452 auto e2
= cparseCondExp();
1453 e
= new AST
.CondExp(loc
, e
, e1
, e2
);
1460 * assignment-expression:
1461 * conditional-expression
1462 * unary-expression assignment-operator assignment-expression
1464 * assignment-operator:
1465 * = *= /= %= += -= <<= >>= &= ^= |=
1467 AST
.Expression
cparseAssignExp()
1470 e
= cparseCondExp(); // constrain it to being unary-expression in semantic pass
1474 const loc
= token
.loc
;
1475 switch (token
.value
)
1479 auto e2
= cparseAssignExp();
1480 e
= new AST
.AssignExp(loc
, e
, e2
);
1485 auto e2
= cparseAssignExp();
1486 e
= new AST
.AddAssignExp(loc
, e
, e2
);
1491 auto e2
= cparseAssignExp();
1492 e
= new AST
.MinAssignExp(loc
, e
, e2
);
1497 auto e2
= cparseAssignExp();
1498 e
= new AST
.MulAssignExp(loc
, e
, e2
);
1503 auto e2
= cparseAssignExp();
1504 e
= new AST
.DivAssignExp(loc
, e
, e2
);
1509 auto e2
= cparseAssignExp();
1510 e
= new AST
.ModAssignExp(loc
, e
, e2
);
1515 auto e2
= cparseAssignExp();
1516 e
= new AST
.AndAssignExp(loc
, e
, e2
);
1521 auto e2
= cparseAssignExp();
1522 e
= new AST
.OrAssignExp(loc
, e
, e2
);
1527 auto e2
= cparseAssignExp();
1528 e
= new AST
.XorAssignExp(loc
, e
, e2
);
1531 case TOK
.leftShiftAssign
:
1533 auto e2
= cparseAssignExp();
1534 e
= new AST
.ShlAssignExp(loc
, e
, e2
);
1537 case TOK
.rightShiftAssign
:
1539 auto e2
= cparseAssignExp();
1540 e
= new AST
.ShrAssignExp(loc
, e
, e2
);
1550 /***********************
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
;
1566 check(TOK
.leftParenthesis
);
1567 auto cntlExp
= cparseAssignExp();
1569 auto types
= new AST
.Types();
1570 auto exps
= new AST
.Expressions();
1575 if (token
.value
== TOK
.default_
)
1579 error("only one `default` allowed in generic-assoc-list");
1584 t
= cparseTypeName();
1588 auto e
= cparseAssignExp();
1590 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
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 /*****************************
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
1620 check(TOK
.leftParenthesis
);
1622 auto arguments
= new AST
.Expressions();
1623 auto arg
= cparseAssignExp();
1624 arguments
.push(arg
);
1628 auto t
= cparseTypeName();
1629 auto tiargs
= new AST
.Objects();
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
);
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();
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);
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
1678 /********************************************************************************/
1679 /********************************* Declaration Parser ***************************/
1682 /*************************************
1685 * declaration-specifiers init-declarator-list (opt) ;
1686 * static_assert-declaration
1688 * init-declarator-list:
1690 * init-declarator-list , init-declarator
1694 * declarator = initializer
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();
1709 if (token
.value
== TOK
.__pragma
)
1711 uupragmaDirective(scanloc
);
1715 if (token
.value
== TOK
._import
) // import declaration extension
1717 auto a
= parseImport();
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
;
1746 symbols
= new AST
.Dsymbols();
1747 auto stags
= applySpecifier(stag
, specifier
);
1748 symbols
.push(stags
);
1752 /* If a declarator does not follow, it is unnamed
1754 if (token
.value
== TOK
.semicolon
)
1759 return; // accept empty declaration as an extension
1762 if (auto ti
= tspec
.isTypeIdentifier())
1765 error("type-specifier missing for declaration of `%s`", ti
.ident
.toChars());
1771 auto tt
= tspec
.isTypeTag();
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
1781 error(tt
.loc
, "`enum %s` has no members", stags
.toChars());
1788 error("no type for declarator before `%s`", token
.toChars());
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
)
1807 if (token
.value
== TOK
.assign
&& tspec
&& tspec
.isTypeIdentifier())
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();
1820 AST
.StringExp asmName
;
1821 auto dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
1826 break; // error recovery
1831 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1832 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1834 switch (token
.value
)
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.
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
1870 * declaration-list declaration
1873 if (first
&& // first declarator
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
;
1886 AST
.Dsymbol s
= null;
1887 typedefTab
.setDim(typedefTabLengthSave
);
1888 symbols
= symbolsSave
;
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;
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.
1922 else if (auto te
= dt.isTypeEnum())
1924 if (te
.sym
.isAnonymous())
1926 // This is a typedef for an anonymous enum.
1932 else if (auto tt
= dt.isTypeTag())
1934 if (tt
.id || tt
.tok
== TOK
.enum_
)
1937 /* This applies for enums declared as
1938 * typedef enum {A} E;
1942 declareTag(tt
, spec
);
1948 auto ad
= new AST
.AliasDeclaration(token
.loc
, id
, dt);
1950 ad
.adFlags |
= ad
.hidden
; // do not print when generating .di files
1954 insertTypedefToTypedefTab(id
, dt); // remember typedefs
1958 if (auto tt
= dt.isTypeTag())
1960 if (tt
.members
&& (tt
.id || tt
.tok
== TOK
.enum_
))
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
)
1978 hasInitializer
= true;
1979 initializer
= cparseInitializer();
1981 // declare the symbol
1984 if (isFunctionTypedef(dt))
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
);
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
);
2007 if (level
!= LVL
.global
)
2008 insertIdToTypedefTab(id
); // non-typedef declarations can hide typedefs in outer scopes
2012 // Saw `asm("name")` in the function, type, or variable definition.
2013 // This is equivalent to `pragma(mangle, "name")` in D
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
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);
2043 s
= new AST
.LinkDeclaration(s
.loc
, linkage
, decls
);
2047 if (level
== LVL
.global
&& !id
)
2048 error("expected identifier for declaration");
2052 switch (token
.value
)
2054 case TOK
.identifier
:
2057 error(token
.loc
, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s
.toChars(), token
.toChars());
2068 symbolsSave
= symbols
;
2073 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token
.toChars());
2075 return scanPastSemicolon();
2080 /***************************************
2081 * C11 Function Definitions
2082 * function-definition
2083 * declaration-specifiers declarator declaration-list (opt) compound-statement
2087 * declaration-list declaration
2089 * It's already been parsed up to the declaration-list (opt).
2090 * Pick it up from there.
2092 * id = function identifier
2093 * ft = function type
2094 * specifier = function specifiers
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
2133 if (auto t
= p
.type
.isTypeIdentifier())
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();
2146 s
= (*ad
.decl
)[0]; // AlignDeclaration wrapping the declaration
2148 auto d
= s
.isDeclaration();
2149 if (d
&& p
.ident
== d
.ident
&& d
.type
)
2152 p
.storageClass
= d
.storage_class
;
2153 d
.type
= null; // don't reuse
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
);
2178 auto s
= createFuncName(locFunc
, id
);
2179 body = new AST
.CompoundStatement(locFunc
, s
, body);
2183 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2188 /***************************************
2189 * C11 Initialization
2191 * assignment-expression
2192 * { } // C23 6.7.10 addition
2193 * { initializer-list }
2194 * { initializer-list , }
2197 * designation (opt) initializer
2198 * initializer-list , designation (opt) initializer
2205 * designator-list designator
2208 * [ constant-expression ]
2213 AST
.Initializer
cparseInitializer()
2215 if (token
.value
!= TOK
.leftCurly
)
2217 auto ae
= cparseAssignExp(); // assignment-expression
2218 return new AST
.ExpInitializer(token
.loc
, ae
);
2221 const loc
= token
.loc
;
2223 if (token
.value
== TOK
.rightCurly
) // { }
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
;
2235 /* There can be 0 or more designators preceding an initializer.
2236 * Collect them in desigInit
2238 AST
.DesigInit desigInit
;
2241 if (token
.value
== TOK
.leftBracket
) // [ constant-expression ]
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
2253 if (token
.value
!= TOK
.identifier
)
2255 error("identifier expected following `.` designator");
2258 if (!desigInit
.designatorList
)
2259 desigInit
.designatorList
= new AST
.Designators
;
2260 desigInit
.designatorList
.push(AST
.Designator(token
.ident
));
2265 if (desigInit
.designatorList
)
2271 desigInit
.initializer
= cparseInitializer();
2273 ci
= new AST
.CInitializer(loc
);
2274 ci
.initializerList
.push(desigInit
);
2275 if (token
.value
== TOK
.comma
)
2278 if (token
.value
!= TOK
.rightCurly
)
2283 check(TOK
.rightCurly
);
2284 //printf("ci: %s\n", ci.toChars());
2288 /*************************************
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)
2297 * level = declaration context
2298 * specifier = specifiers in and out
2300 * resulting type, null if not specified
2302 private AST
.Type
cparseDeclarationSpecifiers(LVL level
, ref Specifier specifier
)
2321 ximaginary
= 0x8000,
2329 //printf("parseDeclarationSpecifiers()\n");
2332 SCW scw
= specifier
.scw
& SCW
.xtypedef
;
2340 //printf("token %s\n", token.toChars());
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;
2355 case TOK
._Thread_local
: scwx
= SCW
.x_Thread_local
; break;
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;
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
:
2386 const structOrUnion
= token
.value
;
2387 const sloc
= token
.loc
;
2390 Specifier tagSpecifier
;
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
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
);
2408 t
= cparseStruct(sloc
, structOrUnion
, tagSpecifier
.packalign
, symbols
);
2414 t
= cparseEnum(symbols
);
2421 // type-specifier if followed by `( type-name )`
2422 auto tk
= peek(&token
);
2423 if (tk
.value
== TOK
.leftParenthesis
)
2426 if (isTypeName(tk
) && tk
.value
== TOK
.rightParenthesis
)
2430 t
= cparseTypeName();
2431 tkwx
= TKW
.x_Atomic
;
2435 // C11 6.7.3 type-qualifier if not
2436 modx
= MOD
.x_Atomic
;
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
2451 check(TOK
.leftParenthesis
);
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
);
2475 case TOK
.__attribute__
:
2478 * declaration-specifiers:
2479 * gnu-attributes declaration-specifiers (opt)
2481 cparseGnuAttributes(specifier
);
2485 case TOK
.__declspec
:
2487 /* Microsoft extension
2489 cparseDeclspec(specifier
);
2496 check(TOK
.leftParenthesis
);
2501 e
= new AST
.TypeExp(loc
, cparseTypeName());
2503 e
= cparseExpression();
2504 t
= new AST
.TypeTypeof(loc
, e
);
2506 if(token
.value
== TOK
.rightParenthesis
)
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
2519 case TOK
.leftParenthesis
:
2522 case TOK
.rightParenthesis
:
2545 if (tkw
& TKW
.xlong
&& tkwx
& TKW
.xlong
)
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
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");
2567 if (!(tkwx
& TKW
.xtag
)) // if parser already advanced
2582 error("duplicate storage class");
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");
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`");
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
2605 if (level
& (LVL
.parameter | LVL
.prototype
) &&
2606 scw
& ~SCW
.xregister
)
2608 error("only `register` storage class allowed for function parameters");
2611 if (level
== LVL
.global
&&
2612 scw
& (SCW
.xauto | SCW
.xregister
))
2614 error("`auto` and `register` storage class not allowed for global");
2622 specifier
.scw
= scw
;
2623 specifier
.mod
= mod
;
2625 // Convert TKW bits to type t
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;
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;
2644 case TKW
.xsigned | TKW
.xint
: t
= integerTypeForSize(intsize
); break;
2647 case TKW
.xunsigned | TKW
.xint
: t
= unsignedTypeForSize(intsize
); break;
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;
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;
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;
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
2692 /* Punch through to what the typedef is, to support things like:
2695 auto pt
= lookupTypedef(previd
);
2696 if (pt
&& *pt
) // if previd is a known typedef
2700 t
= new AST
.TypeIdentifier(loc
, previd
);
2705 case TKW
.x_Atomic
: // no atomics for you
2706 break; // t is already set
2709 error("illegal type combination");
2710 t
= AST
.Type
.terror
;
2717 /********************************
2719 * Parse a declarator (including function definitions).
2721 * pointer (opt) direct-declarator
2723 * direct-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) )
2734 * * type-qualifier-list (opt)
2735 * * type-qualifier-list (opt) pointer
2737 * type-qualifier-list :
2739 * type-qualifier-list type-qualifier
2741 * parameter-type-list :
2743 * parameter-list , ...
2746 * parameter-declaration
2747 * parameter-list , parameter-declaration
2749 * parameter-declaration :
2750 * declaration-specifiers declarator
2751 * declaration-specifiers abstract-declarator (opt)
2755 * identifier-list , identifier
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
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
2776 * ts -> ... -> tx -> t
2778 static void insertTx(ref AST
.Type ts
, AST
.Type tx
, AST
.Type t
)
2781 for (pt
= &ts
; *pt
!= t
; pt
= &(cast(AST
.TypeNext
)*pt
).next
)
2787 AST
.Type
parseDecl(AST
.Type t
)
2789 //printf("parseDecl() t: %s\n", t.toChars());
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
;
2804 case TOK
.leftParenthesis
: // ( declarator )
2805 //printf("leftParen\n");
2810 if (!isCDeclarator(tk
, declarator
))
2812 /* Not ( declarator ), might be parameter-list
2819 if (token
.value
== TOK
.__stdcall
) // T (__stdcall*fp)();
2821 specifier
.mod |
= MOD
.x__stdcall
;
2826 check(TOK
.rightParenthesis
);
2829 case TOK
.mul: // pointer
2831 t
= new AST
.TypePointer(t
);
2833 // add post fixes const/volatile/restrict/_Atomic
2834 const mod
= cparseTypeQualifierList();
2835 if (mod
& MOD
.xconst
)
2837 if (token
.value
== TOK
.__attribute__
)
2838 cparseGnuAttributes(specifier
);
2842 //printf("default %s\n", token.toChars());
2843 if (declarator
== DTR
.xdirect
)
2845 if (!t || t
.isTypeIdentifier())
2848 error("no type-specifier for declarator");
2849 t
= AST
.Type
.tint32
;
2852 error("identifier or `(` expected"); // )
2861 // parse DeclaratorSuffixes
2864 switch (token
.value
)
2866 case TOK
.leftBracket
:
2868 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2872 auto mod
= cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2876 if (token
.value
== TOK
.static_
)
2878 isStatic
= true; // `static`
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; // `*`
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
);
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.
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
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
2944 insertTx(ts
, ta
, t
); // ts -> ... -> ta -> t
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
)
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
2964 this.symbols
= symbolsSave
;
2973 if (declarator
== DTR
.xdirect
&& !pident
)
2974 error("expected identifier for declarator");
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
2995 if (constTypes
.length
)
2997 AST
.Type
constApply(AST
.Type t
)
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
[])
3014 if (declarator
== DTR
.xparameter
&&
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
);
3028 //printf("result: %s\n", t.toChars());
3032 /******************************
3041 MOD
cparseTypeQualifierList()
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;
3061 /***********************************
3064 AST
.Type
cparseTypeName()
3066 Specifier specifier
;
3067 specifier
.packalign
.setDefault();
3068 auto tspec
= cparseSpecifierQualifierList(LVL
.global
, specifier
);
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
3080 return cparseDeclarator(DTR
.xabstract
, tspec
, id
, specifier
);
3083 /***********************************
3085 * specifier-qualifier-list:
3086 * type-specifier specifier-qualifier-list (opt)
3087 * type-qualifier specifier-qualifier-list (opt)
3089 * level = declaration context
3090 * specifier = specifiers in and out
3092 * resulting type, null if not specified
3094 AST
.Type
cparseSpecifierQualifierList(LVL level
, ref Specifier specifier
)
3096 auto t
= cparseDeclarationSpecifiers(level
, specifier
);
3098 error("storage class not allowed in specifier-qualified-list");
3102 /***********************************
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)
3118 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3121 if (token
.value
== TOK
.rightParenthesis
) // func()
3124 return AST
.ParameterList(parameters
, AST
.VarArg
.KRvariadic
, varargsStc
);
3127 /* Create function prototype scope
3129 typedefTab
.push(null);
3131 AST
.ParameterList
finish()
3134 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3137 /* The check for identifier-list comes later,
3138 * when doing the trailing declaration-list (opt)
3142 if (token
.value
== TOK
.rightParenthesis
)
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
3151 check(TOK
.rightParenthesis
);
3155 Specifier specifier
;
3156 specifier
.packalign
.setDefault();
3157 auto tspec
= cparseDeclarationSpecifiers(LVL
.prototype
, specifier
);
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
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
)
3181 auto param
= new AST
.Parameter(paramLoc
, specifiersToSTC(LVL
.parameter
, specifier
),
3183 parameters
.push(param
);
3184 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
3188 check(TOK
.rightParenthesis
);
3192 /***********************************
3194 * _Static_assert ( constant-expression , string-literal ) ;
3196 private AST
.StaticAssert
cparseStaticAssert()
3198 const loc
= token
.loc
;
3200 //printf("cparseStaticAssert()\n");
3202 check(TOK
.leftParenthesis
);
3203 auto exp
= cparseConstantExp();
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.
3219 private AST
.Expressions
* cparseArguments()
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
)
3230 nextToken(); // consume comma
3233 check(TOK
.rightParenthesis
);
3238 /*************************
3240 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
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:
3250 * deprecated(depMsg)
3259 * specifier = filled in with the attribute(s)
3261 private void cparseDeclspec(ref Specifier specifier
)
3263 //printf("cparseDeclspec()\n");
3264 /* Check for dllexport, dllimport
3267 nextToken(); // move past __declspec
3268 check(TOK
.leftParenthesis
);
3271 if (token
.value
== TOK
.rightParenthesis
)
3276 else if (token
.value
== TOK
.endOfFile
)
3278 else if (token
.value
== TOK
.identifier
)
3280 if (token
.ident
== Id
.dllimport
)
3282 specifier
.dllimport
= true;
3285 else if (token
.ident
== Id
.dllexport
)
3287 specifier
.dllexport
= true;
3290 else if (token
.ident
== Id
.naked
)
3292 specifier
.naked
= true;
3295 else if (token
.ident
== Id
.noinline
)
3297 specifier
.scw |
= SCW
.xnoinline
;
3300 else if (token
.ident
== Id
.noreturn
)
3302 specifier
.noreturn
= true;
3305 else if (token
.ident
== Id
._nothrow
)
3307 specifier
._nothrow
= true;
3310 else if (token
.ident
== Id
.thread
)
3312 specifier
.scw |
= SCW
.x_Thread_local
;
3315 else if (token
.ident
== Id
._align
)
3317 // Microsoft spec is very imprecise as to how this actually works
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);
3331 error("alignment value expected, not `%s`", token
.toChars());
3335 check(TOK
.rightParenthesis
);
3337 else if (token
.ident
== Id
._deprecated
)
3339 specifier
._deprecated
= true;
3341 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3344 specifier
.depMsg
= cparseExpression();
3345 check(TOK
.rightParenthesis
);
3351 if (token
.value
== TOK
.leftParenthesis
)
3355 else if (token
.value
== TOK
.restrict
) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3359 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token
.toChars());
3361 if (token
.value
!= TOK
.rightParenthesis
)
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
3372 * asm ( asm-string-literal )
3374 * asm-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`.
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
;
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)
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();
3421 switch (token
.value
)
3423 case TOK
.leftParenthesis
:
3427 case TOK
.rightParenthesis
:
3434 error("matching `)` expected, not `;`");
3439 error("matching `)` expected, not end of file");
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
;
3458 *ptoklist
= allocateToken();
3459 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3460 ptoklist
= &(*ptoklist
).next
;
3467 // Create AsmStatement from list of tokens we've saved
3468 AST
.Statement s
= new AST
.AsmStatement(token
.loc
, toklist
);
3474 auto s
= new AST
.CompoundAsmStatement(loc
, statements
, 0);
3478 /*************************
3479 * __attribute__ parser
3480 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
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
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
)
3506 cparseGnuAttribute(specifier
);
3507 if (token
.value
!= TOK
.comma
)
3513 check(TOK
.rightParenthesis
);
3514 check(TOK
.rightParenthesis
);
3518 /*************************
3519 * Parse a single 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:
3531 * constant-expression
3532 * expression-list , constant-expression
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)
3542 if (!isGnuAttributeName())
3545 if (token
.value
== TOK
.identifier
)
3547 if (token
.ident
== Id
.aligned
)
3550 if (token
.value
== TOK
.leftParenthesis
)
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);
3564 error("alignment value expected, not `%s`", token
.toChars());
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
;
3579 else if (token
.ident
== Id
._deprecated
)
3581 specifier
._deprecated
= true;
3583 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3586 specifier
.depMsg
= cparseExpression();
3587 check(TOK
.rightParenthesis
);
3590 else if (token
.ident
== Id
.dllimport
)
3592 specifier
.dllimport
= true;
3595 else if (token
.ident
== Id
.dllexport
)
3597 specifier
.dllexport
= true;
3600 else if (token
.ident
== Id
.naked
)
3602 specifier
.naked
= true;
3605 else if (token
.ident
== Id
.noinline
)
3607 specifier
.scw |
= SCW
.xnoinline
;
3610 else if (token
.ident
== Id
.noreturn
)
3612 specifier
.noreturn
= true;
3615 else if (token
.ident
== Id
._nothrow
)
3617 specifier
._nothrow
= true;
3620 else if (token
.ident
== Id
._pure
)
3622 specifier
._pure
= true;
3625 else if (token
.ident
== Id
.vector_size
)
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
;
3639 error("value for vector_size expected, not `%s`", token
.toChars());
3642 check(TOK
.rightParenthesis
);
3647 if (token
.value
== TOK
.leftParenthesis
)
3654 if (token
.value
== TOK
.leftParenthesis
)
3659 /*************************
3660 * See if match for GNU attribute name, which may be any identifier,
3661 * storage-class-specifier, type-specifier, or type-qualifier.
3663 * true if a valid GNU attribute name
3665 private bool isGnuAttributeName()
3667 switch (token
.value
)
3669 case TOK
.identifier
:
3685 case TOK
._Thread_local
:
3701 /***************************
3702 * Like skipParens(), but consume the tokens.
3704 private void cparseParens()
3706 check(TOK
.leftParenthesis
);
3711 switch (token
.value
)
3713 case TOK
.leftParenthesis
:
3717 case TOK
.rightParenthesis
:
3721 error("extra right parenthesis");
3732 error("end of file found before right parenthesis");
3743 /******************************************************************************/
3744 /***************************** Struct & Enum Parser ***************************/
3747 /*************************************
3750 * enum identifier (opt) { enumerator-list }
3751 * enum identifier (opt) { enumerator-list , }
3756 * enumerator-list , enumerator
3759 * enumeration-constant
3760 * enumeration-constant = constant-expression
3762 * enumeration-constant:
3766 * symbols = symbols to add enum declaration to
3770 private AST
.Type
cparseEnum(ref AST
.Dsymbols
* symbols
)
3772 const loc
= token
.loc
;
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
);
3787 if (token
.value
== TOK
.identifier
)
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
)
3802 base
= cparseTypeName();
3805 AST
.Dsymbols
* members
;
3806 if (token
.value
== TOK
.leftCurly
)
3809 members
= new AST
.Dsymbols();
3811 if (token
.value
== TOK
.rightCurly
) // C11 6.7.2.2-1
3814 error("no members for `enum %s`", tag
.toChars());
3816 error("no members for anonymous enum");
3819 while (token
.value
== TOK
.identifier
)
3821 auto ident
= token
.ident
; // enumeration-constant
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
)
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);
3856 if (token
.value
== TOK
.comma
)
3863 check(TOK
.rightCurly
);
3866 * Parse the postfix gnu-attributes (opt)
3868 if (token
.value
== TOK
.__attribute__
)
3869 cparseGnuAttributes(specifier
);
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 /*************************************
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
3893 * struct-declaration-list:
3894 * struct-declaration
3895 * struct-declaration-list struct-declaration
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
3903 * type of the struct
3905 private AST
.Type
cparseStruct(Loc loc
, TOK structOrUnion
, structalign_t packalign
, ref AST
.Dsymbols
* symbols
)
3909 if (token
.value
== TOK
.identifier
)
3915 AST
.Dsymbols
* members
;
3916 if (token
.value
== TOK
.leftCurly
)
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
)
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)
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 /*************************************
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:
3960 * struct-declarator-list , struct-declarator
3962 * struct-declarator:
3964 * declarator (opt) : constant-expression
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();
3978 Specifier specifier
;
3979 specifier
.packalign
= this.packalign
;
3980 auto tspec
= cparseSpecifierQualifierList(LVL
.member
, specifier
);
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
)
3997 auto tt
= tspec
.isTypeTag();
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 ... };
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
);
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
;
4033 symbols
= new AST
.Dsymbols();
4034 auto s
= applySpecifier(stag
, specifier
);
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");
4057 dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
4062 break; // error recovery
4066 AST
.Expression width
;
4067 if (token
.value
== TOK
.colon
)
4069 // C11 6.7.2.1-10 bit-field
4071 width
= cparseConstantExp();
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");
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
);
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
);
4104 switch (token
.value
)
4106 case TOK
.identifier
:
4107 error("missing comma");
4119 error("`;` or `,` expected");
4120 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
4129 /******************************************************************************/
4130 /********************************* Lookahead Parser ***************************/
4133 /************************************
4134 * Determine if the scanner is sitting on the start of a declaration.
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)
4141 * true at start of a declaration
4143 private bool isCDeclaration(ref Token
* pt
)
4146 //printf("isCDeclaration() %s\n", t.toChars());
4147 if (!isDeclarationSpecifiers(t
))
4152 if (t
.value
== TOK
.semicolon
)
4158 if (!isCDeclarator(t
, DTR
.xdirect
))
4160 if (t
.value
== TOK
.asm_
)
4163 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4166 if (t
.value
== TOK
.__attribute__
)
4169 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4172 if (t
.value
== TOK
.assign
)
4175 if (!isInitializer(t
))
4195 /********************************
4196 * See if match for initializer.
4198 * pt = starting token, updated to one past end of initializer if true
4200 * true if initializer
4202 private bool isInitializer(ref Token
* pt
)
4204 //printf("isInitializer()\n");
4207 if (t
.value
== TOK
.leftCurly
)
4215 // skip over assignment-expression, ending before comma or semiColon or EOF
4216 if (!isAssignmentExpression(t
))
4222 /********************************
4224 * postfix-expression ( argument-expression-list(opt) )
4226 * pt = starting token, updated to one past end of initializer if true
4228 * true if function call
4230 private bool isFunctionCall(ref Token
* pt
)
4232 //printf("isFunctionCall()\n");
4235 if (!isPrimaryExpression(t
))
4237 if (t
.value
!= TOK
.leftParenthesis
)
4242 if (!isAssignmentExpression(t
))
4244 if (t
.value
== TOK
.comma
)
4249 if (t
.value
== TOK
.rightParenthesis
)
4256 if (t
.value
!= TOK
.semicolon
)
4262 /********************************
4263 * See if match for assignment-expression.
4265 * pt = starting token, updated to one past end of assignment-expression if true
4267 * true if assignment-expression
4269 private bool isAssignmentExpression(ref Token
* 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.
4285 case TOK
.rightParenthesis
:
4286 case TOK
.rightBracket
:
4293 case TOK
.leftParenthesis
:
4294 if (!skipParens(t
, &t
))
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.).
4307 case TOK
.leftBracket
:
4308 if (!skipBrackets(t
))
4318 any
= true; // assume token was part of an a-e
4327 /********************************
4328 * See if match for constant-expression.
4330 * pt = starting token, updated to one past end of constant-expression if true
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.
4343 * pt = starting token, updated to one past end of declaration-specifiers if true
4345 * true if declaration-specifiers
4347 private bool isDeclarationSpecifiers(ref Token
* pt
)
4349 //printf("isDeclarationSpecifiers()\n");
4371 //case TOK._Imaginary:
4378 case TOK
.identifier
: // typedef-name
4392 if (t
.value
== TOK
.__attribute__ ||
4393 t
.value
== TOK
.__declspec
)
4396 if (!skipParens(t
, &t
))
4399 if (t
.value
== TOK
.identifier
)
4402 if (t
.value
== TOK
.leftCurly
)
4408 else if (t
.value
== TOK
.leftCurly
)
4418 // storage-class-specifiers
4423 case TOK
._Thread_local
:
4427 // function-specifiers
4440 case TOK
._Alignas
: // alignment-specifier
4441 case TOK
.__declspec
: // decl-specifier
4442 case TOK
.__attribute__
: // attribute-specifier
4444 if (!skipParens(t
, &t
))
4449 // either atomic-type-specifier or type_qualifier
4450 case TOK
._Atomic
: // TODO _Atomic ( type-name )
4452 if (t
.value
== TOK
.leftParenthesis
) // maybe atomic-type-specifier
4456 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4457 { // it's a type-qualifier
4458 t
= tsave
; // back up parser
4462 t
= peek(t
); // move past right parenthesis of atomic-type-specifier
4481 /**************************************
4482 * See if declaration-list is present.
4484 * true if declaration-list is present, even an empty one
4486 bool isDeclarationList(ref Token
* pt
)
4491 if (t
.value
== TOK
.leftCurly
)
4496 if (!isCDeclaration(t
))
4501 /*******************************************
4504 * pt = enters on left brace, set to token past right bracket on true
4506 * true if successful
4508 private bool skipBraces(ref Token
* pt
)
4511 if (t
.value
!= TOK
.leftCurly
)
4525 case TOK
.rightCurly
:
4548 /*******************************************
4551 * pt = enters on left bracket, set to token past right bracket on true
4553 * true if successful
4555 private bool skipBrackets(ref Token
* pt
)
4558 if (t
.value
!= TOK
.leftBracket
)
4567 case TOK
.leftBracket
:
4572 case TOK
.rightBracket
:
4595 /*********************************
4596 * Check to see if tokens starting with *pt form a declarator.
4598 * pt = pointer to starting token, updated to point past declarator if true is returned
4599 * declarator = declarator kind
4603 private bool isCDeclarator(ref Token
* pt
, DTR declarator
)
4605 //printf("isCDeclarator()\n");
4609 if (t
.value
== TOK
.mul) // pointer
4612 if (!isTypeQualifierList(t
))
4619 if (t
.value
== TOK
.identifier
)
4621 if (declarator
== DTR
.xabstract
)
4625 else if (t
.value
== TOK
.leftParenthesis
)
4628 if (t
.value
== TOK
.__stdcall
)
4630 if (!isCDeclarator(t
, declarator
))
4632 if (t
.value
!= TOK
.rightParenthesis
)
4636 else if (declarator
== DTR
.xdirect
)
4643 if (t
.value
== TOK
.leftBracket
)
4645 if (!skipBrackets(t
))
4648 else if (t
.value
== TOK
.leftParenthesis
)
4650 if (!skipParens(t
, &t
))
4660 /***************************
4661 * Is this the start of a type-qualifier-list?
4664 * pt = first token; updated with past end of type-qualifier-list if true
4666 * true if start of type-qualifier-list
4668 private bool isTypeQualifierList(ref Token
* pt
)
4692 /***************************
4693 * Is this the start of a type-name?
4695 * pt = first token; updated with past end of type-name if true
4697 * true if start of type-name
4699 private bool isTypeName(ref Token
* pt
)
4702 //printf("isTypeName() %s\n", t.toChars());
4703 if (!isSpecifierQualifierList(t
))
4705 if (!isCDeclarator(t
, DTR
.xabstract
))
4707 if (t
.value
!= TOK
.rightParenthesis
)
4713 /***************************
4714 * Is this the start of a specifier-qualifier-list?
4716 * pt = first token; updated with past end of specifier-qualifier-list if true
4718 * true if start of specifier-qualifier-list
4720 private bool isSpecifierQualifierList(ref Token
* pt
)
4746 //case TOK._Imaginary: // ? missing in Spec
4751 case TOK
.identifier
:
4752 // Use typedef table to disambiguate
4753 if (isTypedef(t
.ident
))
4763 // struct-or-union-specifier
4769 if (t
.value
== TOK
.identifier
)
4772 if (t
.value
== TOK
.leftCurly
)
4778 else if (t
.value
== TOK
.leftCurly
)
4787 // atomic-type-specifier
4790 case TOK
.__attribute__
:
4792 if (t
.value
!= TOK
.leftParenthesis ||
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 }
4814 * pt = starting token, updated to one past end of constant-expression if true
4815 * afterParenType = true if already seen `( type-name )`
4817 * true if matches ( type-name ) ...
4819 private bool isCastExpression(ref Token
* pt
, bool afterParenType
= false)
4822 if (log
) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token
.toChars(pt
.value
), afterParenType
);
4826 case TOK
.leftParenthesis
:
4827 auto tk
= peek(t
); // move past left parenthesis
4828 if (!isTypeName(tk
) || tk
.value
!= TOK
.rightParenthesis
)
4831 goto default; // could be ( type-name ) ( unary-expression )
4834 tk
= peek(tk
); // move past right parenthesis
4836 if (tk
.value
== TOK
.leftCurly
)
4838 // ( type-name ) { initializer-list }
4839 if (!isInitializer(tk
))
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
4858 // ( type-name ) cast-expression
4863 if (!afterParenType ||
!isUnaryExpression(t
, afterParenType
))
4867 // if we've already seen ( type-name ), then this is a cast
4871 if (log
) printf("isCastExpression true\n");
4875 /********************************
4876 * See if match for unary-expression.
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
4881 * true if unary-expression
4883 private bool isUnaryExpression(ref Token
* pt
, bool afterParenType
= false)
4889 case TOK
.minusMinus
:
4891 if (!isUnaryExpression(t
, afterParenType
))
4902 if (!isCastExpression(t
, afterParenType
))
4908 if (t
.value
== TOK
.leftParenthesis
)
4913 if (tk
.value
!= TOK
.rightParenthesis
)
4919 if (!isUnaryExpression(t
, afterParenType
))
4925 if (t
.value
!= TOK
.leftParenthesis
)
4928 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4933 // Compound literals are handled by cast and sizeof expressions,
4934 // so be content with just seeing a primary expression.
4935 if (!isPrimaryExpression(t
))
4943 /********************************
4944 * See if match for primary-expression.
4946 * pt = starting token, updated to one past end of constant-expression if true
4948 * true if primary-expression
4950 private bool isPrimaryExpression(ref Token
* pt
)
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
:
4973 case TOK
.leftParenthesis
:
4975 if (!skipParens(t
, &t
))
4981 if (!skipParens(t
, &t
))
4993 /******************************************************************************/
4994 /********************************* More ***************************************/
4998 * Declaration context
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
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
5027 // C11 6.7.4 Function specifiers
5034 /// C11 6.7.3 Type qualifiers
5042 x__stdcall
= 0x10, // Windows linkage extension
5045 /**********************************
5046 * Aggregate for all the various specifiers
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
5069 * level = declaration context
5070 * specifier = specifiers, context, etc.
5072 * corresponding D storage class
5074 StorageClass
specifiersToSTC(LVL level
, const ref Specifier specifier
)
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_
;
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_
;
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
;
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_
;
5141 /***********************
5142 * Add attributes from Specifier to function
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
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
5174 * size = size of type
5176 * corresponding signed D integer type
5178 private AST
.Type
integerTypeForSize(ubyte size
)
5181 return AST
.Type
.tint8
;
5183 return AST
.Type
.tint16
;
5185 return AST
.Type
.tint32
;
5187 return AST
.Type
.tint64
;
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
5200 * size = size of type
5202 * corresponding unsigned D integer type
5204 private AST
.Type
unsignedTypeForSize(ubyte size
)
5207 return AST
.Type
.tuns8
;
5209 return AST
.Type
.tuns16
;
5211 return AST
.Type
.tuns32
;
5213 return AST
.Type
.tuns64
;
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`
5226 * flags = kind of float to return (real, imaginary, complex).
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
;
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
;
5255 * Flags for realType
5257 private enum RTFlags
5264 /********************
5265 * C11 6.4.2.2 Create declaration to predefine __func__
5266 * `static const char __func__[] = " function-name ";`
5268 * loc = location for this declaration
5269 * id = identifier of function
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.
5293 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
5297 /**************************
5298 * Apply `const` to a type.
5300 * t = type to add const to
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_
;
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_
);
5323 /***************************
5324 * Apply specifier to a Dsymbol.
5327 * specifier = specifiers to apply
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);
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);
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);
5359 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.packalign
, decls
);
5366 /******************************************************************************/
5367 /************************** typedefTab symbol table ***************************/
5370 /********************************
5371 * Determines if type t is a function type.
5375 * true if it represents a function
5377 bool isFunctionTypedef(AST
.Type t
)
5379 //printf("isFunctionTypedef() %s\n", t.toChars());
5380 if (t
.isTypeFunction())
5382 if (auto tid
= t
.isTypeIdentifier())
5384 auto pt
= lookupTypedef(tid
.ident
);
5387 return (*pt
).isTypeFunction() !is null;
5393 /********************************
5394 * Determine if `id` is a symbol for a Typedef.
5396 * id = possible typedef
5398 * true if id is a Type
5400 bool isTypedef(Identifier id
)
5402 auto pt
= lookupTypedef(id
);
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[]
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
);
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[].
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()
5472 if (n
.value
== TOK
.int32Literal
)
5477 if (n
.value
== TOK
.identifier
)
5479 if (n
.ident
== Id
.line
)
5481 poundLine(n
, false);
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');
5495 else if (n
.ident
== Id
.__pragma
)
5497 pragmaDirective(scanloc
);
5500 else if (n
.ident
== Id
.ident
) // #ident "string"
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;
5512 if (n
.value
== TOK
.endOfFile || n
.value
== TOK
.endOfLine
)
5515 error("\"string\" expected after `#ident`");
5519 if (n
.ident
!= Id
.undef
)
5520 error("C preprocessor directive `#%s` is not supported", n
.toChars());
5524 /*********************************************
5526 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5527 * Scanner is on the `__pragma`
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());
5543 if (token
.value
== TOK
.identifier
)
5545 if (token
.ident
== Id
.pack
)
5546 pragmaPack(startloc
, false);
5550 if (token
.value
== TOK
.leftParenthesis
)
5555 else if (token
.value
== TOK
.endOfFile
)
5558 else if (token
.value
== TOK
.rightParenthesis
)
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());
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
)
5583 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pack
)
5584 return pragmaPack(loc
, true);
5585 if (n
.value
!= TOK
.endOfLine
)
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`
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()
5619 if (n
.value
!= TOK
.leftParenthesis
)
5621 error(loc
, "left parenthesis expected to follow `#pragma pack`");
5622 if (n
.value
!= TOK
.endOfLine
)
5629 if (n
.value
!= TOK
.rightParenthesis
)
5631 error(loc
, "right parenthesis expected to close `#pragma pack(`");
5633 if (n
.value
!= TOK
.endOfLine
)
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);
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");
5661 eSink
.warning(startloc
, "current pack attribute is %d", packalign
.get());
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)
5673 Identifier record
= null;
5674 if (n
.value
== TOK
.comma
)
5677 if (n
.value
== TOK
.identifier
)
5681 if (n
.value
== TOK
.comma
)
5684 if (n
.value
== TOK
.int32Literal
)
5690 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5693 else if (n
.value
== TOK
.int32Literal
)
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 )
5708 * , IdentifierOrInteger
5709 * , IdentifierOrInteger PopList
5710 * IdentifierOrInteger:
5714 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pop)
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();
5728 packalign
= (*this.packs
)[len
- 1];
5729 return closingParen();
5731 while (n
.value
== TOK
.comma
) // #pragma pack ( pop ,
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.
5743 if ((*this.records
)[len
- 1] == n
.ident
)
5745 this.records
.setDim(len
- 1);
5746 this.packs
.setDim(len
- 1);
5748 packalign
= (*this.packs
)[len
- 2];
5750 packalign
.setDefault(); // stack empty, use default
5756 else if (n
.value
== TOK
.int32Literal
)
5763 error(loc
, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n
.toChars());
5767 return closingParen();
5769 /* # pragma pack ( integer )
5770 * Sets alignment to integer
5772 if (n
.value
== TOK
.int32Literal
)
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
)
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[]
5804 if (!defines || defines
.length
< 10) // minimum length of a #define line
5806 OutBuffer
* buf
= defines
;
5807 defines
= null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5808 // from appending to slice[]
5809 const length
= buf
.length
;
5811 auto slice
= buf
.peekChars()[0 .. length
];
5812 resetDefineLines(slice
); // reset lexer
5814 auto eLatch
= new ErrorSinkLatch();
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
5831 if (size_t
* pd
= cast(void*)s
.ident
in defineTab
)
5833 //printf("replacing %s\n", v.toChars());
5834 (*symbols
)[*pd
] = s
;
5837 defineTab
[cast(void*)s
.ident
] = symbols
.length
;
5843 if (p
[0 .. 7] == "#define")
5847 //printf("define %s\n", token.toChars());
5848 if (token
.value
== TOK
.identifier
)
5850 auto id
= token
.ident
;
5851 const params
= *p
== '(';
5856 switch (token
.value
)
5858 case TOK
.endOfLine
: // #define identifier
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
;
5871 const intvalue
= token
.intvalue
;
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
);
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
;
5894 const floatvalue
= token
.floatvalue
;
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
);
5910 const str = token
.ustring
;
5911 const len
= token
.len
;
5912 const postfix
= token
.postfix
;
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
);
5927 case TOK
.leftParenthesis
:
5929 * #define ID ( expression )
5930 * and rewrite it to a template function:
5931 * auto ID()() { return expression; }
5934 break; // no parameters
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
)
5943 if (token
.value
!= TOK
.endOfLine
)
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);
5951 AST
.Dsymbols
* decldefs
= new AST
.Dsymbols();
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);
5969 if (token
.value
!= TOK
.endOfLine
)