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
.int32Literal
:
251 case TOK
.uns32Literal
:
252 case TOK
.int64Literal
:
253 case TOK
.uns64Literal
:
254 case TOK
.int128Literal
:
255 case TOK
.uns128Literal
:
256 case TOK
.float32Literal
:
257 case TOK
.float64Literal
:
258 case TOK
.float80Literal
:
259 case TOK
.imaginary32Literal
:
260 case TOK
.imaginary64Literal
:
261 case TOK
.imaginary80Literal
:
262 case TOK
.leftParenthesis
:
275 auto exp
= cparseExpression();
276 if (token
.value
== TOK
.identifier
&& exp
.op
== EXP
.identifier
)
278 error(token
.loc
, "found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token
).toChars(), exp
.toChars(), token
.toChars(), peek(peek(&token
)).toChars());
282 check(TOK
.semicolon
, "statement");
283 s
= new AST
.ExpStatement(loc
, exp
);
298 //case TOK._Imaginary:
305 // storage-class-specifiers
309 case TOK
._Thread_local
:
314 // function-specifiers
324 // alignment-specifier
327 // atomic-type-specifier or type_qualifier
330 case TOK
.__attribute__
:
334 cparseDeclaration(LVL
.local
);
335 if (symbols
.length
> 1)
337 auto as
= new AST
.Statements();
338 as
.reserve(symbols
.length
);
339 foreach (d
; (*symbols
)[])
341 s
= new AST
.ExpStatement(loc
, d
);
344 s
= new AST
.CompoundDeclarationStatement(loc
, as
);
347 else if (symbols
.length
== 1)
349 auto d
= (*symbols
)[0];
350 s
= new AST
.ExpStatement(loc
, d
);
354 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
355 if (flags
& ParseStatementFlags
.scope_
)
356 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
360 case TOK
._Static_assert
: // _Static_assert ( constant-expression, string-literal ) ;
361 s
= new AST
.StaticAssertStatement(cparseStaticAssert());
367 * compound-statement:
368 * { block-item-list (opt) }
372 * block-item-list block-item
379 auto statements
= new AST
.Statements();
380 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.endOfFile
)
382 statements
.push(cparseStatement(ParseStatementFlags
.curlyScope
));
389 *pEndloc
= token
.loc
;
390 pEndloc
= null; // don't set it again
392 s
= new AST
.CompoundStatement(loc
, statements
);
393 if (flags
& (ParseStatementFlags
.scope_ | ParseStatementFlags
.curlyScope
))
394 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
395 check(TOK
.rightCurly
, "compound statement");
402 check(TOK
.leftParenthesis
);
403 auto condition
= cparseExpression();
404 check(TOK
.rightParenthesis
);
406 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
407 s
= new AST
.WhileStatement(loc
, condition
, _body
, endloc
, null);
412 /* C11 6.8.3 null statement
415 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
421 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
423 check(TOK
.leftParenthesis
);
424 auto condition
= cparseExpression();
425 check(TOK
.rightParenthesis
);
426 check(TOK
.semicolon
, "terminating `;` required after do-while statement");
427 s
= new AST
.DoStatement(loc
, _body
, condition
, token
.loc
);
434 AST
.Expression condition
;
435 AST
.Expression increment
;
438 check(TOK
.leftParenthesis
);
439 if (token
.value
== TOK
.semicolon
)
446 _init
= cparseStatement(0);
448 if (token
.value
== TOK
.semicolon
)
455 condition
= cparseExpression();
456 check(TOK
.semicolon
, "`for` condition");
458 if (token
.value
== TOK
.rightParenthesis
)
465 increment
= cparseExpression();
466 check(TOK
.rightParenthesis
);
469 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
470 s
= new AST
.ForStatement(loc
, _init
, condition
, increment
, _body
, endloc
);
477 check(TOK
.leftParenthesis
);
478 auto condition
= cparseExpression();
479 check(TOK
.rightParenthesis
);
480 auto ifbody
= cparseStatement(ParseStatementFlags
.scope_
);
481 AST
.Statement elsebody
;
482 if (token
.value
== TOK
.else_
)
485 elsebody
= cparseStatement(ParseStatementFlags
.scope_
);
489 if (condition
&& ifbody
)
490 s
= new AST
.IfStatement(loc
, null, condition
, ifbody
, elsebody
, token
.loc
);
492 s
= null; // don't propagate parsing errors
497 error("found `else` without a corresponding `if` statement");
503 check(TOK
.leftParenthesis
);
504 auto condition
= cparseExpression();
505 check(TOK
.rightParenthesis
);
506 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
507 s
= new AST
.SwitchStatement(loc
, null, condition
, _body
, false, token
.loc
);
515 auto exp
= cparseAssignExp();
516 AST
.Expression expHigh
;
517 if (token
.value
== TOK
.dotDotDot
)
519 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
522 expHigh
= cparseAssignExp();
526 if (flags
& ParseStatementFlags
.curlyScope
)
528 auto statements
= new AST
.Statements();
529 while (token
.value
!= TOK
.case_
&& token
.value
!= TOK
.default_
&& token
.value
!= TOK
.endOfFile
&& token
.value
!= TOK
.rightCurly
)
531 auto cur
= cparseStatement(ParseStatementFlags
.curlyScope
);
532 statements
.push(cur
);
534 // https://issues.dlang.org/show_bug.cgi?id=21739
535 // Stop at the last break s.t. the following non-case statements are
536 // not merged into the current case. This can happen for
537 // case 1: ... break;
538 // debug { case 2: ... }
539 if (cur
&& cur
.isBreakStatement())
542 s
= new AST
.CompoundStatement(loc
, statements
);
546 s
= cparseStatement(0);
548 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
550 s
= new AST
.CaseRangeStatement(loc
, exp
, expHigh
, s
);
552 s
= new AST
.CaseStatement(loc
, exp
, s
);
561 if (flags
& ParseStatementFlags
.curlyScope
)
563 auto statements
= new AST
.Statements();
564 while (token
.value
!= TOK
.case_
&& token
.value
!= TOK
.default_
&& token
.value
!= TOK
.endOfFile
&& token
.value
!= TOK
.rightCurly
)
566 statements
.push(cparseStatement(ParseStatementFlags
.curlyScope
));
568 s
= new AST
.CompoundStatement(loc
, statements
);
571 s
= cparseStatement(0);
572 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
573 s
= new AST
.DefaultStatement(loc
, s
);
580 * return expression ;
583 auto exp
= token
.value
== TOK
.semicolon ?
null : cparseExpression();
584 check(TOK
.semicolon
, "`return` statement");
585 s
= new AST
.ReturnStatement(loc
, exp
);
591 check(TOK
.semicolon
, "`break` statement");
592 s
= new AST
.BreakStatement(loc
, null);
597 check(TOK
.semicolon
, "`continue` statement");
598 s
= new AST
.ContinueStatement(loc
, null);
605 if (token
.value
!= TOK
.identifier
)
607 error("identifier expected following `goto`");
615 s
= new AST
.GotoStatement(loc
, ident
);
616 check(TOK
.semicolon
, "`goto` statement");
626 case TOK
.leftParenthesis
:
631 // ImportC extensions: parse as a D asm block.
632 s
= parseAsm(compileEnv
.masm
);
638 error("found `%s` instead of statement", token
.toChars());
643 if (token
.value
== TOK
.semicolon
)
650 symbols
= symbolsSave
;
651 typedefTab
.setDim(typedefTabLengthSave
);
656 /*******************************************************************************/
657 /********************************* Expression Parser ***************************/
663 * assignment-expression
664 * expression , assignment-expression
666 AST
.Expression
cparseExpression()
668 auto loc
= token
.loc
;
670 //printf("cparseExpression() loc = %d\n", loc.linnum);
671 auto e
= cparseAssignExp();
672 while (token
.value
== TOK
.comma
)
675 auto e2
= cparseAssignExp();
676 e
= new AST
.CommaExp(loc
, e
, e2
, false);
683 /*********************
685 * primary-expression:
691 * __builtin_va_arg(assign_expression, type)
693 AST
.Expression
cparsePrimaryExp()
696 const loc
= token
.loc
;
698 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
702 const id
= token
.ident
.toString();
703 if (id
.length
> 2 && id
[0] == '_' && id
[1] == '_') // leading double underscore
705 if (token
.ident
is Id
.__func__
)
707 addFuncName
= true; // implicitly declare __func__
709 else if (token
.ident
is Id
.builtin_va_arg
)
711 e
= cparseBuiltin_va_arg();
715 importBuiltins
= true; // probably one of those compiler extensions
717 e
= new AST
.IdentifierExp(loc
, token
.ident
);
721 case TOK
.charLiteral
:
722 case TOK
.int32Literal
:
723 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint32
);
727 case TOK
.uns32Literal
:
728 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns32
);
732 case TOK
.int64Literal
:
733 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint64
);
737 case TOK
.uns64Literal
:
738 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns64
);
742 case TOK
.float32Literal
:
743 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat32
);
747 case TOK
.float64Literal
:
748 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat64
);
752 case TOK
.float80Literal
:
753 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat80
);
757 case TOK
.imaginary32Literal
:
758 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary32
);
762 case TOK
.imaginary64Literal
:
763 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary64
);
767 case TOK
.imaginary80Literal
:
768 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary80
);
774 // cat adjacent strings
775 auto s
= token
.ustring
;
776 auto len
= token
.len
;
777 auto postfix
= token
.postfix
;
781 if (token
.value
== TOK
.string_
)
785 if (token
.postfix
!= postfix
)
786 error(token
.loc
, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix
, token
.postfix
);
787 postfix
= token
.postfix
;
791 const len2
= token
.len
;
793 auto s2
= cast(char*)mem
.xmalloc_noscan(len
* char.sizeof
);
794 memcpy(s2
, s
, len1
* char.sizeof
);
795 memcpy(s2
+ len1
, token
.ustring
, len2
* char.sizeof
);
801 e
= new AST
.StringExp(loc
, s
[0 .. len
], len
, 1, postfix
);
805 case TOK
.leftParenthesis
:
807 if (token
.value
== TOK
.leftCurly
)
808 e
= cparseStatementExpression(); // gcc extension
810 e
= cparseExpression();
811 check(TOK
.rightParenthesis
);
815 e
= cparseGenericSelection();
818 case TOK
._assert
: // __check(assign-exp) extension
820 check(TOK
.leftParenthesis
, "`__check`");
821 e
= parseAssignExp();
822 check(TOK
.rightParenthesis
);
823 e
= new AST
.AssertExp(loc
, e
, null);
827 error("expression expected, not `%s`", token
.toChars());
828 // Anything for e, as long as it's not NULL
829 e
= new AST
.IntegerExp(loc
, 0, AST
.Type
.tint32
);
836 /*********************************
838 * postfix-expression:
840 * postfix-expression [ expression ]
841 * postfix-expression ( argument-expression-list (opt) )
842 * postfix-expression . identifier
843 * postfix-expression -> identifier
844 * postfix-expression ++
845 * postfix-expression --
846 * ( type-name ) { initializer-list }
847 * ( type-name ) { initializer-list , }
849 * argument-expression-list:
850 * assignment-expression
851 * argument-expression-list , assignment-expression
853 private AST
.Expression
cparsePostfixExp(AST
.Expression e
)
855 e
= cparsePrimaryExp();
856 return cparsePostfixOperators(e
);
859 /********************************
861 * Parse a series of operators for a postfix expression after already parsing
862 * a primary-expression or compound literal expression.
864 * e = parsed primary or compound literal expression
866 * parsed postfix expression
868 private AST
.Expression
cparsePostfixOperators(AST
.Expression e
)
872 const loc
= token
.loc
;
877 if (token
.value
== TOK
.identifier
)
879 Identifier id
= token
.ident
;
880 e
= new AST
.DotIdExp(loc
, e
, id
);
883 error("identifier expected following `.`, not `%s`", token
.toChars());
888 if (token
.value
== TOK
.identifier
)
890 Identifier id
= token
.ident
;
891 auto die
= new AST
.DotIdExp(loc
, e
, id
);
896 error("identifier expected following `->`, not `%s`", token
.toChars());
900 e
= new AST
.PostExp(EXP
.plusPlus
, loc
, e
);
904 e
= new AST
.PostExp(EXP
.minusMinus
, loc
, e
);
907 case TOK
.leftParenthesis
:
908 e
= new AST
.CallExp(loc
, e
, cparseArguments());
911 case TOK
.leftBracket
:
913 // array dereferences:
915 AST
.Expression index
;
916 auto arguments
= new AST
.Expressions();
920 index
= cparseAssignExp();
921 arguments
.push(index
);
922 check(TOK
.rightBracket
);
924 e
= new AST
.ArrayExp(loc
, e
, arguments
);
934 /************************
938 * ++ unary-expression
939 * -- unary-expression
940 * unary-operator cast-expression
941 * sizeof unary-expression
942 * sizeof ( type-name )
943 * _Alignof ( type-name )
948 private AST
.Expression
cparseUnaryExp()
951 const loc
= token
.loc
;
957 // Parse `++` as an unary operator so that cast expressions only give
958 // an error for being non-lvalues.
960 e
= new AST
.PreExp(EXP
.prePlusPlus
, loc
, e
);
965 // Parse `--` as an unary operator, same as prefix increment.
967 e
= new AST
.PreExp(EXP
.preMinusMinus
, loc
, e
);
973 e
= new AST
.AddrExp(loc
, e
);
979 e
= new AST
.PtrExp(loc
, e
);
985 e
= new AST
.NegExp(loc
, e
);
991 e
= new AST
.UAddExp(loc
, e
);
997 e
= new AST
.NotExp(loc
, e
);
1002 e
= cparseCastExp();
1003 e
= new AST
.ComExp(loc
, e
);
1009 if (token
.value
== TOK
.leftParenthesis
)
1011 auto tk
= peek(&token
);
1014 /* Expression may be either be requesting the sizeof a type-name
1015 * or a compound literal, which requires checking whether
1016 * the next token is leftCurly
1019 auto t
= cparseTypeName();
1020 check(TOK
.rightParenthesis
);
1021 if (token
.value
== TOK
.leftCurly
)
1023 // ( type-name ) { initializer-list }
1024 auto ci
= cparseInitializer();
1025 e
= new AST
.CompoundLiteralExp(loc
, t
, ci
);
1026 e
= cparsePostfixOperators(e
);
1031 e
= new AST
.TypeExp(loc
, t
);
1036 // must be an expression
1037 e
= cparseUnaryExp();
1043 e
= cparseUnaryExp();
1046 e
= new AST
.DotIdExp(loc
, e
, Id
.__sizeof
);
1053 check(TOK
.leftParenthesis
);
1054 auto t
= cparseTypeName();
1055 check(TOK
.rightParenthesis
);
1056 e
= new AST
.TypeExp(loc
, t
);
1057 e
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
1062 e
= cparsePostfixExp(e
);
1073 * ( type-name ) cast-expression
1075 private AST
.Expression
cparseCastExp()
1077 if (token
.value
== TOK
.leftParenthesis
)
1079 //printf("cparseCastExp()\n");
1080 auto tk
= peek(&token
);
1083 if (tk
.value
== TOK
.identifier
)
1085 iscast
= isTypedef(tk
.ident
);
1090 // ( identifier ) is an expression
1091 return cparseUnaryExp();
1097 if (isCastExpression(pt
))
1099 // Expression may be either a cast or a compound literal, which
1100 // requires checking whether the next token is leftCurly
1101 const loc
= token
.loc
;
1103 auto t
= cparseTypeName();
1104 check(TOK
.rightParenthesis
);
1107 if (token
.value
== TOK
.leftCurly
)
1109 // C11 6.5.2.5 ( type-name ) { initializer-list }
1110 auto ci
= cparseInitializer();
1111 auto ce
= new AST
.CompoundLiteralExp(loc
, t
, ci
);
1112 return cparsePostfixOperators(ce
);
1117 // ( type-name ) cast-expression
1118 auto ce
= cparseCastExp();
1119 return new AST
.CastExp(loc
, ce
, t
);
1122 if (t
.isTypeIdentifier() &&
1124 token
.value
== TOK
.leftParenthesis
&&
1125 !isCastExpression(pt
))
1127 /* (t)(...)... might be a cast expression or a function call,
1128 * with different grammars: a cast would be cparseCastExp(),
1129 * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1130 * We can't know until t is known. So, parse it as a function call
1131 * and let semantic() rewrite the AST as a CastExp if it turns out
1134 auto ie
= new AST
.IdentifierExp(loc
, t
.isTypeIdentifier().ident
);
1135 ie
.parens
= true; // let semantic know it might be a CastExp
1136 AST
.Expression e
= new AST
.CallExp(loc
, ie
, cparseArguments());
1137 return cparsePostfixOperators(e
);
1140 // ( type-name ) cast-expression
1141 auto ce
= cparseCastExp();
1142 return new AST
.CastExp(loc
, ce
, t
);
1145 return cparseUnaryExp();
1150 * multiplicative-expression
1152 * multiplicative-expression * cast-expression
1153 * multiplicative-expression / cast-expression
1154 * multiplicative-expression % cast-expression
1156 private AST
.Expression
cparseMulExp()
1158 const loc
= token
.loc
;
1159 auto e
= cparseCastExp();
1163 switch (token
.value
)
1167 auto e2
= cparseCastExp();
1168 e
= new AST
.MulExp(loc
, e
, e2
);
1173 auto e2
= cparseCastExp();
1174 e
= new AST
.DivExp(loc
, e
, e2
);
1179 auto e2
= cparseCastExp();
1180 e
= new AST
.ModExp(loc
, e
, e2
);
1193 * additive-expression
1194 * multiplicative-expression
1195 * additive-expression + multiplicative-expression
1196 * additive-expression - multiplicative-expression
1198 private AST
.Expression
cparseAddExp()
1200 const loc
= token
.loc
;
1201 auto e
= cparseMulExp();
1205 switch (token
.value
)
1209 auto e2
= cparseMulExp();
1210 e
= new AST
.AddExp(loc
, e
, e2
);
1215 auto e2
= cparseMulExp();
1216 e
= new AST
.MinExp(loc
, e
, e2
);
1230 * additive-expression
1231 * shift-expression << additive-expression
1232 * shift-expression >> additive-expression
1234 private AST
.Expression
cparseShiftExp()
1236 const loc
= token
.loc
;
1237 auto e
= cparseAddExp();
1241 switch (token
.value
)
1245 auto e2
= cparseAddExp();
1246 e
= new AST
.ShlExp(loc
, e
, e2
);
1249 case TOK
.rightShift
:
1251 auto e2
= cparseAddExp();
1252 e
= new AST
.ShrExp(loc
, e
, e2
);
1265 * relational-expression
1267 * relational-expression < shift-expression
1268 * relational-expression > shift-expression
1269 * relational-expression <= shift-expression
1270 * relational-expression >= shift-expression
1272 private AST
.Expression
cparseRelationalExp()
1274 const loc
= token
.loc
;
1276 auto e
= cparseShiftExp();
1278 EXP op
= EXP
.reserved
;
1279 switch (token
.value
)
1281 case TOK
.lessThan
: op
= EXP
.lessThan
; goto Lcmp
;
1282 case TOK
.lessOrEqual
: op
= EXP
.lessOrEqual
; goto Lcmp
;
1283 case TOK
.greaterThan
: op
= EXP
.greaterThan
; goto Lcmp
;
1284 case TOK
.greaterOrEqual
: op
= EXP
.greaterOrEqual
; goto Lcmp
;
1287 auto e2
= cparseShiftExp();
1288 e
= new AST
.CmpExp(op
, loc
, e
, e2
);
1299 * equality-expression
1300 * relational-expression
1301 * equality-expression == relational-expression
1302 * equality-expression != relational-expression
1304 private AST
.Expression
cparseEqualityExp()
1306 const loc
= token
.loc
;
1308 auto e
= cparseRelationalExp();
1310 EXP op
= EXP
.reserved
;
1311 switch (token
.value
)
1313 case TOK
.equal
: op
= EXP
.equal
; goto Lequal
;
1314 case TOK
.notEqual
: op
= EXP
.notEqual
; goto Lequal
;
1317 auto e2
= cparseRelationalExp();
1318 e
= new AST
.EqualExp(op
, loc
, e
, e2
);
1330 * equality-expression
1331 * AND-expression & equality-expression
1333 private AST
.Expression
cparseAndExp()
1335 Loc loc
= token
.loc
;
1336 auto e
= cparseEqualityExp();
1337 while (token
.value
== TOK
.and)
1340 auto e2
= cparseEqualityExp();
1341 e
= new AST
.AndExp(loc
, e
, e2
);
1349 * exclusive-OR-expression
1351 * exclusive-OR-expression ^ AND-expression
1353 private AST
.Expression
cparseXorExp()
1355 const loc
= token
.loc
;
1357 auto e
= cparseAndExp();
1358 while (token
.value
== TOK
.xor)
1361 auto e2
= cparseAndExp();
1362 e
= new AST
.XorExp(loc
, e
, e2
);
1369 * inclusive-OR-expression
1370 * exclusive-OR-expression
1371 * inclusive-OR-expression | exclusive-OR-expression
1373 private AST
.Expression
cparseOrExp()
1375 const loc
= token
.loc
;
1377 auto e
= cparseXorExp();
1378 while (token
.value
== TOK
.or)
1381 auto e2
= cparseXorExp();
1382 e
= new AST
.OrExp(loc
, e
, e2
);
1389 * logical-AND-expression
1390 * inclusive-OR-expression
1391 * logical-AND-expression && inclusive-OR-expression
1393 private AST
.Expression
cparseAndAndExp()
1395 const loc
= token
.loc
;
1397 auto e
= cparseOrExp();
1398 while (token
.value
== TOK
.andAnd
)
1401 auto e2
= cparseOrExp();
1402 e
= new AST
.LogicalExp(loc
, EXP
.andAnd
, e
, e2
);
1409 * logical-OR-expression
1410 * logical-AND-expression
1411 * logical-OR-expression || logical-AND-expression
1413 private AST
.Expression
cparseOrOrExp()
1415 const loc
= token
.loc
;
1417 auto e
= cparseAndAndExp();
1418 while (token
.value
== TOK
.orOr
)
1421 auto e2
= cparseAndAndExp();
1422 e
= new AST
.LogicalExp(loc
, EXP
.orOr
, e
, e2
);
1429 * conditional-expression:
1430 * logical-OR-expression
1431 * logical-OR-expression ? expression : conditional-expression
1433 private AST
.Expression
cparseCondExp()
1435 const loc
= token
.loc
;
1437 auto e
= cparseOrOrExp();
1438 if (token
.value
== TOK
.question
)
1441 auto e1
= cparseExpression();
1443 auto e2
= cparseCondExp();
1444 e
= new AST
.CondExp(loc
, e
, e1
, e2
);
1451 * assignment-expression:
1452 * conditional-expression
1453 * unary-expression assignment-operator assignment-expression
1455 * assignment-operator:
1456 * = *= /= %= += -= <<= >>= &= ^= |=
1458 AST
.Expression
cparseAssignExp()
1461 e
= cparseCondExp(); // constrain it to being unary-expression in semantic pass
1465 const loc
= token
.loc
;
1466 switch (token
.value
)
1470 auto e2
= cparseAssignExp();
1471 e
= new AST
.AssignExp(loc
, e
, e2
);
1476 auto e2
= cparseAssignExp();
1477 e
= new AST
.AddAssignExp(loc
, e
, e2
);
1482 auto e2
= cparseAssignExp();
1483 e
= new AST
.MinAssignExp(loc
, e
, e2
);
1488 auto e2
= cparseAssignExp();
1489 e
= new AST
.MulAssignExp(loc
, e
, e2
);
1494 auto e2
= cparseAssignExp();
1495 e
= new AST
.DivAssignExp(loc
, e
, e2
);
1500 auto e2
= cparseAssignExp();
1501 e
= new AST
.ModAssignExp(loc
, e
, e2
);
1506 auto e2
= cparseAssignExp();
1507 e
= new AST
.AndAssignExp(loc
, e
, e2
);
1512 auto e2
= cparseAssignExp();
1513 e
= new AST
.OrAssignExp(loc
, e
, e2
);
1518 auto e2
= cparseAssignExp();
1519 e
= new AST
.XorAssignExp(loc
, e
, e2
);
1522 case TOK
.leftShiftAssign
:
1524 auto e2
= cparseAssignExp();
1525 e
= new AST
.ShlAssignExp(loc
, e
, e2
);
1528 case TOK
.rightShiftAssign
:
1530 auto e2
= cparseAssignExp();
1531 e
= new AST
.ShrAssignExp(loc
, e
, e2
);
1541 /***********************
1543 * _Generic ( assignment-expression, generic-assoc-list )
1545 * generic-assoc-list:
1546 * generic-association
1547 * generic-assoc-list generic-association
1549 * generic-association:
1550 * type-name : assignment-expression
1551 * default : assignment-expression
1553 private AST
.Expression
cparseGenericSelection()
1555 const loc
= token
.loc
;
1557 check(TOK
.leftParenthesis
);
1558 auto cntlExp
= cparseAssignExp();
1560 auto types
= new AST
.Types();
1561 auto exps
= new AST
.Expressions();
1566 if (token
.value
== TOK
.default_
)
1570 error("only one `default` allowed in generic-assoc-list");
1575 t
= cparseTypeName();
1579 auto e
= cparseAssignExp();
1581 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
1585 check(TOK
.rightParenthesis
);
1586 return new AST
.GenericExp(loc
, cntlExp
, types
, exps
);
1589 /***********************
1590 * C11 6.6 Constant expressions
1591 * constant-expression:
1592 * conditional-expression
1594 private AST
.Expression
cparseConstantExp()
1596 return cparseAssignExp();
1599 /*****************************
1601 * type __builtin_va_arg(assign-expression, type)
1602 * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1603 * va_arg!(type)(assign-expression);
1604 * Lexer is on `__builtin_va_arg`
1606 private AST
.Expression
cparseBuiltin_va_arg()
1608 importBuiltins
= true; // need core.stdc.stdarg
1611 check(TOK
.leftParenthesis
);
1613 auto arguments
= new AST
.Expressions();
1614 auto arg
= cparseAssignExp();
1615 arguments
.push(arg
);
1619 auto t
= cparseTypeName();
1620 auto tiargs
= new AST
.Objects();
1624 auto ti
= new AST
.TemplateInstance(loc
, Id
.va_arg
, tiargs
);
1625 auto tie
= new AST
.ScopeExp(loc
, ti
);
1627 AST
.Expression e
= new AST
.CallExp(loc
, tie
, arguments
);
1629 check(TOK
.rightParenthesis
);
1633 /*****************************
1634 * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1635 * Represent as a function literal, then call the function literal.
1636 * Parser is on opening curly brace.
1638 private AST
.Expression
cparseStatementExpression()
1640 AST
.ParameterList parameterList
;
1641 StorageClass
stc = 0;
1642 const loc
= token
.loc
;
1643 typedefTab
.push(null);
1644 auto fbody
= cparseStatement(ParseStatementFlags
.scope_
);
1645 typedefTab
.pop(); // end of function scope
1647 // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1648 auto ss
= fbody
.isScopeStatement();
1649 auto cs
= ss
.statement
.isCompoundStatement();
1651 if (const len
= (*cs
.statements
).length
)
1653 auto s
= (*cs
.statements
)[len
- 1];
1654 if (auto es
= s
.isExpStatement())
1655 (*cs
.statements
)[len
- 1] = new AST
.ReturnStatement(es
.loc
, es
.exp
);
1658 auto tf
= new AST
.TypeFunction(parameterList
, null, LINK
.d
, stc);
1659 auto fd
= new AST
.FuncLiteralDeclaration(loc
, token
.loc
, tf
, TOK
.delegate_
, null, null, 0);
1662 auto fe
= new AST
.FuncExp(loc
, fd
);
1663 auto args
= new AST
.Expressions();
1664 auto e
= new AST
.CallExp(loc
, fe
, args
); // call the function literal
1669 /********************************************************************************/
1670 /********************************* Declaration Parser ***************************/
1673 /*************************************
1676 * declaration-specifiers init-declarator-list (opt) ;
1677 * static_assert-declaration
1679 * init-declarator-list:
1681 * init-declarator-list , init-declarator
1685 * declarator = initializer
1688 * level = declaration context
1690 void cparseDeclaration(LVL level
)
1692 //printf("cparseDeclaration(level = %d)\n", level);
1693 if (token
.value
== TOK
._Static_assert
)
1695 auto s
= cparseStaticAssert();
1700 if (token
.value
== TOK
.__pragma
)
1702 uupragmaDirective(scanloc
);
1706 if (token
.value
== TOK
._import
) // import declaration extension
1708 auto a
= parseImport();
1714 const typedefTabLengthSave
= typedefTab
.length
;
1715 auto symbolsSave
= symbols
;
1716 Specifier specifier
;
1717 specifier
.packalign
= this.packalign
;
1718 auto tspec
= cparseDeclarationSpecifiers(level
, specifier
);
1720 AST
.Dsymbol
declareTag(AST
.TypeTag tt
, ref Specifier specifier
)
1722 /* `struct tag;` and `struct tag { ... };`
1723 * always result in a declaration in the current scope
1725 auto stag
= (tt
.tok
== TOK
.struct_
) ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false) :
1726 (tt
.tok
== TOK
.union_
) ?
new AST
.UnionDeclaration(tt
.loc
, tt
.id
) :
1727 new AST
.EnumDeclaration(tt
.loc
, tt
.id
, tt
.base
);
1728 if (!tt
.packalign
.isUnknown())
1730 // saw `struct __declspec(align(N)) Tag ...`
1731 auto st
= stag
.isStructDeclaration();
1732 st
.alignment
= tt
.packalign
;
1734 stag
.members
= tt
.members
;
1737 symbols
= new AST
.Dsymbols();
1738 auto stags
= applySpecifier(stag
, specifier
);
1739 symbols
.push(stags
);
1743 /* If a declarator does not follow, it is unnamed
1745 if (token
.value
== TOK
.semicolon
)
1750 return; // accept empty declaration as an extension
1753 if (auto ti
= tspec
.isTypeIdentifier())
1756 error("type-specifier missing for declaration of `%s`", ti
.ident
.toChars());
1762 auto tt
= tspec
.isTypeTag();
1764 !tt
.id
&& (tt
.tok
== TOK
.struct_ || tt
.tok
== TOK
.union_
))
1765 return; // legal but meaningless empty declaration, ignore it
1767 auto stags
= declareTag(tt
, specifier
);
1769 if (0 && tt
.tok
== TOK
.enum_
) // C11 proscribes enums with no members, but we allow it
1772 error(tt
.loc
, "`enum %s` has no members", stags
.toChars());
1779 error("no type for declarator before `%s`", token
.toChars());
1785 if (tspec
&& specifier
.mod
& MOD
.xconst
)
1787 tspec
= toConst(tspec
);
1788 specifier
.mod
&= ~MOD
.xnone
; // 'used' it
1791 void scanPastSemicolon()
1793 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
1798 if (token
.value
== TOK
.assign
&& tspec
&& tspec
.isTypeIdentifier())
1801 * Special check for `const b = 1;` because some compilers allow it
1803 error("type-specifier omitted for declaration of `%s`", tspec
.isTypeIdentifier().ident
.toChars());
1804 return scanPastSemicolon();
1811 AST
.StringExp asmName
;
1812 auto dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
1817 break; // error recovery
1822 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1823 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1825 switch (token
.value
)
1831 case TOK
.__attribute__
:
1832 if (token
.value
== TOK
.asm_
)
1833 asmName
= cparseGnuAsmLabel();
1834 if (token
.value
== TOK
.__attribute__
)
1836 cparseGnuAttributes(specifier
);
1837 if (token
.value
== TOK
.leftCurly
)
1838 break; // function definition
1840 /* This is a data definition, there cannot now be a
1841 * function definition.
1850 if (specifier
.alignExps
&& dt.isTypeFunction())
1851 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1852 if (specifier
.alignExps
&& specifier
.scw
== SCW
.xregister
)
1853 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1855 /* C11 6.9.1 Function Definitions
1856 * function-definition:
1857 * declaration-specifiers declarator declaration-list (opt) compound-statement
1861 * declaration-list declaration
1864 if (first
&& // first declarator
1866 dt.isTypeFunction() && // function type not inherited from a typedef
1867 isDeclarationList(t
) && // optional declaration-list
1868 level
== LVL
.global
&& // function definitions only at global scope
1869 t
.value
== TOK
.leftCurly
) // start of compound-statement
1871 auto s
= cparseFunctionDefinition(id
, dt.isTypeFunction(), specifier
);
1872 typedefTab
.setDim(typedefTabLengthSave
);
1873 symbols
= symbolsSave
;
1877 AST
.Dsymbol s
= null;
1878 typedefTab
.setDim(typedefTabLengthSave
);
1879 symbols
= symbolsSave
;
1881 symbols
= new AST
.Dsymbols
; // lazilly create it
1883 if (level
!= LVL
.global
&& !tspec
&& !specifier
.scw
&& !specifier
.mod
)
1884 error("declaration-specifier-seq required");
1885 else if (specifier
.scw
== SCW
.xtypedef
)
1887 if (token
.value
== TOK
.assign
)
1888 error("no initializer for typedef declaration");
1889 if (specifier
.alignExps
)
1890 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1892 bool isalias
= true;
1893 if (auto ts
= dt.isTypeStruct())
1895 if (ts
.sym
.isAnonymous())
1897 // This is a typedef for an anonymous struct-or-union.
1898 // Directly set the ident for the struct-or-union.
1903 else if (auto te
= dt.isTypeEnum())
1905 if (te
.sym
.isAnonymous())
1907 // This is a typedef for an anonymous enum.
1912 else if (auto tt
= dt.isTypeTag())
1914 if (tt
.id || tt
.tok
== TOK
.enum_
)
1917 /* This applies for enums declared as
1918 * typedef enum {A} E;
1922 declareTag(tt
, spec
);
1927 auto ad
= new AST
.AliasDeclaration(token
.loc
, id
, dt);
1928 ad
.adFlags |
= ad
.hidden
; // do not print when generating .di files
1932 insertTypedefToTypedefTab(id
, dt); // remember typedefs
1936 if (auto tt
= dt.isTypeTag())
1938 if (tt
.members
&& (tt
.id || tt
.tok
== TOK
.enum_
))
1941 declareTag(tt
, spec
);
1945 if (level
== LVL
.prototype
)
1946 break; // declared later as Parameter, not VarDeclaration
1948 if (dt.ty
== AST
.Tvoid
)
1949 error("`void` has no value");
1951 AST
.Initializer initializer
;
1952 bool hasInitializer
;
1953 if (token
.value
== TOK
.assign
)
1956 hasInitializer
= true;
1957 initializer
= cparseInitializer();
1959 // declare the symbol
1962 if (isFunctionTypedef(dt))
1965 error("no initializer for function declaration");
1966 if (specifier
.scw
& SCW
.x_Thread_local
)
1967 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1968 StorageClass
stc = specifiersToSTC(level
, specifier
);
1969 stc &= ~STC
.gshared
; // no gshared functions
1970 auto fd
= new AST
.FuncDeclaration(token
.loc
, Loc
.initial
, id
, stc, dt, specifier
.noreturn
);
1971 specifiersToFuncDeclaration(fd
, specifier
);
1976 // Give non-extern variables an implicit void initializer
1977 // if one has not been explicitly set.
1978 if (!hasInitializer
&&
1979 !(specifier
.scw
& (SCW
.xextern | SCW
.xstatic | SCW
.x_Thread_local
) || level
== LVL
.global
))
1980 initializer
= new AST
.VoidInitializer(token
.loc
);
1981 auto vd
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(level
, specifier
));
1982 specifiersToVarDeclaration(vd
, specifier
);
1985 if (level
!= LVL
.global
)
1986 insertIdToTypedefTab(id
); // non-typedef declarations can hide typedefs in outer scopes
1990 // Saw `asm("name")` in the function, type, or variable definition.
1991 // This is equivalent to `pragma(mangle, "name")` in D
1995 https://issues.dlang.org/show_bug.cgi?id=23012
1996 Ideally this would be translated to a pragma(mangle)
1997 decl. This is not possible because ImportC symbols are
1998 (currently) merged before semantic analysis is performed,
1999 so the pragma(mangle) never effects any change on the declarations
2002 Writing to mangleOverride directly avoids this, and is possible
2003 because C only a StringExp is allowed unlike a full fat pragma(mangle)
2004 which is more liberal.
2006 if (auto p
= s
.isDeclaration())
2008 auto str = asmName
.peekString();
2009 p
.mangleOverride
= str;
2010 // p.adFlags |= AST.VarDeclaration.nounderscore;
2011 p
.adFlags |
= 4; // cannot get above line to compile on Ubuntu
2014 s
= applySpecifier(s
, specifier
);
2015 if (level
== LVL
.local
)
2017 // Wrap the declaration in `extern (C) { declaration }`
2018 // Necessary for function pointers, but harmless to apply to all.
2019 auto decls
= new AST
.Dsymbols(1);
2021 s
= new AST
.LinkDeclaration(s
.loc
, linkage
, decls
);
2027 switch (token
.value
)
2029 case TOK
.identifier
:
2032 error(token
.loc
, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s
.toChars(), token
.toChars());
2043 symbolsSave
= symbols
;
2048 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token
.toChars());
2050 return scanPastSemicolon();
2055 /***************************************
2056 * C11 Function Definitions
2057 * function-definition
2058 * declaration-specifiers declarator declaration-list (opt) compound-statement
2062 * declaration-list declaration
2064 * It's already been parsed up to the declaration-list (opt).
2065 * Pick it up from there.
2067 * id = function identifier
2068 * ft = function type
2069 * specifier = function specifiers
2071 * Dsymbol for the function
2073 AST
.Dsymbol
cparseFunctionDefinition(Identifier id
, AST
.TypeFunction ft
, ref Specifier specifier
)
2075 /* Start function scope
2077 typedefTab
.push(null);
2079 if (token
.value
!= TOK
.leftCurly
) // if not start of a compound-statement
2081 // Do declaration-list
2084 cparseDeclaration(LVL
.parameter
);
2085 } while (token
.value
!= TOK
.leftCurly
);
2087 /* Since there were declarations, the parameter-list must have been
2088 * an identifier-list.
2090 ft
.parameterList
.hasIdentifierList
= true; // semantic needs to know to adjust parameter types
2091 auto pl
= ft
.parameterList
;
2092 if (pl
.varargs
!= AST
.VarArg
.none
&& pl
.length
)
2093 error("function identifier-list cannot end with `...`");
2094 ft
.parameterList
.varargs
= AST
.VarArg
.KRvariadic
; // but C11 allows extra arguments
2095 auto plLength
= pl
.length
;
2096 if (symbols
.length
!= plLength
)
2097 error(token
.loc
, "%d identifiers does not match %d declarations", cast(int)plLength
, cast(int)symbols
.length
);
2099 /* Transfer the types and storage classes from symbols[] to pl[]
2101 foreach (i
; 0 .. plLength
)
2103 auto p
= pl
[i
]; // yes, quadratic
2105 // Convert typedef-identifier to identifier
2108 if (auto t
= p
.type
.isTypeIdentifier())
2115 if (p
.type ||
!(p
.storageClass
& STC
.parameter
))
2116 error("storage class and type are not allowed in identifier-list");
2117 foreach (s
; (*symbols
)[]) // yes, quadratic
2119 auto ad
= s
.isAttribDeclaration();
2121 s
= (*ad
.decl
)[0]; // AlignDeclaration wrapping the declaration
2123 auto d
= s
.isDeclaration();
2124 if (d
&& p
.ident
== d
.ident
&& d
.type
)
2127 p
.storageClass
= d
.storage_class
;
2128 d
.type
= null; // don't reuse
2134 error("no declaration for identifier `%s`", p
.ident
.toChars());
2135 p
.type
= AST
.Type
.terror
;
2140 addFuncName
= false; // gets set to true if somebody references __func__ in this function
2141 const locFunc
= token
.loc
;
2143 auto body = cparseStatement(ParseStatementFlags
.curly
); // don't start a new scope; continue with parameter scope
2144 typedefTab
.pop(); // end of function scope
2146 StorageClass
stc = specifiersToSTC(LVL
.global
, specifier
);
2147 stc &= ~STC
.gshared
; // no gshared functions
2148 auto fd
= new AST
.FuncDeclaration(locFunc
, prevloc
, id
, stc, ft
, specifier
.noreturn
);
2149 specifiersToFuncDeclaration(fd
, specifier
);
2153 auto s
= createFuncName(locFunc
, id
);
2154 body = new AST
.CompoundStatement(locFunc
, s
, body);
2158 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2163 /***************************************
2164 * C11 Initialization
2166 * assignment-expression
2167 * { initializer-list }
2168 * { initializer-list , }
2171 * designation (opt) initializer
2172 * initializer-list , designation (opt) initializer
2179 * designator-list designator
2182 * [ constant-expression ]
2187 AST
.Initializer
cparseInitializer()
2189 if (token
.value
!= TOK
.leftCurly
)
2191 auto ae
= cparseAssignExp(); // assignment-expression
2192 return new AST
.ExpInitializer(token
.loc
, ae
);
2195 const loc
= token
.loc
;
2197 /* Collect one or more `designation (opt) initializer`
2198 * into ci.initializerList, but lazily create ci
2200 AST
.CInitializer ci
;
2203 /* There can be 0 or more designators preceding an initializer.
2204 * Collect them in desigInit
2206 AST
.DesigInit desigInit
;
2209 if (token
.value
== TOK
.leftBracket
) // [ constant-expression ]
2212 auto e
= cparseConstantExp();
2213 check(TOK
.rightBracket
);
2214 if (!desigInit
.designatorList
)
2215 desigInit
.designatorList
= new AST
.Designators
;
2216 desigInit
.designatorList
.push(AST
.Designator(e
));
2218 else if (token
.value
== TOK
.dot
) // . identifier
2221 if (token
.value
!= TOK
.identifier
)
2223 error("identifier expected following `.` designator");
2226 if (!desigInit
.designatorList
)
2227 desigInit
.designatorList
= new AST
.Designators
;
2228 desigInit
.designatorList
.push(AST
.Designator(token
.ident
));
2233 if (desigInit
.designatorList
)
2239 desigInit
.initializer
= cparseInitializer();
2241 ci
= new AST
.CInitializer(loc
);
2242 ci
.initializerList
.push(desigInit
);
2243 if (token
.value
== TOK
.comma
)
2246 if (token
.value
!= TOK
.rightCurly
)
2251 check(TOK
.rightCurly
);
2252 //printf("ci: %s\n", ci.toChars());
2256 /*************************************
2258 * declaration-specifier:
2259 * storage-class-specifier declaration-specifiers (opt)
2260 * type-specifier declaration-specifiers (opt)
2261 * type-qualifier declaration-specifiers (opt)
2262 * function-specifier declaration-specifiers (opt)
2263 * alignment-specifier declaration-specifiers (opt)
2265 * level = declaration context
2266 * specifier = specifiers in and out
2268 * resulting type, null if not specified
2270 private AST
.Type
cparseDeclarationSpecifiers(LVL level
, ref Specifier specifier
)
2289 ximaginary
= 0x8000,
2297 //printf("parseDeclarationSpecifiers()\n");
2300 SCW scw
= specifier
.scw
& SCW
.xtypedef
;
2308 //printf("token %s\n", token.toChars());
2312 switch (token
.value
)
2314 // Storage class specifiers
2315 case TOK
.static_
: scwx
= SCW
.xstatic
; break;
2316 case TOK
.extern_
: scwx
= SCW
.xextern
; break;
2317 case TOK
.auto_
: scwx
= SCW
.xauto
; break;
2318 case TOK
.register
: scwx
= SCW
.xregister
; break;
2319 case TOK
.typedef_
: scwx
= SCW
.xtypedef
; break;
2320 case TOK
.inline
: scwx
= SCW
.xinline
; break;
2321 case TOK
._Noreturn
: scwx
= SCW
.x_Noreturn
; break;
2323 case TOK
._Thread_local
: scwx
= SCW
.x_Thread_local
; break;
2326 case TOK
.const_
: modx
= MOD
.xconst
; break;
2327 case TOK
.volatile: modx
= MOD
.xvolatile
; break;
2328 case TOK
.restrict
: modx
= MOD
.xrestrict
; break;
2329 case TOK
.__stdcall
: modx
= MOD
.x__stdcall
; break;
2332 case TOK
.char_
: tkwx
= TKW
.xchar
; break;
2333 case TOK
.signed
: tkwx
= TKW
.xsigned
; break;
2334 case TOK
.unsigned
: tkwx
= TKW
.xunsigned
; break;
2335 case TOK
.int16
: tkwx
= TKW
.xshort
; break;
2336 case TOK
.int32
: tkwx
= TKW
.xint
; break;
2337 case TOK
.int64
: tkwx
= TKW
.xlong
; break;
2338 case TOK
.__int128
: tkwx
= TKW
.xint128
; break;
2339 case TOK
.float32
: tkwx
= TKW
.xfloat
; break;
2340 case TOK
.float64
: tkwx
= TKW
.xdouble
; break;
2341 case TOK
.void_
: tkwx
= TKW
.xvoid
; break;
2342 case TOK
._Bool
: tkwx
= TKW
.xbool
; break;
2343 case TOK
._Imaginary
: tkwx
= TKW
.ximaginary
; break;
2344 case TOK
._Complex
: tkwx
= TKW
.xcomplex
; break;
2346 case TOK
.identifier
:
2354 const structOrUnion
= token
.value
;
2355 const sloc
= token
.loc
;
2358 Specifier tagSpecifier
;
2361 * struct-or-union-specifier:
2362 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2363 * struct-or-union gnu-attribute (opt) identifier
2367 if (token
.value
== TOK
.__attribute__
)
2368 cparseGnuAttributes(tagSpecifier
);
2369 else if (token
.value
== TOK
.__declspec
)
2370 cparseDeclspec(tagSpecifier
);
2371 else if (token
.value
== TOK
.__pragma
)
2372 uupragmaDirective(sloc
);
2376 t
= cparseStruct(sloc
, structOrUnion
, tagSpecifier
.packalign
, symbols
);
2382 t
= cparseEnum(symbols
);
2389 // type-specifier if followed by `( type-name )`
2390 auto tk
= peek(&token
);
2391 if (tk
.value
== TOK
.leftParenthesis
)
2394 if (isTypeName(tk
) && tk
.value
== TOK
.rightParenthesis
)
2398 t
= cparseTypeName();
2399 tkwx
= TKW
.x_Atomic
;
2403 // C11 6.7.3 type-qualifier if not
2404 modx
= MOD
.x_Atomic
;
2411 * _Alignas ( type-name )
2412 * _Alignas ( constant-expression )
2415 if (level
& (LVL
.parameter | LVL
.prototype
))
2416 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2419 check(TOK
.leftParenthesis
);
2422 if (isTypeName(tk
)) // _Alignas ( type-name )
2424 auto talign
= cparseTypeName();
2425 /* Convert type to expression: `talign.alignof`
2427 auto e
= new AST
.TypeExp(loc
, talign
);
2428 exp
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
2430 else // _Alignas ( constant-expression )
2432 exp
= cparseConstantExp();
2435 if (!specifier
.alignExps
)
2436 specifier
.alignExps
= new AST
.Expressions(0);
2437 specifier
.alignExps
.push(exp
);
2439 check(TOK
.rightParenthesis
);
2443 case TOK
.__attribute__
:
2446 * declaration-specifiers:
2447 * gnu-attributes declaration-specifiers (opt)
2449 cparseGnuAttributes(specifier
);
2453 case TOK
.__declspec
:
2455 /* Microsoft extension
2457 cparseDeclspec(specifier
);
2464 check(TOK
.leftParenthesis
);
2469 e
= new AST
.TypeExp(loc
, cparseTypeName());
2471 e
= cparseExpression();
2472 t
= new AST
.TypeTypeof(loc
, e
);
2474 if(token
.value
== TOK
.rightParenthesis
)
2478 t
= AST
.Type
.terror
;
2479 error("`typeof` operator expects an expression or type name in parentheses");
2481 // skipParens et. al expect to be on the opening parenthesis
2487 case TOK
.leftParenthesis
:
2490 case TOK
.rightParenthesis
:
2513 if (tkw
& TKW
.xlong
&& tkwx
& TKW
.xlong
)
2518 if (tkw
&& tkwx
& TKW
.xident
)
2520 // 2nd identifier can't be a typedef
2521 break Lwhile
; // leave parser on the identifier for the following declarator
2523 else if (tkwx
& TKW
.xident
)
2525 // 1st identifier, save it for TypeIdentifier
2528 if (tkw
& TKW
.xident
&& tkwx ||
// typedef-name followed by type-specifier
2529 tkw
& tkwx
) // duplicate type-specifiers
2531 error("illegal combination of type specifiers");
2535 if (!(tkwx
& TKW
.xtag
)) // if parser already advanced
2550 error("duplicate storage class");
2552 // C11 6.7.1-2 At most one storage-class may be given, except that
2553 // _Thread_local may appear with static or extern.
2554 const scw2
= scw
& (SCW
.xstatic | SCW
.xextern | SCW
.xauto | SCW
.xregister | SCW
.xtypedef
);
2555 if (scw2
& (scw2
- 1) ||
2556 scw
& (SCW
.x_Thread_local
) && scw
& (SCW
.xauto | SCW
.xregister | SCW
.xtypedef
))
2558 error("multiple storage classes in declaration specifiers");
2561 if (level
== LVL
.local
&&
2562 scw
& (SCW
.x_Thread_local
) && scw
& (SCW
.xinline | SCW
.x_Noreturn
))
2564 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2567 if (level
== LVL
.local
&&
2568 scw
& (SCW
.x_Thread_local
) && !(scw
& (SCW
.xstatic | SCW
.xextern
)))
2570 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2573 if (level
& (LVL
.parameter | LVL
.prototype
) &&
2574 scw
& ~SCW
.xregister
)
2576 error("only `register` storage class allowed for function parameters");
2579 if (level
== LVL
.global
&&
2580 scw
& (SCW
.xauto | SCW
.xregister
))
2582 error("`auto` and `register` storage class not allowed for global");
2590 specifier
.scw
= scw
;
2591 specifier
.mod
= mod
;
2593 // Convert TKW bits to type t
2596 case TKW
.xnone
: t
= null; break;
2598 case TKW
.xchar
: t
= AST
.Type
.tchar
; break;
2599 case TKW
.xsigned | TKW
.xchar
: t
= AST
.Type
.tint8
; break;
2600 case TKW
.xunsigned | TKW
.xchar
: t
= AST
.Type
.tuns8
; break;
2603 case TKW
.xsigned | TKW
.xshort
:
2604 case TKW
.xsigned | TKW
.xshort | TKW
.xint
:
2605 case TKW
.xshort | TKW
.xint
: t
= integerTypeForSize(shortsize
); break;
2607 case TKW
.xunsigned | TKW
.xshort | TKW
.xint
:
2608 case TKW
.xunsigned | TKW
.xshort
: t
= unsignedTypeForSize(shortsize
); break;
2612 case TKW
.xsigned | TKW
.xint
: t
= integerTypeForSize(intsize
); break;
2615 case TKW
.xunsigned | TKW
.xint
: t
= unsignedTypeForSize(intsize
); break;
2618 case TKW
.xsigned | TKW
.xlong
:
2619 case TKW
.xsigned | TKW
.xlong | TKW
.xint
:
2620 case TKW
.xlong | TKW
.xint
: t
= integerTypeForSize(longsize
); break;
2622 case TKW
.xunsigned | TKW
.xlong | TKW
.xint
:
2623 case TKW
.xunsigned | TKW
.xlong
: t
= unsignedTypeForSize(longsize
); break;
2626 case TKW
.xsigned | TKW
.xllong
:
2627 case TKW
.xsigned | TKW
.xllong | TKW
.xint
:
2628 case TKW
.xllong | TKW
.xint
: t
= integerTypeForSize(long_longsize
); break;
2630 case TKW
.xunsigned | TKW
.xllong | TKW
.xint
:
2631 case TKW
.xunsigned | TKW
.xllong
: t
= unsignedTypeForSize(long_longsize
); break;
2634 case TKW
.xsigned | TKW
.xint128
: t
= integerTypeForSize(16); break;
2636 case TKW
.xunsigned | TKW
.xint128
: t
= unsignedTypeForSize(16); break;
2638 case TKW
.xvoid
: t
= AST
.Type
.tvoid
; break;
2639 case TKW
.xbool
: t
= boolsize
== 1 ? AST
.Type
.tbool
: integerTypeForSize(boolsize
); break;
2641 case TKW
.xfloat
: t
= AST
.Type
.tfloat32
; break;
2642 case TKW
.xdouble
: t
= AST
.Type
.tfloat64
; break;
2643 case TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.realfloat
); break;
2645 case TKW
.ximaginary | TKW
.xfloat
: t
= AST
.Type
.timaginary32
; break;
2646 case TKW
.ximaginary | TKW
.xdouble
: t
= AST
.Type
.timaginary64
; break;
2647 case TKW
.ximaginary | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.imaginary
); break;
2649 case TKW
.xcomplex | TKW
.xfloat
: t
= AST
.Type
.tcomplex32
; break;
2650 case TKW
.xcomplex | TKW
.xdouble
: t
= AST
.Type
.tcomplex64
; break;
2651 case TKW
.xcomplex | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.complex
); break;
2655 const idx
= previd
.toString();
2656 if (idx
.length
> 2 && idx
[0] == '_' && idx
[1] == '_') // leading double underscore
2657 importBuiltins
= true; // probably one of those compiler extensions
2660 /* Punch through to what the typedef is, to support things like:
2663 auto pt
= lookupTypedef(previd
);
2664 if (pt
&& *pt
) // if previd is a known typedef
2668 t
= new AST
.TypeIdentifier(loc
, previd
);
2673 case TKW
.x_Atomic
: // no atomics for you
2674 break; // t is already set
2677 error("illegal type combination");
2678 t
= AST
.Type
.terror
;
2685 /********************************
2687 * Parse a declarator (including function definitions).
2689 * pointer (opt) direct-declarator
2691 * direct-declarator :
2694 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2695 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2696 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2697 * direct-declarator [ type-qualifier-list (opt) * ]
2698 * direct-declarator ( parameter-type-list )
2699 * direct-declarator ( identifier-list (opt) )
2702 * * type-qualifier-list (opt)
2703 * * type-qualifier-list (opt) pointer
2705 * type-qualifier-list :
2707 * type-qualifier-list type-qualifier
2709 * parameter-type-list :
2711 * parameter-list , ...
2714 * parameter-declaration
2715 * parameter-list , parameter-declaration
2717 * parameter-declaration :
2718 * declaration-specifiers declarator
2719 * declaration-specifiers abstract-declarator (opt)
2723 * identifier-list , identifier
2726 * declarator = declarator kind
2727 * tbase = base type to start with
2728 * pident = set to Identifier if there is one, null if not
2729 * specifier = specifiers in and out
2731 * type declared. If a TypeFunction is returned, this.symbols is the
2732 * symbol table for the parameter-type-list, which will contain any
2733 * declared struct, union or enum tags.
2735 private AST
.Type
cparseDeclarator(DTR declarator
, AST
.Type tbase
,
2736 out Identifier pident
, ref Specifier specifier
)
2738 //printf("cparseDeclarator(%d, %p)\n", declarator, t);
2739 AST
.Types constTypes
; // all the Types that will need `const` applied to them
2741 /* Insert tx -> t into
2744 * ts -> ... -> tx -> t
2746 static void insertTx(ref AST
.Type ts
, AST
.Type tx
, AST
.Type t
)
2749 for (pt
= &ts
; *pt
!= t
; pt
= &(cast(AST
.TypeNext
)*pt
).next
)
2755 AST
.Type
parseDecl(AST
.Type t
)
2760 switch (token
.value
)
2762 case TOK
.identifier
: // identifier
2763 //printf("identifier %s\n", token.ident.toChars());
2764 if (declarator
== DTR
.xabstract
)
2765 error("identifier not allowed in abstract-declarator");
2766 pident
= token
.ident
;
2771 case TOK
.leftParenthesis
: // ( declarator )
2777 if (token
.value
== TOK
.__stdcall
) // T (__stdcall*fp)();
2779 specifier
.mod |
= MOD
.x__stdcall
;
2784 check(TOK
.rightParenthesis
);
2787 case TOK
.mul: // pointer
2788 t
= new AST
.TypePointer(t
);
2790 // add post fixes const/volatile/restrict/_Atomic
2791 const mod
= cparseTypeQualifierList();
2792 if (mod
& MOD
.xconst
)
2794 if (token
.value
== TOK
.__attribute__
)
2795 cparseGnuAttributes(specifier
);
2799 if (declarator
== DTR
.xdirect
)
2801 if (!t || t
.isTypeIdentifier())
2804 error("no type-specifier for declarator");
2805 t
= AST
.Type
.tint32
;
2808 error("identifier or `(` expected"); // )
2817 // parse DeclaratorSuffixes
2820 switch (token
.value
)
2822 case TOK
.leftBracket
:
2824 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2828 auto mod
= cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2832 if (token
.value
== TOK
.static_
)
2834 isStatic
= true; // `static`
2836 if (!mod
) // type qualifiers after `static`
2837 mod
= cparseTypeQualifierList();
2839 else if (token
.value
== TOK
.mul)
2841 if (peekNext() == TOK
.rightBracket
)
2843 isVLA
= true; // `*`
2848 if (isStatic || token
.value
!= TOK
.rightBracket
)
2850 //printf("It's a static array\n");
2851 AST
.Expression e
= cparseAssignExp(); // [ expression ]
2852 ta
= new AST
.TypeSArray(t
, e
);
2856 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2858 ta
= new AST
.TypeSArray(t
);
2860 check(TOK
.rightBracket
);
2862 // Issue errors for unsupported types.
2863 if (isVLA
) // C11 6.7.6.2
2865 error("variable length arrays are not supported");
2867 if (isStatic
) // C11 6.7.6.3
2869 error("static array parameters are not supported");
2871 if (declarator
!= DTR
.xparameter
)
2873 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2876 error("variable length array used outside of function prototype");
2877 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2878 * in a declaration of a function parameter with an array type.
2880 if (isStatic || mod
)
2881 error("static or type qualifier used outside of function prototype");
2883 if (ts
.isTypeSArray() || ts
.isTypeDArray())
2885 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2886 * in the outermost array type derivation.
2888 if (isStatic || mod
)
2889 error("static or type qualifier used in non-outermost array type derivation");
2890 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2893 if (ta
.isTypeSArray() && ta
.isTypeSArray().isIncomplete() && !isVLA
)
2894 error("array type has incomplete element type `%s`", ta
.toChars());
2897 // Apply type qualifiers to the constructed type.
2898 if (mod
& MOD
.xconst
) // ignore the other bits
2900 insertTx(ts
, ta
, t
); // ts -> ... -> ta -> t
2904 case TOK
.leftParenthesis
:
2906 // New symbol table for parameter-list
2907 auto symbolsSave
= this.symbols
;
2908 this.symbols
= null;
2910 auto parameterList
= cparseParameterList();
2911 const lkg
= specifier
.mod
& MOD
.x__stdcall ? LINK
.windows
: linkage
;
2912 StorageClass
stc = specifier
._nothrow ? STC
.nothrow_
: 0;
2913 if (specifier
._pure
)
2915 AST
.Type tf
= new AST
.TypeFunction(parameterList
, t
, lkg
, stc);
2916 // tf = tf.addSTC(storageClass); // TODO
2917 insertTx(ts
, tf
, t
); // ts -> ... -> tf -> t
2920 this.symbols
= symbolsSave
;
2932 auto t
= parseDecl(tbase
);
2934 if (specifier
.vector_size
)
2936 auto length
= new AST
.IntegerExp(token
.loc
, specifier
.vector_size
/ tbase
.size(), AST
.Type
.tuns32
);
2937 auto tsa
= new AST
.TypeSArray(tbase
, length
);
2938 AST
.Type tv
= new AST
.TypeVector(tsa
);
2939 specifier
.vector_size
= 0; // used it up
2941 insertTx(t
, tv
, tbase
); // replace tbase with tv
2944 /* Because const is transitive, cannot assemble types from
2945 * fragments. Instead, types to be annotated with const are put
2946 * in constTypes[], and a bottom up scan of t is done to apply
2949 if (constTypes
.length
)
2951 AST
.Type
constApply(AST
.Type t
)
2955 auto tn
= cast(AST
.TypeNext
)t
; // t.nextOf() should return a ref instead of this
2956 tn
.next
= constApply(tn
.next
);
2958 foreach (tc
; constTypes
[])
2968 if (declarator
== DTR
.xparameter
&&
2971 /* Because there are instances in .h files of "const pointer to mutable",
2972 * skip applying transitive `const`
2973 * https://issues.dlang.org/show_bug.cgi?id=22534
2975 auto tn
= cast(AST
.TypeNext
)t
;
2976 tn
.next
= constApply(tn
.next
);
2982 //printf("result: %s\n", t.toChars());
2986 /******************************
2995 MOD
cparseTypeQualifierList()
3000 switch (token
.value
)
3002 case TOK
.const_
: mod |
= MOD
.xconst
; break;
3003 case TOK
.volatile: mod |
= MOD
.xvolatile
; break;
3004 case TOK
.restrict
: mod |
= MOD
.xrestrict
; break;
3005 case TOK
._Atomic
: mod |
= MOD
.x_Atomic
; break;
3006 case TOK
.__stdcall
: mod |
= MOD
.x__stdcall
; break;
3015 /***********************************
3018 AST
.Type
cparseTypeName()
3020 Specifier specifier
;
3021 specifier
.packalign
.setDefault();
3022 auto tspec
= cparseSpecifierQualifierList(LVL
.global
, specifier
);
3025 error("type-specifier is missing");
3026 tspec
= AST
.Type
.tint32
;
3028 if (tspec
&& specifier
.mod
& MOD
.xconst
)
3030 tspec
= toConst(tspec
);
3031 specifier
.mod
= MOD
.xnone
; // 'used' it
3034 return cparseDeclarator(DTR
.xabstract
, tspec
, id
, specifier
);
3037 /***********************************
3039 * specifier-qualifier-list:
3040 * type-specifier specifier-qualifier-list (opt)
3041 * type-qualifier specifier-qualifier-list (opt)
3043 * level = declaration context
3044 * specifier = specifiers in and out
3046 * resulting type, null if not specified
3048 AST
.Type
cparseSpecifierQualifierList(LVL level
, ref Specifier specifier
)
3050 auto t
= cparseDeclarationSpecifiers(level
, specifier
);
3052 error("storage class not allowed in specifier-qualified-list");
3056 /***********************************
3058 * ( parameter-type-list )
3059 * ( identifier-list (opt) )
3061 AST
.ParameterList
cparseParameterList()
3063 auto parameters
= new AST
.Parameters();
3064 AST
.VarArg varargs
= AST
.VarArg
.none
;
3065 StorageClass varargsStc
;
3067 check(TOK
.leftParenthesis
);
3068 if (token
.value
== TOK
.void_
&& peekNext() == TOK
.rightParenthesis
) // func(void)
3072 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3075 if (token
.value
== TOK
.rightParenthesis
) // func()
3078 return AST
.ParameterList(parameters
, AST
.VarArg
.KRvariadic
, varargsStc
);
3081 /* Create function prototype scope
3083 typedefTab
.push(null);
3085 AST
.ParameterList
finish()
3088 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3091 /* The check for identifier-list comes later,
3092 * when doing the trailing declaration-list (opt)
3096 if (token
.value
== TOK
.rightParenthesis
)
3098 if (token
.value
== TOK
.dotDotDot
)
3100 if (parameters
.length
== 0) // func(...)
3101 error("named parameter required before `...`");
3102 importBuiltins
= true; // will need __va_list_tag
3103 varargs
= AST
.VarArg
.variadic
; // C-style variadics
3105 check(TOK
.rightParenthesis
);
3109 Specifier specifier
;
3110 specifier
.packalign
.setDefault();
3111 auto tspec
= cparseDeclarationSpecifiers(LVL
.prototype
, specifier
);
3114 error("no type-specifier for parameter");
3115 tspec
= AST
.Type
.tint32
;
3118 if (specifier
.mod
& MOD
.xconst
)
3120 if ((token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.comma
) &&
3121 tspec
.isTypeIdentifier())
3122 error("type-specifier omitted for parameter `%s`", tspec
.isTypeIdentifier().ident
.toChars());
3124 tspec
= toConst(tspec
);
3125 specifier
.mod
= MOD
.xnone
; // 'used' it
3129 const paramLoc
= token
.loc
;
3130 auto t
= cparseDeclarator(DTR
.xparameter
, tspec
, id
, specifier
);
3131 if (token
.value
== TOK
.__attribute__
)
3132 cparseGnuAttributes(specifier
);
3133 if (specifier
.mod
& MOD
.xconst
)
3135 auto param
= new AST
.Parameter(paramLoc
, specifiersToSTC(LVL
.parameter
, specifier
),
3137 parameters
.push(param
);
3138 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
3142 check(TOK
.rightParenthesis
);
3146 /***********************************
3148 * _Static_assert ( constant-expression , string-literal ) ;
3150 private AST
.StaticAssert
cparseStaticAssert()
3152 const loc
= token
.loc
;
3154 //printf("cparseStaticAssert()\n");
3156 check(TOK
.leftParenthesis
);
3157 auto exp
= cparseConstantExp();
3159 if (token
.value
!= TOK
.string_
)
3160 error("string literal expected");
3161 auto msg
= cparsePrimaryExp();
3162 check(TOK
.rightParenthesis
);
3163 check(TOK
.semicolon
);
3164 return new AST
.StaticAssert(loc
, exp
, msg
);
3167 /*************************
3168 * Collect argument list.
3169 * Parser is on opening parenthesis.
3173 private AST
.Expressions
* cparseArguments()
3176 auto arguments
= new AST
.Expressions();
3177 while (token
.value
!= TOK
.rightParenthesis
&& token
.value
!= TOK
.endOfFile
)
3179 auto arg
= cparseAssignExp();
3180 arguments
.push(arg
);
3181 if (token
.value
!= TOK
.comma
)
3184 nextToken(); // consume comma
3187 check(TOK
.rightParenthesis
);
3192 /*************************
3194 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3196 * __declspec ( extended-decl-modifier-seq )
3198 * extended-decl-modifier-seq:
3199 * extended-decl-modifier (opt)
3200 * extended-decl-modifier extended-decl-modifier-seq
3202 * extended-decl-modifier:
3204 * deprecated(depMsg)
3213 * specifier = filled in with the attribute(s)
3215 private void cparseDeclspec(ref Specifier specifier
)
3217 //printf("cparseDeclspec()\n");
3218 /* Check for dllexport, dllimport
3221 nextToken(); // move past __declspec
3222 check(TOK
.leftParenthesis
);
3225 if (token
.value
== TOK
.rightParenthesis
)
3230 else if (token
.value
== TOK
.endOfFile
)
3232 else if (token
.value
== TOK
.identifier
)
3234 if (token
.ident
== Id
.dllimport
)
3236 specifier
.dllimport
= true;
3239 else if (token
.ident
== Id
.dllexport
)
3241 specifier
.dllexport
= true;
3244 else if (token
.ident
== Id
.naked
)
3246 specifier
.naked
= true;
3249 else if (token
.ident
== Id
.noinline
)
3251 specifier
.scw |
= SCW
.xnoinline
;
3254 else if (token
.ident
== Id
.noreturn
)
3256 specifier
.noreturn
= true;
3259 else if (token
.ident
== Id
._nothrow
)
3261 specifier
._nothrow
= true;
3264 else if (token
.ident
== Id
.thread
)
3266 specifier
.scw |
= SCW
.x_Thread_local
;
3269 else if (token
.ident
== Id
._align
)
3271 // Microsoft spec is very imprecise as to how this actually works
3273 check(TOK
.leftParenthesis
);
3274 if (token
.value
== TOK
.int32Literal
)
3276 const n
= token
.unsvalue
;
3277 if (n
< 1 || n
& (n
- 1) ||
8192 < n
)
3278 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n
);
3279 specifier
.packalign
.set(cast(uint)n
);
3280 specifier
.packalign
.setPack(true);
3285 error("alignment value expected, not `%s`", token
.toChars());
3289 check(TOK
.rightParenthesis
);
3291 else if (token
.ident
== Id
._deprecated
)
3293 specifier
._deprecated
= true;
3295 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3298 specifier
.depMsg
= cparseExpression();
3299 check(TOK
.rightParenthesis
);
3305 if (token
.value
== TOK
.leftParenthesis
)
3309 else if (token
.value
== TOK
.restrict
) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3313 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token
.toChars());
3315 if (token
.value
!= TOK
.rightParenthesis
)
3321 /*************************
3322 * Parser for asm label. It appears after the declarator, and has apparently
3323 * nothing to do with inline assembler.
3324 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3326 * asm ( asm-string-literal )
3328 * asm-string-literal:
3331 private AST
.StringExp
cparseGnuAsmLabel()
3333 nextToken(); // move past asm
3334 check(TOK
.leftParenthesis
);
3335 if (token
.value
!= TOK
.string_
)
3336 error("string literal expected for Asm Label, not `%s`", token
.toChars());
3337 auto label
= cparsePrimaryExp();
3338 check(TOK
.rightParenthesis
);
3339 return cast(AST
.StringExp
) label
;
3342 /********************
3343 * Parse C inline assembler statement in Gnu format.
3344 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3345 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3346 * Current token is on the `asm`.
3348 * inline assembler expression as a Statement
3350 private AST
.Statement
cparseGnuAsm()
3352 // Defer parsing of AsmStatements until semantic processing.
3353 const loc
= token
.loc
;
3357 // Consume all asm-qualifiers. As a future optimization, we could record
3358 // the `inline` and `volatile` storage classes against the statement.
3359 while (token
.value
== TOK
.goto_ ||
3360 token
.value
== TOK
.inline ||
3361 token
.value
== TOK
.volatile)
3364 check(TOK
.leftParenthesis
);
3365 if (token
.value
!= TOK
.string_
)
3366 error("string literal expected for Assembler Template, not `%s`", token
.toChars());
3367 Token
* toklist
= null;
3368 Token
** ptoklist
= &toklist
;
3369 //Identifier label = null;
3370 auto statements
= new AST
.Statements();
3375 switch (token
.value
)
3377 case TOK
.leftParenthesis
:
3381 case TOK
.rightParenthesis
:
3388 error("matching `)` expected, not `;`");
3393 error("matching `)` expected, not end of file");
3396 case TOK
.colonColon
: // treat as two separate : tokens for iasmgcc
3397 *ptoklist
= allocateToken();
3398 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3399 (*ptoklist
).value
= TOK
.colon
;
3400 ptoklist
= &(*ptoklist
).next
;
3402 *ptoklist
= allocateToken();
3403 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3404 (*ptoklist
).value
= TOK
.colon
;
3405 ptoklist
= &(*ptoklist
).next
;
3412 *ptoklist
= allocateToken();
3413 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3414 ptoklist
= &(*ptoklist
).next
;
3421 // Create AsmStatement from list of tokens we've saved
3422 AST
.Statement s
= new AST
.AsmStatement(token
.loc
, toklist
);
3428 auto s
= new AST
.CompoundAsmStatement(loc
, statements
, 0);
3432 /*************************
3433 * __attribute__ parser
3434 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3436 * gnu-attributes gnu-attribute-specifier
3438 * gnu-attribute-specifier:
3439 * __attribute__ (( gnu-attribute-list ))
3441 * gnu-attribute-list:
3442 * gnu-attribute (opt)
3443 * gnu-attribute-list , gnu-attribute
3446 * specifier = filled in with the attribute(s)
3448 private void cparseGnuAttributes(ref Specifier specifier
)
3450 while (token
.value
== TOK
.__attribute__
)
3452 nextToken(); // move past __attribute__
3453 check(TOK
.leftParenthesis
);
3454 check(TOK
.leftParenthesis
);
3456 if (token
.value
!= TOK
.rightParenthesis
)
3460 cparseGnuAttribute(specifier
);
3461 if (token
.value
!= TOK
.comma
)
3467 check(TOK
.rightParenthesis
);
3468 check(TOK
.rightParenthesis
);
3472 /*************************
3473 * Parse a single GNU attribute
3475 * gnu-attribute-name
3476 * gnu-attribute-name ( identifier )
3477 * gnu-attribute-name ( identifier , expression-list )
3478 * gnu-attribute-name ( expression-list (opt) )
3480 * gnu-attribute-name:
3485 * constant-expression
3486 * expression-list , constant-expression
3489 * specifier = filled in with the attribute(s)
3491 private void cparseGnuAttribute(ref Specifier specifier
)
3493 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3496 if (!isGnuAttributeName())
3499 if (token
.value
== TOK
.identifier
)
3501 if (token
.ident
== Id
.aligned
)
3504 if (token
.value
== TOK
.leftParenthesis
)
3507 if (token
.value
== TOK
.int32Literal
)
3509 const n
= token
.unsvalue
;
3510 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
3511 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n
);
3512 specifier
.packalign
.set(cast(uint)n
);
3513 specifier
.packalign
.setPack(true);
3518 error("alignment value expected, not `%s`", token
.toChars());
3522 check(TOK
.rightParenthesis
);
3524 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3525 * type on the target machine. It's the opposite of __attribute__((packed))
3528 else if (token
.ident
== Id
.always_inline
) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3530 specifier
.scw |
= SCW
.xinline
;
3533 else if (token
.ident
== Id
._deprecated
)
3535 specifier
._deprecated
= true;
3537 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3540 specifier
.depMsg
= cparseExpression();
3541 check(TOK
.rightParenthesis
);
3544 else if (token
.ident
== Id
.dllimport
)
3546 specifier
.dllimport
= true;
3549 else if (token
.ident
== Id
.dllexport
)
3551 specifier
.dllexport
= true;
3554 else if (token
.ident
== Id
.naked
)
3556 specifier
.naked
= true;
3559 else if (token
.ident
== Id
.noinline
)
3561 specifier
.scw |
= SCW
.xnoinline
;
3564 else if (token
.ident
== Id
.noreturn
)
3566 specifier
.noreturn
= true;
3569 else if (token
.ident
== Id
._nothrow
)
3571 specifier
._nothrow
= true;
3574 else if (token
.ident
== Id
._pure
)
3576 specifier
._pure
= true;
3579 else if (token
.ident
== Id
.vector_size
)
3582 check(TOK
.leftParenthesis
);
3583 if (token
.value
== TOK
.int32Literal
)
3585 const n
= token
.unsvalue
;
3586 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
3587 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n
);
3588 specifier
.vector_size
= cast(uint) n
;
3593 error("value for vector_size expected, not `%s`", token
.toChars());
3596 check(TOK
.rightParenthesis
);
3601 if (token
.value
== TOK
.leftParenthesis
)
3608 if (token
.value
== TOK
.leftParenthesis
)
3613 /*************************
3614 * See if match for GNU attribute name, which may be any identifier,
3615 * storage-class-specifier, type-specifier, or type-qualifier.
3617 * true if a valid GNU attribute name
3619 private bool isGnuAttributeName()
3621 switch (token
.value
)
3623 case TOK
.identifier
:
3639 case TOK
._Thread_local
:
3655 /***************************
3656 * Like skipParens(), but consume the tokens.
3658 private void cparseParens()
3660 check(TOK
.leftParenthesis
);
3665 switch (token
.value
)
3667 case TOK
.leftParenthesis
:
3671 case TOK
.rightParenthesis
:
3675 error("extra right parenthesis");
3686 error("end of file found before right parenthesis");
3697 /******************************************************************************/
3698 /***************************** Struct & Enum Parser ***************************/
3701 /*************************************
3704 * enum identifier (opt) { enumerator-list }
3705 * enum identifier (opt) { enumerator-list , }
3710 * enumerator-list , enumerator
3713 * enumeration-constant
3714 * enumeration-constant = constant-expression
3716 * enumeration-constant:
3720 * symbols = symbols to add enum declaration to
3724 private AST
.Type
cparseEnum(ref AST
.Dsymbols
* symbols
)
3726 const loc
= token
.loc
;
3731 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3732 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3733 * enum gnu-attributes (opt) identifier
3735 Specifier specifier
;
3736 specifier
.packalign
.setDefault();
3737 if (token
.value
== TOK
.__attribute__
)
3738 cparseGnuAttributes(specifier
);
3741 if (token
.value
== TOK
.identifier
)
3747 /* clang extension: add optional base type after the identifier
3748 * https://en.cppreference.com/w/cpp/language/enum
3749 * enum Identifier : Type
3751 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3752 AST
.Type base
= null; // C23 says base type is determined by enum member values
3753 if (token
.value
== TOK
.colon
)
3756 base
= cparseTypeName();
3759 AST
.Dsymbols
* members
;
3760 if (token
.value
== TOK
.leftCurly
)
3763 members
= new AST
.Dsymbols();
3765 if (token
.value
== TOK
.rightCurly
) // C11 6.7.2.2-1
3768 error("no members for `enum %s`", tag
.toChars());
3770 error("no members for anonymous enum");
3773 while (token
.value
== TOK
.identifier
)
3775 auto ident
= token
.ident
; // enumeration-constant
3777 auto mloc
= token
.loc
;
3779 if (token
.value
== TOK
.__attribute__
)
3781 /* gnu-attributes can appear here, but just scan and ignore them
3782 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3784 Specifier specifierx
;
3785 specifierx
.packalign
.setDefault();
3786 cparseGnuAttributes(specifierx
);
3789 AST
.Expression value
;
3790 if (token
.value
== TOK
.assign
)
3793 value
= cparseConstantExp();
3794 // TODO C11 6.7.2.2-2 value must fit into an int
3797 if (token
.value
== TOK
.__attribute__
)
3799 /* gnu-attributes can appear here, but just scan and ignore them
3800 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3802 Specifier specifierx
;
3803 specifierx
.packalign
.setDefault();
3804 cparseGnuAttributes(specifierx
);
3807 auto em
= new AST
.EnumMember(mloc
, ident
, value
, null, 0, null, null);
3810 if (token
.value
== TOK
.comma
)
3817 check(TOK
.rightCurly
);
3820 * Parse the postfix gnu-attributes (opt)
3822 if (token
.value
== TOK
.__attribute__
)
3823 cparseGnuAttributes(specifier
);
3826 error("missing `identifier` after `enum`");
3828 /* Need semantic information to determine if this is a declaration,
3829 * redeclaration, or reference to existing declaration.
3830 * Defer to the semantic() pass with a TypeTag.
3832 return new AST
.TypeTag(loc
, TOK
.enum_
, tag
, structalign_t
.init
, base
, members
);
3835 /*************************************
3837 * Parse struct and union specifiers.
3838 * Parser is advanced to the tag identifier or brace.
3839 * struct-or-union-specifier:
3840 * struct-or-union identifier (opt) { struct-declaration-list }
3841 * struct-or-union identifier
3847 * struct-declaration-list:
3848 * struct-declaration
3849 * struct-declaration-list struct-declaration
3852 * loc = location of `struct` or `union`
3853 * structOrUnion = TOK.struct_ or TOK.union_
3854 * packalign = alignment to use for struct members
3855 * symbols = symbols to add struct-or-union declaration to
3857 * type of the struct
3859 private AST
.Type
cparseStruct(Loc loc
, TOK structOrUnion
, structalign_t packalign
, ref AST
.Dsymbols
* symbols
)
3863 if (token
.value
== TOK
.identifier
)
3869 AST
.Dsymbols
* members
;
3870 if (token
.value
== TOK
.leftCurly
)
3873 members
= new AST
.Dsymbols(); // so `members` will be non-null even with 0 members
3874 while (token
.value
!= TOK
.rightCurly
)
3876 cparseStructDeclaration(members
);
3878 if (token
.value
== TOK
.endOfFile
)
3881 check(TOK
.rightCurly
);
3883 if ((*members
).length
== 0) // C11 6.7.2.1-8
3885 /* allow empty structs as an extension
3886 * struct-declarator-list:
3887 * struct-declarator (opt)
3892 error("missing tag `identifier` after `%s`", Token
.toChars(structOrUnion
));
3894 // many ways and places to declare alignment
3895 if (packalign
.isUnknown() && !this.packalign
.isUnknown())
3896 packalign
.set(this.packalign
.get());
3898 /* Need semantic information to determine if this is a declaration,
3899 * redeclaration, or reference to existing declaration.
3900 * Defer to the semantic() pass with a TypeTag.
3902 return new AST
.TypeTag(loc
, structOrUnion
, tag
, packalign
, null, members
);
3905 /*************************************
3907 * Parse a struct declaration member.
3908 * struct-declaration:
3909 * specifier-qualifier-list struct-declarator-list (opt) ;
3910 * static_assert-declaration
3912 * struct-declarator-list:
3914 * struct-declarator-list , struct-declarator
3916 * struct-declarator:
3918 * declarator (opt) : constant-expression
3920 * members = where to put the fields (members)
3922 void cparseStructDeclaration(AST
.Dsymbols
* members
)
3924 //printf("cparseStructDeclaration()\n");
3925 if (token
.value
== TOK
._Static_assert
)
3927 auto s
= cparseStaticAssert();
3932 Specifier specifier
;
3933 specifier
.packalign
= this.packalign
;
3934 auto tspec
= cparseSpecifierQualifierList(LVL
.member
, specifier
);
3937 error("no type-specifier for struct member");
3938 tspec
= AST
.Type
.tint32
;
3940 if (specifier
.mod
& MOD
.xconst
)
3942 tspec
= toConst(tspec
);
3943 specifier
.mod
= MOD
.xnone
; // 'used' it
3946 /* If a declarator does not follow, it is unnamed
3948 if (token
.value
== TOK
.semicolon
&& tspec
)
3951 auto tt
= tspec
.isTypeTag();
3954 if (auto ti
= tspec
.isTypeIdentifier())
3956 error("type-specifier omitted before declaration of `%s`", ti
.ident
.toChars());
3958 return; // legal but meaningless empty declaration
3961 /* If anonymous struct declaration
3962 * struct { ... members ... };
3965 if (!tt
.id
&& tt
.members
)
3967 /* members of anonymous struct are considered members of
3968 * the containing struct
3970 auto ad
= new AST
.AnonDeclaration(tt
.loc
, tt
.tok
== TOK
.union_
, tt
.members
);
3971 auto s
= applySpecifier(ad
, specifier
);
3975 if (!tt
.id
&& !tt
.members
)
3976 return; // already gave error in cparseStruct()
3978 /* `struct tag;` and `struct tag { ... };`
3979 * always result in a declaration in the current scope
3981 // TODO: merge in specifier
3982 auto stag
= (tt
.tok
== TOK
.struct_
)
3983 ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false)
3984 : new AST
.UnionDeclaration(tt
.loc
, tt
.id
);
3985 stag
.members
= tt
.members
;
3987 symbols
= new AST
.Dsymbols();
3988 auto s
= applySpecifier(stag
, specifier
);
3997 if (token
.value
== TOK
.colon
)
3999 if (auto ti
= tspec
.isTypeIdentifier())
4001 error("type-specifier omitted before bit field declaration of `%s`", ti
.ident
.toChars());
4002 tspec
= AST
.Type
.tint32
;
4005 // C11 6.7.2.1-12 unnamed bit-field
4006 id
= Identifier
.generateAnonymousId("BitField");
4011 dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
4016 break; // error recovery
4020 AST
.Expression width
;
4021 if (token
.value
== TOK
.colon
)
4023 // C11 6.7.2.1-10 bit-field
4025 width
= cparseConstantExp();
4029 * struct-declarator:
4030 * declarator gnu-attributes (opt)
4031 * declarator (opt) : constant-expression gnu-attributes (opt)
4033 if (token
.value
== TOK
.__attribute__
)
4034 cparseGnuAttributes(specifier
);
4036 if (!tspec
&& !specifier
.scw
&& !specifier
.mod
)
4037 error("specifier-qualifier-list required");
4040 if (specifier
.alignExps
)
4041 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4042 auto s
= new AST
.BitFieldDeclaration(width
.loc
, dt, id
, width
);
4047 if (dt.ty
== AST
.Tvoid
)
4048 error("`void` has no value");
4050 // declare the symbol
4051 // Give member variables an implicit void initializer
4052 auto initializer
= new AST
.VoidInitializer(token
.loc
);
4053 AST
.Dsymbol s
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(LVL
.member
, specifier
));
4054 s
= applySpecifier(s
, specifier
);
4058 switch (token
.value
)
4060 case TOK
.identifier
:
4061 error("missing comma");
4073 error("`;` or `,` expected");
4074 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
4083 /******************************************************************************/
4084 /********************************* Lookahead Parser ***************************/
4087 /************************************
4088 * Determine if the scanner is sitting on the start of a declaration.
4090 * t = current token of the scanner
4091 * needId = flag with additional requirements for a declaration
4092 * endtok = ending token
4093 * pt = will be set ending token (if not null)
4095 * true at start of a declaration
4097 private bool isCDeclaration(ref Token
* pt
)
4100 //printf("isCDeclaration() %s\n", t.toChars());
4101 if (!isDeclarationSpecifiers(t
))
4106 if (t
.value
== TOK
.semicolon
)
4112 if (!isCDeclarator(t
, DTR
.xdirect
))
4114 if (t
.value
== TOK
.asm_
)
4117 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4120 if (t
.value
== TOK
.__attribute__
)
4123 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4126 if (t
.value
== TOK
.assign
)
4129 if (!isInitializer(t
))
4149 /********************************
4150 * See if match for initializer.
4152 * pt = starting token, updated to one past end of initializer if true
4154 * true if initializer
4156 private bool isInitializer(ref Token
* pt
)
4158 //printf("isInitializer()\n");
4161 if (t
.value
== TOK
.leftCurly
)
4169 // skip over assignment-expression, ending before comma or semiColon or EOF
4170 if (!isAssignmentExpression(t
))
4176 /********************************
4178 * postfix-expression ( argument-expression-list(opt) )
4180 * pt = starting token, updated to one past end of initializer if true
4182 * true if function call
4184 private bool isFunctionCall(ref Token
* pt
)
4186 //printf("isFunctionCall()\n");
4189 if (!isPrimaryExpression(t
))
4191 if (t
.value
!= TOK
.leftParenthesis
)
4196 if (!isAssignmentExpression(t
))
4198 if (t
.value
== TOK
.comma
)
4203 if (t
.value
== TOK
.rightParenthesis
)
4210 if (t
.value
!= TOK
.semicolon
)
4216 /********************************
4217 * See if match for assignment-expression.
4219 * pt = starting token, updated to one past end of assignment-expression if true
4221 * true if assignment-expression
4223 private bool isAssignmentExpression(ref Token
* pt
)
4226 //printf("isAssignmentExpression() %s\n", t.toChars());
4228 /* This doesn't actually check for grammar matching an
4229 * assignment-expression. It just matches ( ) [ ] looking for
4230 * an ending token that would terminate one.
4239 case TOK
.rightParenthesis
:
4240 case TOK
.rightBracket
:
4246 case TOK
.leftParenthesis
:
4247 if (!skipParens(t
, &t
))
4250 https://issues.dlang.org/show_bug.cgi?id=22267
4251 If the parser encounters the following
4252 `identifier variableName = (expression);`
4253 the initializer is not identified as such since the parentheses
4254 cause the parser to keep walking indefinitely
4255 (whereas `(1) + 1` would not be affected.).
4260 case TOK
.leftBracket
:
4261 if (!skipBrackets(t
))
4271 any
= true; // assume token was part of an a-e
4280 /********************************
4281 * See if match for constant-expression.
4283 * pt = starting token, updated to one past end of constant-expression if true
4285 * true if constant-expression
4287 private bool isConstantExpression(ref Token
* pt
)
4289 return isAssignmentExpression(pt
);
4292 /********************************
4293 * See if match for declaration-specifiers.
4294 * No errors are diagnosed.
4296 * pt = starting token, updated to one past end of declaration-specifiers if true
4298 * true if declaration-specifiers
4300 private bool isDeclarationSpecifiers(ref Token
* pt
)
4302 //printf("isDeclarationSpecifiers()\n");
4324 //case TOK._Imaginary:
4331 case TOK
.identifier
: // typedef-name
4345 if (t
.value
== TOK
.__attribute__ ||
4346 t
.value
== TOK
.__declspec
)
4349 if (!skipParens(t
, &t
))
4352 if (t
.value
== TOK
.identifier
)
4355 if (t
.value
== TOK
.leftCurly
)
4361 else if (t
.value
== TOK
.leftCurly
)
4371 // storage-class-specifiers
4376 case TOK
._Thread_local
:
4380 // function-specifiers
4393 case TOK
._Alignas
: // alignment-specifier
4394 case TOK
.__declspec
: // decl-specifier
4395 case TOK
.__attribute__
: // attribute-specifier
4397 if (!skipParens(t
, &t
))
4402 // either atomic-type-specifier or type_qualifier
4403 case TOK
._Atomic
: // TODO _Atomic ( type-name )
4405 if (t
.value
== TOK
.leftParenthesis
) // maybe atomic-type-specifier
4409 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4410 { // it's a type-qualifier
4411 t
= tsave
; // back up parser
4415 t
= peek(t
); // move past right parenthesis of atomic-type-specifier
4434 /**************************************
4435 * See if declaration-list is present.
4437 * true if declaration-list is present, even an empty one
4439 bool isDeclarationList(ref Token
* pt
)
4444 if (t
.value
== TOK
.leftCurly
)
4449 if (!isCDeclaration(t
))
4454 /*******************************************
4457 * pt = enters on left brace, set to token past right bracket on true
4459 * true if successful
4461 private bool skipBraces(ref Token
* pt
)
4464 if (t
.value
!= TOK
.leftCurly
)
4478 case TOK
.rightCurly
:
4501 /*******************************************
4504 * pt = enters on left bracket, set to token past right bracket on true
4506 * true if successful
4508 private bool skipBrackets(ref Token
* pt
)
4511 if (t
.value
!= TOK
.leftBracket
)
4520 case TOK
.leftBracket
:
4525 case TOK
.rightBracket
:
4548 /*********************************
4549 * Check to see if tokens starting with *pt form a declarator.
4551 * pt = pointer to starting token, updated to point past declarator if true is returned
4552 * declarator = declarator kind
4556 private bool isCDeclarator(ref Token
* pt
, DTR declarator
)
4561 if (t
.value
== TOK
.mul) // pointer
4564 if (!isTypeQualifierList(t
))
4571 if (t
.value
== TOK
.identifier
)
4573 if (declarator
== DTR
.xabstract
)
4577 else if (t
.value
== TOK
.leftParenthesis
)
4580 if (!isCDeclarator(t
, declarator
))
4582 if (t
.value
!= TOK
.rightParenthesis
)
4586 else if (declarator
== DTR
.xdirect
)
4593 if (t
.value
== TOK
.leftBracket
)
4595 if (!skipBrackets(t
))
4598 else if (t
.value
== TOK
.leftParenthesis
)
4600 if (!skipParens(t
, &t
))
4610 /***************************
4611 * Is this the start of a type-qualifier-list?
4614 * pt = first token; updated with past end of type-qualifier-list if true
4616 * true if start of type-qualifier-list
4618 private bool isTypeQualifierList(ref Token
* pt
)
4642 /***************************
4643 * Is this the start of a type-name?
4645 * pt = first token; updated with past end of type-name if true
4647 * true if start of type-name
4649 private bool isTypeName(ref Token
* pt
)
4652 //printf("isTypeName() %s\n", t.toChars());
4653 if (!isSpecifierQualifierList(t
))
4655 if (!isCDeclarator(t
, DTR
.xabstract
))
4657 if (t
.value
!= TOK
.rightParenthesis
)
4663 /***************************
4664 * Is this the start of a specifier-qualifier-list?
4666 * pt = first token; updated with past end of specifier-qualifier-list if true
4668 * true if start of specifier-qualifier-list
4670 private bool isSpecifierQualifierList(ref Token
* pt
)
4696 //case TOK._Imaginary: // ? missing in Spec
4701 case TOK
.identifier
:
4702 // Use typedef table to disambiguate
4703 if (isTypedef(t
.ident
))
4713 // struct-or-union-specifier
4719 if (t
.value
== TOK
.identifier
)
4722 if (t
.value
== TOK
.leftCurly
)
4728 else if (t
.value
== TOK
.leftCurly
)
4737 // atomic-type-specifier
4740 case TOK
.__attribute__
:
4742 if (t
.value
!= TOK
.leftParenthesis ||
4756 /************************************
4757 * Looking at the leading left parenthesis, and determine if it is
4758 * either of the following:
4759 * ( type-name ) cast-expression
4760 * ( type-name ) { initializer-list }
4764 * pt = starting token, updated to one past end of constant-expression if true
4765 * afterParenType = true if already seen `( type-name )`
4767 * true if matches ( type-name ) ...
4769 private bool isCastExpression(ref Token
* pt
, bool afterParenType
= false)
4772 if (log
) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token
.toChars(pt
.value
), afterParenType
);
4776 case TOK
.leftParenthesis
:
4777 auto tk
= peek(t
); // move past left parenthesis
4778 if (!isTypeName(tk
) || tk
.value
!= TOK
.rightParenthesis
)
4781 goto default; // could be ( type-name ) ( unary-expression )
4784 tk
= peek(tk
); // move past right parenthesis
4786 if (tk
.value
== TOK
.leftCurly
)
4788 // ( type-name ) { initializer-list }
4789 if (!isInitializer(tk
))
4797 if (tk
.value
== TOK
.leftParenthesis
&& peek(tk
).value
== TOK
.rightParenthesis
)
4799 return false; // (type-name)() is not a cast (it might be a function call)
4802 if (!isCastExpression(tk
, true))
4804 if (afterParenType
) // could be ( type-name ) ( unary-expression )
4805 goto default; // where unary-expression also matched type-name
4808 // ( type-name ) cast-expression
4813 if (!afterParenType ||
!isUnaryExpression(t
, afterParenType
))
4817 // if we've already seen ( type-name ), then this is a cast
4821 if (log
) printf("isCastExpression true\n");
4825 /********************************
4826 * See if match for unary-expression.
4828 * pt = starting token, updated to one past end of constant-expression if true
4829 * afterParenType = true if already seen ( type-name ) of a cast-expression
4831 * true if unary-expression
4833 private bool isUnaryExpression(ref Token
* pt
, bool afterParenType
= false)
4839 case TOK
.minusMinus
:
4841 if (!isUnaryExpression(t
, afterParenType
))
4852 if (!isCastExpression(t
, afterParenType
))
4858 if (t
.value
== TOK
.leftParenthesis
)
4863 if (tk
.value
!= TOK
.rightParenthesis
)
4869 if (!isUnaryExpression(t
, afterParenType
))
4875 if (t
.value
!= TOK
.leftParenthesis
)
4878 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4883 // Compound literals are handled by cast and sizeof expressions,
4884 // so be content with just seeing a primary expression.
4885 if (!isPrimaryExpression(t
))
4893 /********************************
4894 * See if match for primary-expression.
4896 * pt = starting token, updated to one past end of constant-expression if true
4898 * true if primary-expression
4900 private bool isPrimaryExpression(ref Token
* pt
)
4905 case TOK
.identifier
:
4906 case TOK
.charLiteral
:
4907 case TOK
.int32Literal
:
4908 case TOK
.uns32Literal
:
4909 case TOK
.int64Literal
:
4910 case TOK
.uns64Literal
:
4911 case TOK
.float32Literal
:
4912 case TOK
.float64Literal
:
4913 case TOK
.float80Literal
:
4914 case TOK
.imaginary32Literal
:
4915 case TOK
.imaginary64Literal
:
4916 case TOK
.imaginary80Literal
:
4921 case TOK
.leftParenthesis
:
4923 if (!skipParens(t
, &t
))
4929 if (!skipParens(t
, &t
))
4941 /******************************************************************************/
4942 /********************************* More ***************************************/
4946 * Declaration context
4950 global
= 1, /// global
4951 parameter
= 2, /// function parameter (declarations for function identifier-list)
4952 prototype
= 4, /// function prototype
4953 local
= 8, /// local
4954 member
= 0x10, /// struct member
4957 /// Types of declarator to parse
4960 xdirect
= 1, /// C11 6.7.6 direct-declarator
4961 xabstract
= 2, /// C11 6.7.7 abstract-declarator
4962 xparameter
= 3, /// parameter declarator may be either direct or abstract
4965 /// C11 6.7.1 Storage-class specifiers
4975 // C11 6.7.4 Function specifiers
4982 /// C11 6.7.3 Type qualifiers
4990 x__stdcall
= 0x10, // Windows linkage extension
4993 /**********************************
4994 * Aggregate for all the various specifiers
4998 bool noreturn
; /// noreturn attribute
4999 bool naked
; /// naked attribute
5000 bool _nothrow
; /// nothrow attribute
5001 bool _pure
; /// pure attribute
5002 bool dllimport
; /// dllimport attribute
5003 bool dllexport
; /// dllexport attribute
5004 bool _deprecated
; /// deprecated attribute
5005 AST
.Expression depMsg
; /// deprecated message
5006 uint vector_size
; /// positive power of 2 multipe of base type size
5008 SCW scw
; /// storage-class specifiers
5009 MOD mod
; /// type qualifiers
5010 AST
.Expressions
* alignExps
; /// alignment
5011 structalign_t packalign
; /// #pragma pack alignment value
5014 /***********************
5015 * Convert from C specifiers to D storage class
5017 * level = declaration context
5018 * specifier = specifiers, context, etc.
5020 * corresponding D storage class
5022 StorageClass
specifiersToSTC(LVL level
, const ref Specifier specifier
)
5025 if (specifier
.scw
& SCW
.x_Thread_local
)
5027 if (level
== LVL
.global
)
5029 if (specifier
.scw
& SCW
.xextern
)
5030 stc = AST
.STC
.extern_
;
5031 else if (specifier
.scw
& SCW
.xstatic
)
5032 stc = AST
.STC
.static_
;
5034 else if (level
== LVL
.local
)
5036 if (specifier
.scw
& SCW
.xextern
)
5037 stc = AST
.STC
.extern_
;
5038 else if (specifier
.scw
& SCW
.xstatic
)
5039 stc = AST
.STC
.static_
;
5041 else if (level
== LVL
.member
)
5043 if (specifier
.scw
& SCW
.xextern
)
5044 stc = AST
.STC
.extern_
;
5045 else if (specifier
.scw
& SCW
.xstatic
)
5046 stc = AST
.STC
.static_
;
5051 if (level
== LVL
.global
)
5053 if (specifier
.scw
& SCW
.xextern
)
5054 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5055 else if (specifier
.scw
& SCW
.xstatic
)
5056 stc = AST
.STC
.gshared | AST
.STC
.static_
;
5058 stc = AST
.STC
.gshared
;
5060 else if (level
== LVL
.local
)
5062 if (specifier
.scw
& SCW
.xextern
)
5063 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5064 else if (specifier
.scw
& SCW
.xstatic
)
5065 stc = AST
.STC
.gshared
;
5066 else if (specifier
.scw
& SCW
.xregister
)
5067 stc = AST
.STC
.register
;
5069 else if (level
== LVL
.parameter
)
5071 if (specifier
.scw
& SCW
.xregister
)
5072 stc = AST
.STC
.register | AST
.STC
.parameter
;
5074 stc = AST
.STC
.parameter
;
5076 else if (level
== LVL
.member
)
5078 if (specifier
.scw
& SCW
.xextern
)
5079 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5080 else if (specifier
.scw
& SCW
.xstatic
)
5081 stc = AST
.STC
.gshared
;
5084 if (specifier
._deprecated
&& !specifier
.depMsg
)
5085 stc |
= AST
.STC
.deprecated_
;
5089 /***********************
5090 * Add attributes from Specifier to function
5092 * fd = function to apply them to
5093 * specifier = specifiers
5095 void specifiersToFuncDeclaration(AST
.FuncDeclaration fd
, const ref Specifier specifier
)
5097 fd
.isNaked
= specifier
.naked
;
5098 fd
.dllImport
= specifier
.dllimport
;
5099 fd
.dllExport
= specifier
.dllexport
;
5101 if (specifier
.scw
& SCW
.xnoinline
)
5102 fd
.inlining
= PINLINE
.never
;
5103 else if (specifier
.scw
& SCW
.xinline
)
5104 fd
.inlining
= PINLINE
.always
;
5107 /***********************
5108 * Add attributes from Specifier to variable
5110 * vd = function to apply them to
5111 * specifier = specifiers
5113 void specifiersToVarDeclaration(AST
.VarDeclaration vd
, const ref Specifier specifier
)
5115 vd
.dllImport
= specifier
.dllimport
;
5116 vd
.dllExport
= specifier
.dllexport
;
5119 /***********************
5120 * Return suitable signed integer type for the given size
5122 * size = size of type
5124 * corresponding signed D integer type
5126 private AST
.Type
integerTypeForSize(ubyte size
)
5129 return AST
.Type
.tint8
;
5131 return AST
.Type
.tint16
;
5133 return AST
.Type
.tint32
;
5135 return AST
.Type
.tint64
;
5138 error("__int128 not supported");
5139 return AST
.Type
.terror
;
5141 error("unsupported integer type");
5142 return AST
.Type
.terror
;
5145 /***********************
5146 * Return suitable unsigned integer type for the given size
5148 * size = size of type
5150 * corresponding unsigned D integer type
5152 private AST
.Type
unsignedTypeForSize(ubyte size
)
5155 return AST
.Type
.tuns8
;
5157 return AST
.Type
.tuns16
;
5159 return AST
.Type
.tuns32
;
5161 return AST
.Type
.tuns64
;
5164 error("unsigned __int128 not supported");
5165 return AST
.Type
.terror
;
5167 error("unsupported integer type");
5168 return AST
.Type
.terror
;
5171 /***********************
5172 * Return suitable D float type for C `long double`
5174 * flags = kind of float to return (real, imaginary, complex).
5176 * corresponding D type
5178 private AST
.Type
realType(RTFlags flags
)
5180 if (long_doublesize
== AST
.Type
.tfloat80
.size())
5182 // On GDC and LDC, D `real` types map to C `long double`, so never
5183 // return a double type when real.sizeof == double.sizeof.
5184 final switch (flags
)
5186 case RTFlags
.realfloat
: return AST
.Type
.tfloat80
;
5187 case RTFlags
.imaginary
: return AST
.Type
.timaginary80
;
5188 case RTFlags
.complex
: return AST
.Type
.tcomplex80
;
5193 final switch (flags
)
5195 case RTFlags
.realfloat
: return long_doublesize
== 8 ? AST
.Type
.tfloat64
: AST
.Type
.tfloat80
;
5196 case RTFlags
.imaginary
: return long_doublesize
== 8 ? AST
.Type
.timaginary64
: AST
.Type
.timaginary80
;
5197 case RTFlags
.complex
: return long_doublesize
== 8 ? AST
.Type
.tcomplex64
: AST
.Type
.tcomplex80
;
5203 * Flags for realType
5205 private enum RTFlags
5212 /********************
5213 * C11 6.4.2.2 Create declaration to predefine __func__
5214 * `static const char __func__[] = " function-name ";`
5216 * loc = location for this declaration
5217 * id = identifier of function
5219 * statement representing the declaration of __func__
5221 private AST
.Statement
createFuncName(Loc loc
, Identifier id
)
5223 const fn
= id
.toString(); // function-name
5224 auto efn
= new AST
.StringExp(loc
, fn
, fn
.length
, 1, 'c');
5225 auto ifn
= new AST
.ExpInitializer(loc
, efn
);
5226 auto lenfn
= new AST
.IntegerExp(loc
, fn
.length
+ 1, AST
.Type
.tuns32
); // +1 for terminating 0
5227 auto tfn
= new AST
.TypeSArray(AST
.Type
.tchar
, lenfn
);
5228 efn
.type
= tfn
.immutableOf();
5229 efn
.committed
= true;
5230 auto sfn
= new AST
.VarDeclaration(loc
, tfn
, Id
.__func__
, ifn
, STC
.gshared | STC
.immutable_
);
5231 auto e
= new AST
.DeclarationExp(loc
, sfn
);
5232 return new AST
.ExpStatement(loc
, e
);
5235 /************************
5236 * After encountering an error, scan forward until a right brace or ; is found
5237 * or the end of the file.
5241 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
5245 /**************************
5246 * Apply `const` to a type.
5248 * t = type to add const to
5252 private AST
.Type
toConst(AST
.Type t
)
5254 // `const` is always applied to the return type, not the
5255 // type function itself.
5256 if (auto tf
= t
.isTypeFunction())
5257 tf
.next
= tf
.next
.addSTC(STC
.const_
);
5258 else if (auto tt
= t
.isTypeTag())
5259 tt
.mod |
= MODFlags
.const_
;
5262 /* Ignore const if the result would be const pointer to mutable
5264 auto tn
= t
.nextOf();
5265 if (!tn || tn
.isConst())
5266 t
= t
.addSTC(STC
.const_
);
5271 /***************************
5272 * Apply specifier to a Dsymbol.
5275 * specifier = specifiers to apply
5277 * Dsymbol with specifiers applied
5279 private AST
.Dsymbol
applySpecifier(AST
.Dsymbol s
, ref Specifier specifier
)
5281 //printf("applySpecifier() %s\n", s.toChars());
5282 if (specifier
._deprecated
)
5284 if (specifier
.depMsg
)
5286 // Wrap declaration in a DeprecatedDeclaration
5287 auto decls
= new AST
.Dsymbols(1);
5289 s
= new AST
.DeprecatedDeclaration(specifier
.depMsg
, decls
);
5293 if (specifier
.alignExps
)
5295 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5296 // Wrap declaration in an AlignDeclaration
5297 auto decls
= new AST
.Dsymbols(1);
5299 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.alignExps
, decls
);
5301 else if (!specifier
.packalign
.isDefault() && !specifier
.packalign
.isUnknown())
5303 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5304 // Wrap #pragma pack in an AlignDeclaration
5305 auto decls
= new AST
.Dsymbols(1);
5307 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.packalign
, decls
);
5314 /******************************************************************************/
5315 /************************** typedefTab symbol table ***************************/
5318 /********************************
5319 * Determines if type t is a function type.
5323 * true if it represents a function
5325 bool isFunctionTypedef(AST
.Type t
)
5327 //printf("isFunctionTypedef() %s\n", t.toChars());
5328 if (t
.isTypeFunction())
5330 if (auto tid
= t
.isTypeIdentifier())
5332 auto pt
= lookupTypedef(tid
.ident
);
5335 return (*pt
).isTypeFunction() !is null;
5341 /********************************
5342 * Determine if `id` is a symbol for a Typedef.
5344 * id = possible typedef
5346 * true if id is a Type
5348 bool isTypedef(Identifier id
)
5350 auto pt
= lookupTypedef(id
);
5354 /*******************************
5355 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5356 * Params: id = identifier for non-typedef symbol
5358 void insertIdToTypedefTab(Identifier id
)
5360 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5361 if (isTypedef(id
)) // if existing typedef
5363 /* Add id as null, so we can later distinguish it from a non-null typedef
5365 auto tab
= cast(void*[void*])(typedefTab
[$ - 1]);
5366 tab
[cast(void*)id
] = cast(void*)null;
5370 /*******************************
5371 * Add `id` to typedefTab[]
5373 * id = identifier for typedef symbol
5374 * t = type of the typedef symbol
5376 void insertTypedefToTypedefTab(Identifier id
, AST
.Type t
)
5378 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5379 if (auto tid
= t
.isTypeIdentifier())
5381 // Try to resolve the TypeIdentifier to its type
5382 auto pt
= lookupTypedef(tid
.ident
);
5386 auto tab
= cast(void*[void*])(typedefTab
[$ - 1]);
5387 tab
[cast(void*)id
] = cast(void*)t
;
5388 typedefTab
[$ - 1] = cast(void*)tab
;
5391 /*********************************
5392 * Lookup id in typedefTab[].
5394 * if not found, then null.
5395 * if found, then Type*. Deferencing it will yield null if it is not
5396 * a typedef, and a type if it is a typedef.
5398 AST
.Type
* lookupTypedef(Identifier id
)
5400 foreach_reverse (tab
; typedefTab
[])
5402 if (auto pt
= cast(void*)id
in cast(void*[void*])tab
)
5404 return cast(AST
.Type
*)pt
;
5407 return null; // not found
5412 /******************************************************************************/
5413 /********************************* Directive Parser ***************************/
5416 override bool parseSpecialTokenSequence()
5420 if (n
.value
== TOK
.int32Literal
)
5425 if (n
.value
== TOK
.identifier
)
5427 if (n
.ident
== Id
.line
)
5429 poundLine(n
, false);
5432 else if (defines
&& (n
.ident
== Id
.define || n
.ident
== Id
.undef
))
5434 /* Append this line to `defines`.
5435 * Not canonicalizing it - assume it already is
5437 defines
.writeByte('#');
5438 defines
.writestring(n
.ident
.toString());
5439 skipToNextLine(defines
);
5440 defines
.writeByte('\n');
5443 else if (n
.ident
== Id
.__pragma
)
5445 pragmaDirective(scanloc
);
5448 else if (n
.ident
== Id
.ident
) // #ident "string"
5451 if (n
.value
== TOK
.string_
&& n
.ptr
[0] == '"' && n
.postfix
== 0)
5453 /* gcc inserts string into the .comment section in the object file.
5454 * Just ignore it for now, but can support it later by writing
5455 * the string to obj_exestr()
5457 //auto comment = n.ustring;
5460 if (n
.value
== TOK
.endOfFile || n
.value
== TOK
.endOfLine
)
5463 error("\"string\" expected after `#ident`");
5467 if (n
.ident
!= Id
.undef
)
5468 error("C preprocessor directive `#%s` is not supported", n
.toChars());
5472 /*********************************************
5474 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5475 * Scanner is on the `__pragma`
5477 * startloc = location to use for error messages
5479 private void uupragmaDirective(const ref Loc startloc
)
5481 const loc
= startloc
;
5482 nextToken(); // move past __pragma
5483 if (token
.value
!= TOK
.leftParenthesis
)
5485 error(loc
, "left parenthesis expected to follow `__pragma` instead of `%s`", token
.toChars());
5491 if (token
.value
== TOK
.identifier
)
5493 if (token
.ident
== Id
.pack
)
5494 pragmaPack(startloc
, false);
5498 if (token
.value
== TOK
.leftParenthesis
)
5503 else if (token
.value
== TOK
.endOfFile
)
5506 else if (token
.value
== TOK
.rightParenthesis
)
5510 error(loc
, "unrecognized `__pragma(%s)`", token
.toChars());
5512 if (token
.value
!= TOK
.rightParenthesis
)
5514 error(loc
, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token
.toChars());
5520 /*********************************************
5521 * C11 6.10.6 Pragma directive
5522 * # pragma pp-tokens(opt) new-line
5523 * The C preprocessor sometimes leaves pragma directives in
5524 * the preprocessed output. Ignore them.
5525 * Upon return, p is at start of next line.
5527 private void pragmaDirective(const ref Loc loc
)
5531 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pack
)
5532 return pragmaPack(loc
, true);
5533 if (n
.value
!= TOK
.endOfLine
)
5539 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5540 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5541 * Scanner is on the `pack`
5543 * startloc = location to use for error messages
5544 * useScan = use scan() to retrieve next token, instead of nextToken()
5546 private void pragmaPack(const ref Loc startloc
, bool useScan
)
5548 const loc
= startloc
;
5550 /* Pull tokens from scan() or nextToken()
5567 if (n
.value
!= TOK
.leftParenthesis
)
5569 error(loc
, "left parenthesis expected to follow `#pragma pack`");
5570 if (n
.value
!= TOK
.endOfLine
)
5577 if (n
.value
!= TOK
.rightParenthesis
)
5579 error(loc
, "right parenthesis expected to close `#pragma pack(`");
5581 if (n
.value
!= TOK
.endOfLine
)
5585 void setPackAlign(ref const Token t
)
5587 const n
= t
.unsvalue
;
5588 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
5589 error(loc
, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n
);
5590 packalign
.set(cast(uint)n
);
5591 packalign
.setPack(true);
5598 records
= new Array
!Identifier
;
5599 packs
= new Array
!structalign_t
;
5602 /* # pragma pack ( show )
5604 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.show
)
5606 if (packalign
.isDefault())
5607 eSink
.warning(startloc
, "current pack attribute is default");
5609 eSink
.warning(startloc
, "current pack attribute is %d", packalign
.get());
5611 return closingParen();
5613 /* # pragma pack ( push )
5614 * # pragma pack ( push , identifier )
5615 * # pragma pack ( push , integer )
5616 * # pragma pack ( push , identifier , integer )
5618 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.push)
5621 Identifier record
= null;
5622 if (n
.value
== TOK
.comma
)
5625 if (n
.value
== TOK
.identifier
)
5629 if (n
.value
== TOK
.comma
)
5632 if (n
.value
== TOK
.int32Literal
)
5638 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5641 else if (n
.value
== TOK
.int32Literal
)
5647 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5649 this.records
.push(record
);
5650 this.packs
.push(packalign
);
5651 return closingParen();
5653 /* # pragma pack ( pop )
5654 * # pragma pack ( pop PopList )
5656 * , IdentifierOrInteger
5657 * , IdentifierOrInteger PopList
5658 * IdentifierOrInteger:
5662 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pop)
5665 size_t len
= this.records
.length
;
5666 if (n
.value
== TOK
.rightParenthesis
) // #pragma pack ( pop )
5668 if (len
== 0) // nothing to pop
5669 return closingParen();
5671 this.records
.setDim(len
- 1);
5672 this.packs
.setDim(len
- 1);
5673 if (len
== 1) // stack is now empty
5674 packalign
.setDefault();
5676 packalign
= (*this.packs
)[len
- 1];
5677 return closingParen();
5679 while (n
.value
== TOK
.comma
) // #pragma pack ( pop ,
5682 if (n
.value
== TOK
.identifier
)
5684 /* pragma pack(pop, identifier
5685 * Pop until identifier is found, pop that one too, and set
5686 * alignment to the new top of the stack.
5687 * If identifier is not found, do nothing.
5691 if ((*this.records
)[len
- 1] == n
.ident
)
5693 this.records
.setDim(len
- 1);
5694 this.packs
.setDim(len
- 1);
5696 packalign
= (*this.packs
)[len
- 2];
5698 packalign
.setDefault(); // stack empty, use default
5704 else if (n
.value
== TOK
.int32Literal
)
5711 error(loc
, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n
.toChars());
5715 return closingParen();
5717 /* # pragma pack ( integer )
5718 * Sets alignment to integer
5720 if (n
.value
== TOK
.int32Literal
)
5724 return closingParen();
5726 /* # pragma pack ( )
5727 * Sets alignment to default
5729 if (n
.value
== TOK
.rightParenthesis
)
5731 packalign
.setDefault();
5732 return closingParen();
5735 error(loc
, "unrecognized `#pragma pack(%s)`", n
.toChars());
5736 if (n
.value
!= TOK
.endOfLine
)
5742 /******************************************************************************/
5743 /********************************* #define Parser *****************************/
5747 * Go through the #define's in the defines buffer and see what we can convert
5748 * to Dsymbols, which are then appended to symbols[]
5752 if (!defines || defines
.length
< 10) // minimum length of a #define line
5754 OutBuffer
* buf
= defines
;
5755 defines
= null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5756 // from appending to slice[]
5757 const length
= buf
.length
;
5759 auto slice
= buf
.peekChars()[0 .. length
];
5760 resetDefineLines(slice
); // reset lexer
5762 const(char)* endp
= &slice
[length
- 7];
5764 size_t
[void*] defineTab
; // hash table of #define's turned into Symbol's
5765 // indexed by Identifier, returns index into symbols[]
5766 // The memory for this is leaked
5768 void addVar(AST
.VarDeclaration v
)
5770 //printf("addVar() %s\n", v.toChars());
5771 v
.isCmacro(true); // mark it as coming from a C #define
5772 /* If it's already defined, replace the earlier
5775 if (size_t
* pd
= cast(void*)v
.ident
in defineTab
)
5777 //printf("replacing %s\n", v.toChars());
5778 (*symbols
)[*pd
] = v
;
5781 defineTab
[cast(void*)v
.ident
] = symbols
.length
;
5789 if (p
[0 .. 7] == "#define")
5793 //printf("%s\n", n.toChars());
5794 if (n
.value
== TOK
.identifier
)
5803 case TOK
.endOfLine
: // #define identifier
5807 case TOK
.int32Literal
:
5808 case TOK
.charLiteral
: t
= AST
.Type
.tint32
; goto Linteger
;
5809 case TOK
.uns32Literal
: t
= AST
.Type
.tuns32
; goto Linteger
;
5810 case TOK
.int64Literal
: t
= AST
.Type
.tint64
; goto Linteger
;
5811 case TOK
.uns64Literal
: t
= AST
.Type
.tuns64
; goto Linteger
;
5814 const intvalue
= n
.intvalue
;
5816 if (n
.value
== TOK
.endOfLine
)
5818 /* Declare manifest constant:
5819 * enum id = intvalue;
5821 AST
.Expression e
= new AST
.IntegerExp(scanloc
, intvalue
, t
);
5822 auto v
= new AST
.VarDeclaration(scanloc
, t
, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5829 case TOK
.float32Literal
: t
= AST
.Type
.tfloat32
; goto Lfloat
;
5830 case TOK
.float64Literal
: t
= AST
.Type
.tfloat64
; goto Lfloat
;
5831 case TOK
.float80Literal
: t
= AST
.Type
.tfloat80
; goto Lfloat
;
5832 case TOK
.imaginary32Literal
: t
= AST
.Type
.timaginary32
; goto Lfloat
;
5833 case TOK
.imaginary64Literal
: t
= AST
.Type
.timaginary64
; goto Lfloat
;
5834 case TOK
.imaginary80Literal
: t
= AST
.Type
.timaginary80
; goto Lfloat
;
5837 const floatvalue
= n
.floatvalue
;
5839 if (n
.value
== TOK
.endOfLine
)
5841 /* Declare manifest constant:
5842 * enum id = floatvalue;
5844 AST
.Expression e
= new AST
.RealExp(scanloc
, floatvalue
, t
);
5845 auto v
= new AST
.VarDeclaration(scanloc
, t
, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5853 const str = n
.ustring
;
5855 const postfix
= n
.postfix
;
5857 if (n
.value
== TOK
.endOfLine
)
5859 /* Declare manifest constant:
5860 * enum id = "string";
5862 AST
.Expression e
= new AST
.StringExp(scanloc
, str[0 .. len
], len
, 1, postfix
);
5863 auto v
= new AST
.VarDeclaration(scanloc
, null, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5879 if (n
.value
!= TOK
.endOfLine
)