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
);
1926 s
= new AST
.AliasDeclaration(token
.loc
, id
, dt);
1927 insertTypedefToTypedefTab(id
, dt); // remember typedefs
1931 if (auto tt
= dt.isTypeTag())
1933 if (tt
.members
&& (tt
.id || tt
.tok
== TOK
.enum_
))
1936 declareTag(tt
, spec
);
1940 if (level
== LVL
.prototype
)
1941 break; // declared later as Parameter, not VarDeclaration
1943 if (dt.ty
== AST
.Tvoid
)
1944 error("`void` has no value");
1946 AST
.Initializer initializer
;
1947 bool hasInitializer
;
1948 if (token
.value
== TOK
.assign
)
1951 hasInitializer
= true;
1952 initializer
= cparseInitializer();
1954 // declare the symbol
1957 if (isFunctionTypedef(dt))
1960 error("no initializer for function declaration");
1961 if (specifier
.scw
& SCW
.x_Thread_local
)
1962 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1963 auto fd
= new AST
.FuncDeclaration(token
.loc
, Loc
.initial
, id
, specifiersToSTC(level
, specifier
), dt, specifier
.noreturn
);
1964 specifiersToFuncDeclaration(fd
, specifier
);
1969 // Give non-extern variables an implicit void initializer
1970 // if one has not been explicitly set.
1971 if (!hasInitializer
&&
1972 !(specifier
.scw
& (SCW
.xextern | SCW
.xstatic | SCW
.x_Thread_local
) || level
== LVL
.global
))
1973 initializer
= new AST
.VoidInitializer(token
.loc
);
1974 auto vd
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(level
, specifier
));
1975 specifiersToVarDeclaration(vd
, specifier
);
1978 if (level
!= LVL
.global
)
1979 insertIdToTypedefTab(id
); // non-typedef declarations can hide typedefs in outer scopes
1983 // Saw `asm("name")` in the function, type, or variable definition.
1984 // This is equivalent to `pragma(mangle, "name")` in D
1988 https://issues.dlang.org/show_bug.cgi?id=23012
1989 Ideally this would be translated to a pragma(mangle)
1990 decl. This is not possible because ImportC symbols are
1991 (currently) merged before semantic analysis is performed,
1992 so the pragma(mangle) never effects any change on the declarations
1995 Writing to mangleOverride directly avoids this, and is possible
1996 because C only a StringExp is allowed unlike a full fat pragma(mangle)
1997 which is more liberal.
1999 if (auto p
= s
.isDeclaration())
2001 auto str = asmName
.peekString();
2002 p
.mangleOverride
= str;
2003 // p.adFlags |= AST.VarDeclaration.nounderscore;
2004 p
.adFlags |
= 4; // cannot get above line to compile on Ubuntu
2007 s
= applySpecifier(s
, specifier
);
2008 if (level
== LVL
.local
)
2010 // Wrap the declaration in `extern (C) { declaration }`
2011 // Necessary for function pointers, but harmless to apply to all.
2012 auto decls
= new AST
.Dsymbols(1);
2014 s
= new AST
.LinkDeclaration(s
.loc
, linkage
, decls
);
2020 switch (token
.value
)
2022 case TOK
.identifier
:
2025 error(token
.loc
, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s
.toChars(), token
.toChars());
2036 symbolsSave
= symbols
;
2041 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token
.toChars());
2043 return scanPastSemicolon();
2048 /***************************************
2049 * C11 Function Definitions
2050 * function-definition
2051 * declaration-specifiers declarator declaration-list (opt) compound-statement
2055 * declaration-list declaration
2057 * It's already been parsed up to the declaration-list (opt).
2058 * Pick it up from there.
2060 * id = function identifier
2061 * ft = function type
2062 * specifier = function specifiers
2064 * Dsymbol for the function
2066 AST
.Dsymbol
cparseFunctionDefinition(Identifier id
, AST
.TypeFunction ft
, ref Specifier specifier
)
2068 /* Start function scope
2070 typedefTab
.push(null);
2072 if (token
.value
!= TOK
.leftCurly
) // if not start of a compound-statement
2074 // Do declaration-list
2077 cparseDeclaration(LVL
.parameter
);
2078 } while (token
.value
!= TOK
.leftCurly
);
2080 /* Since there were declarations, the parameter-list must have been
2081 * an identifier-list.
2083 ft
.parameterList
.hasIdentifierList
= true; // semantic needs to know to adjust parameter types
2084 auto pl
= ft
.parameterList
;
2085 if (pl
.varargs
!= AST
.VarArg
.none
&& pl
.length
)
2086 error("function identifier-list cannot end with `...`");
2087 ft
.parameterList
.varargs
= AST
.VarArg
.KRvariadic
; // but C11 allows extra arguments
2088 auto plLength
= pl
.length
;
2089 if (symbols
.length
!= plLength
)
2090 error(token
.loc
, "%d identifiers does not match %d declarations", cast(int)plLength
, cast(int)symbols
.length
);
2092 /* Transfer the types and storage classes from symbols[] to pl[]
2094 foreach (i
; 0 .. plLength
)
2096 auto p
= pl
[i
]; // yes, quadratic
2098 // Convert typedef-identifier to identifier
2101 if (auto t
= p
.type
.isTypeIdentifier())
2108 if (p
.type ||
!(p
.storageClass
& STC
.parameter
))
2109 error("storage class and type are not allowed in identifier-list");
2110 foreach (s
; (*symbols
)[]) // yes, quadratic
2112 auto ad
= s
.isAttribDeclaration();
2114 s
= (*ad
.decl
)[0]; // AlignDeclaration wrapping the declaration
2116 auto d
= s
.isDeclaration();
2117 if (d
&& p
.ident
== d
.ident
&& d
.type
)
2120 p
.storageClass
= d
.storage_class
;
2121 d
.type
= null; // don't reuse
2127 error("no declaration for identifier `%s`", p
.ident
.toChars());
2128 p
.type
= AST
.Type
.terror
;
2133 addFuncName
= false; // gets set to true if somebody references __func__ in this function
2134 const locFunc
= token
.loc
;
2136 auto body = cparseStatement(ParseStatementFlags
.curly
); // don't start a new scope; continue with parameter scope
2137 typedefTab
.pop(); // end of function scope
2139 auto fd
= new AST
.FuncDeclaration(locFunc
, prevloc
, id
, specifiersToSTC(LVL
.global
, specifier
), ft
, specifier
.noreturn
);
2140 specifiersToFuncDeclaration(fd
, specifier
);
2144 auto s
= createFuncName(locFunc
, id
);
2145 body = new AST
.CompoundStatement(locFunc
, s
, body);
2149 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2154 /***************************************
2155 * C11 Initialization
2157 * assignment-expression
2158 * { initializer-list }
2159 * { initializer-list , }
2162 * designation (opt) initializer
2163 * initializer-list , designation (opt) initializer
2170 * designator-list designator
2173 * [ constant-expression ]
2178 AST
.Initializer
cparseInitializer()
2180 if (token
.value
!= TOK
.leftCurly
)
2182 auto ae
= cparseAssignExp(); // assignment-expression
2183 return new AST
.ExpInitializer(token
.loc
, ae
);
2186 const loc
= token
.loc
;
2188 /* Collect one or more `designation (opt) initializer`
2189 * into ci.initializerList, but lazily create ci
2191 AST
.CInitializer ci
;
2194 /* There can be 0 or more designators preceding an initializer.
2195 * Collect them in desigInit
2197 AST
.DesigInit desigInit
;
2200 if (token
.value
== TOK
.leftBracket
) // [ constant-expression ]
2203 auto e
= cparseConstantExp();
2204 check(TOK
.rightBracket
);
2205 if (!desigInit
.designatorList
)
2206 desigInit
.designatorList
= new AST
.Designators
;
2207 desigInit
.designatorList
.push(AST
.Designator(e
));
2209 else if (token
.value
== TOK
.dot
) // . identifier
2212 if (token
.value
!= TOK
.identifier
)
2214 error("identifier expected following `.` designator");
2217 if (!desigInit
.designatorList
)
2218 desigInit
.designatorList
= new AST
.Designators
;
2219 desigInit
.designatorList
.push(AST
.Designator(token
.ident
));
2224 if (desigInit
.designatorList
)
2230 desigInit
.initializer
= cparseInitializer();
2232 ci
= new AST
.CInitializer(loc
);
2233 ci
.initializerList
.push(desigInit
);
2234 if (token
.value
== TOK
.comma
)
2237 if (token
.value
!= TOK
.rightCurly
)
2242 check(TOK
.rightCurly
);
2243 //printf("ci: %s\n", ci.toChars());
2247 /*************************************
2249 * declaration-specifier:
2250 * storage-class-specifier declaration-specifiers (opt)
2251 * type-specifier declaration-specifiers (opt)
2252 * type-qualifier declaration-specifiers (opt)
2253 * function-specifier declaration-specifiers (opt)
2254 * alignment-specifier declaration-specifiers (opt)
2256 * level = declaration context
2257 * specifier = specifiers in and out
2259 * resulting type, null if not specified
2261 private AST
.Type
cparseDeclarationSpecifiers(LVL level
, ref Specifier specifier
)
2280 ximaginary
= 0x8000,
2288 //printf("parseDeclarationSpecifiers()\n");
2291 SCW scw
= specifier
.scw
& SCW
.xtypedef
;
2299 //printf("token %s\n", token.toChars());
2303 switch (token
.value
)
2305 // Storage class specifiers
2306 case TOK
.static_
: scwx
= SCW
.xstatic
; break;
2307 case TOK
.extern_
: scwx
= SCW
.xextern
; break;
2308 case TOK
.auto_
: scwx
= SCW
.xauto
; break;
2309 case TOK
.register
: scwx
= SCW
.xregister
; break;
2310 case TOK
.typedef_
: scwx
= SCW
.xtypedef
; break;
2311 case TOK
.inline
: scwx
= SCW
.xinline
; break;
2312 case TOK
._Noreturn
: scwx
= SCW
.x_Noreturn
; break;
2314 case TOK
._Thread_local
: scwx
= SCW
.x_Thread_local
; break;
2317 case TOK
.const_
: modx
= MOD
.xconst
; break;
2318 case TOK
.volatile: modx
= MOD
.xvolatile
; break;
2319 case TOK
.restrict
: modx
= MOD
.xrestrict
; break;
2320 case TOK
.__stdcall
: modx
= MOD
.x__stdcall
; break;
2323 case TOK
.char_
: tkwx
= TKW
.xchar
; break;
2324 case TOK
.signed
: tkwx
= TKW
.xsigned
; break;
2325 case TOK
.unsigned
: tkwx
= TKW
.xunsigned
; break;
2326 case TOK
.int16
: tkwx
= TKW
.xshort
; break;
2327 case TOK
.int32
: tkwx
= TKW
.xint
; break;
2328 case TOK
.int64
: tkwx
= TKW
.xlong
; break;
2329 case TOK
.__int128
: tkwx
= TKW
.xint128
; break;
2330 case TOK
.float32
: tkwx
= TKW
.xfloat
; break;
2331 case TOK
.float64
: tkwx
= TKW
.xdouble
; break;
2332 case TOK
.void_
: tkwx
= TKW
.xvoid
; break;
2333 case TOK
._Bool
: tkwx
= TKW
.xbool
; break;
2334 case TOK
._Imaginary
: tkwx
= TKW
.ximaginary
; break;
2335 case TOK
._Complex
: tkwx
= TKW
.xcomplex
; break;
2337 case TOK
.identifier
:
2345 const structOrUnion
= token
.value
;
2346 const sloc
= token
.loc
;
2349 Specifier tagSpecifier
;
2352 * struct-or-union-specifier:
2353 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2354 * struct-or-union gnu-attribute (opt) identifier
2358 if (token
.value
== TOK
.__attribute__
)
2359 cparseGnuAttributes(tagSpecifier
);
2360 else if (token
.value
== TOK
.__declspec
)
2361 cparseDeclspec(tagSpecifier
);
2362 else if (token
.value
== TOK
.__pragma
)
2363 uupragmaDirective(sloc
);
2367 t
= cparseStruct(sloc
, structOrUnion
, tagSpecifier
.packalign
, symbols
);
2373 t
= cparseEnum(symbols
);
2380 // type-specifier if followed by `( type-name )`
2381 auto tk
= peek(&token
);
2382 if (tk
.value
== TOK
.leftParenthesis
)
2385 if (isTypeName(tk
) && tk
.value
== TOK
.rightParenthesis
)
2389 t
= cparseTypeName();
2390 tkwx
= TKW
.x_Atomic
;
2394 // C11 6.7.3 type-qualifier if not
2395 modx
= MOD
.x_Atomic
;
2402 * _Alignas ( type-name )
2403 * _Alignas ( constant-expression )
2406 if (level
& (LVL
.parameter | LVL
.prototype
))
2407 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2410 check(TOK
.leftParenthesis
);
2413 if (isTypeName(tk
)) // _Alignas ( type-name )
2415 auto talign
= cparseTypeName();
2416 /* Convert type to expression: `talign.alignof`
2418 auto e
= new AST
.TypeExp(loc
, talign
);
2419 exp
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
2421 else // _Alignas ( constant-expression )
2423 exp
= cparseConstantExp();
2426 if (!specifier
.alignExps
)
2427 specifier
.alignExps
= new AST
.Expressions(0);
2428 specifier
.alignExps
.push(exp
);
2430 check(TOK
.rightParenthesis
);
2434 case TOK
.__attribute__
:
2437 * declaration-specifiers:
2438 * gnu-attributes declaration-specifiers (opt)
2440 cparseGnuAttributes(specifier
);
2444 case TOK
.__declspec
:
2446 /* Microsoft extension
2448 cparseDeclspec(specifier
);
2455 check(TOK
.leftParenthesis
);
2460 e
= new AST
.TypeExp(loc
, cparseTypeName());
2462 e
= cparseExpression();
2463 t
= new AST
.TypeTypeof(loc
, e
);
2465 if(token
.value
== TOK
.rightParenthesis
)
2469 t
= AST
.Type
.terror
;
2470 error("`typeof` operator expects an expression or type name in parentheses");
2472 // skipParens et. al expect to be on the opening parenthesis
2478 case TOK
.leftParenthesis
:
2481 case TOK
.rightParenthesis
:
2504 if (tkw
& TKW
.xlong
&& tkwx
& TKW
.xlong
)
2509 if (tkw
&& tkwx
& TKW
.xident
)
2511 // 2nd identifier can't be a typedef
2512 break Lwhile
; // leave parser on the identifier for the following declarator
2514 else if (tkwx
& TKW
.xident
)
2516 // 1st identifier, save it for TypeIdentifier
2519 if (tkw
& TKW
.xident
&& tkwx ||
// typedef-name followed by type-specifier
2520 tkw
& tkwx
) // duplicate type-specifiers
2522 error("illegal combination of type specifiers");
2526 if (!(tkwx
& TKW
.xtag
)) // if parser already advanced
2541 error("duplicate storage class");
2543 // C11 6.7.1-2 At most one storage-class may be given, except that
2544 // _Thread_local may appear with static or extern.
2545 const scw2
= scw
& (SCW
.xstatic | SCW
.xextern | SCW
.xauto | SCW
.xregister | SCW
.xtypedef
);
2546 if (scw2
& (scw2
- 1) ||
2547 scw
& (SCW
.x_Thread_local
) && scw
& (SCW
.xauto | SCW
.xregister | SCW
.xtypedef
))
2549 error("multiple storage classes in declaration specifiers");
2552 if (level
== LVL
.local
&&
2553 scw
& (SCW
.x_Thread_local
) && scw
& (SCW
.xinline | SCW
.x_Noreturn
))
2555 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2558 if (level
== LVL
.local
&&
2559 scw
& (SCW
.x_Thread_local
) && !(scw
& (SCW
.xstatic | SCW
.xextern
)))
2561 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2564 if (level
& (LVL
.parameter | LVL
.prototype
) &&
2565 scw
& ~SCW
.xregister
)
2567 error("only `register` storage class allowed for function parameters");
2570 if (level
== LVL
.global
&&
2571 scw
& (SCW
.xauto | SCW
.xregister
))
2573 error("`auto` and `register` storage class not allowed for global");
2581 specifier
.scw
= scw
;
2582 specifier
.mod
= mod
;
2584 // Convert TKW bits to type t
2587 case TKW
.xnone
: t
= null; break;
2589 case TKW
.xchar
: t
= AST
.Type
.tchar
; break;
2590 case TKW
.xsigned | TKW
.xchar
: t
= AST
.Type
.tint8
; break;
2591 case TKW
.xunsigned | TKW
.xchar
: t
= AST
.Type
.tuns8
; break;
2594 case TKW
.xsigned | TKW
.xshort
:
2595 case TKW
.xsigned | TKW
.xshort | TKW
.xint
:
2596 case TKW
.xshort | TKW
.xint
: t
= integerTypeForSize(shortsize
); break;
2598 case TKW
.xunsigned | TKW
.xshort | TKW
.xint
:
2599 case TKW
.xunsigned | TKW
.xshort
: t
= unsignedTypeForSize(shortsize
); break;
2603 case TKW
.xsigned | TKW
.xint
: t
= integerTypeForSize(intsize
); break;
2606 case TKW
.xunsigned | TKW
.xint
: t
= unsignedTypeForSize(intsize
); break;
2609 case TKW
.xsigned | TKW
.xlong
:
2610 case TKW
.xsigned | TKW
.xlong | TKW
.xint
:
2611 case TKW
.xlong | TKW
.xint
: t
= integerTypeForSize(longsize
); break;
2613 case TKW
.xunsigned | TKW
.xlong | TKW
.xint
:
2614 case TKW
.xunsigned | TKW
.xlong
: t
= unsignedTypeForSize(longsize
); break;
2617 case TKW
.xsigned | TKW
.xllong
:
2618 case TKW
.xsigned | TKW
.xllong | TKW
.xint
:
2619 case TKW
.xllong | TKW
.xint
: t
= integerTypeForSize(long_longsize
); break;
2621 case TKW
.xunsigned | TKW
.xllong | TKW
.xint
:
2622 case TKW
.xunsigned | TKW
.xllong
: t
= unsignedTypeForSize(long_longsize
); break;
2625 case TKW
.xsigned | TKW
.xint128
: t
= integerTypeForSize(16); break;
2627 case TKW
.xunsigned | TKW
.xint128
: t
= unsignedTypeForSize(16); break;
2629 case TKW
.xvoid
: t
= AST
.Type
.tvoid
; break;
2630 case TKW
.xbool
: t
= boolsize
== 1 ? AST
.Type
.tbool
: integerTypeForSize(boolsize
); break;
2632 case TKW
.xfloat
: t
= AST
.Type
.tfloat32
; break;
2633 case TKW
.xdouble
: t
= AST
.Type
.tfloat64
; break;
2634 case TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.realfloat
); break;
2636 case TKW
.ximaginary | TKW
.xfloat
: t
= AST
.Type
.timaginary32
; break;
2637 case TKW
.ximaginary | TKW
.xdouble
: t
= AST
.Type
.timaginary64
; break;
2638 case TKW
.ximaginary | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.imaginary
); break;
2640 case TKW
.xcomplex | TKW
.xfloat
: t
= AST
.Type
.tcomplex32
; break;
2641 case TKW
.xcomplex | TKW
.xdouble
: t
= AST
.Type
.tcomplex64
; break;
2642 case TKW
.xcomplex | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.complex
); break;
2646 const idx
= previd
.toString();
2647 if (idx
.length
> 2 && idx
[0] == '_' && idx
[1] == '_') // leading double underscore
2648 importBuiltins
= true; // probably one of those compiler extensions
2651 /* Punch through to what the typedef is, to support things like:
2654 auto pt
= lookupTypedef(previd
);
2655 if (pt
&& *pt
) // if previd is a known typedef
2659 t
= new AST
.TypeIdentifier(loc
, previd
);
2664 case TKW
.x_Atomic
: // no atomics for you
2665 break; // t is already set
2668 error("illegal type combination");
2669 t
= AST
.Type
.terror
;
2676 /********************************
2678 * Parse a declarator (including function definitions).
2680 * pointer (opt) direct-declarator
2682 * direct-declarator :
2685 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2686 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2687 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2688 * direct-declarator [ type-qualifier-list (opt) * ]
2689 * direct-declarator ( parameter-type-list )
2690 * direct-declarator ( identifier-list (opt) )
2693 * * type-qualifier-list (opt)
2694 * * type-qualifier-list (opt) pointer
2696 * type-qualifier-list :
2698 * type-qualifier-list type-qualifier
2700 * parameter-type-list :
2702 * parameter-list , ...
2705 * parameter-declaration
2706 * parameter-list , parameter-declaration
2708 * parameter-declaration :
2709 * declaration-specifiers declarator
2710 * declaration-specifiers abstract-declarator (opt)
2714 * identifier-list , identifier
2717 * declarator = declarator kind
2718 * tbase = base type to start with
2719 * pident = set to Identifier if there is one, null if not
2720 * specifier = specifiers in and out
2722 * type declared. If a TypeFunction is returned, this.symbols is the
2723 * symbol table for the parameter-type-list, which will contain any
2724 * declared struct, union or enum tags.
2726 private AST
.Type
cparseDeclarator(DTR declarator
, AST
.Type tbase
,
2727 out Identifier pident
, ref Specifier specifier
)
2729 //printf("cparseDeclarator(%d, %p)\n", declarator, t);
2730 AST
.Types constTypes
; // all the Types that will need `const` applied to them
2732 /* Insert tx -> t into
2735 * ts -> ... -> tx -> t
2737 static void insertTx(ref AST
.Type ts
, AST
.Type tx
, AST
.Type t
)
2740 for (pt
= &ts
; *pt
!= t
; pt
= &(cast(AST
.TypeNext
)*pt
).next
)
2746 AST
.Type
parseDecl(AST
.Type t
)
2751 switch (token
.value
)
2753 case TOK
.identifier
: // identifier
2754 //printf("identifier %s\n", token.ident.toChars());
2755 if (declarator
== DTR
.xabstract
)
2756 error("identifier not allowed in abstract-declarator");
2757 pident
= token
.ident
;
2762 case TOK
.leftParenthesis
: // ( declarator )
2768 if (token
.value
== TOK
.__stdcall
) // T (__stdcall*fp)();
2770 specifier
.mod |
= MOD
.x__stdcall
;
2775 check(TOK
.rightParenthesis
);
2778 case TOK
.mul: // pointer
2779 t
= new AST
.TypePointer(t
);
2781 // add post fixes const/volatile/restrict/_Atomic
2782 const mod
= cparseTypeQualifierList();
2783 if (mod
& MOD
.xconst
)
2785 if (token
.value
== TOK
.__attribute__
)
2786 cparseGnuAttributes(specifier
);
2790 if (declarator
== DTR
.xdirect
)
2792 if (!t || t
.isTypeIdentifier())
2795 error("no type-specifier for declarator");
2796 t
= AST
.Type
.tint32
;
2799 error("identifier or `(` expected"); // )
2808 // parse DeclaratorSuffixes
2811 switch (token
.value
)
2813 case TOK
.leftBracket
:
2815 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2819 auto mod
= cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2823 if (token
.value
== TOK
.static_
)
2825 isStatic
= true; // `static`
2827 if (!mod
) // type qualifiers after `static`
2828 mod
= cparseTypeQualifierList();
2830 else if (token
.value
== TOK
.mul)
2832 if (peekNext() == TOK
.rightBracket
)
2834 isVLA
= true; // `*`
2839 if (isStatic || token
.value
!= TOK
.rightBracket
)
2841 //printf("It's a static array\n");
2842 AST
.Expression e
= cparseAssignExp(); // [ expression ]
2843 ta
= new AST
.TypeSArray(t
, e
);
2847 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2849 ta
= new AST
.TypeSArray(t
);
2851 check(TOK
.rightBracket
);
2853 // Issue errors for unsupported types.
2854 if (isVLA
) // C11 6.7.6.2
2856 error("variable length arrays are not supported");
2858 if (isStatic
) // C11 6.7.6.3
2860 error("static array parameters are not supported");
2862 if (declarator
!= DTR
.xparameter
)
2864 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2867 error("variable length array used outside of function prototype");
2868 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2869 * in a declaration of a function parameter with an array type.
2871 if (isStatic || mod
)
2872 error("static or type qualifier used outside of function prototype");
2874 if (ts
.isTypeSArray() || ts
.isTypeDArray())
2876 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2877 * in the outermost array type derivation.
2879 if (isStatic || mod
)
2880 error("static or type qualifier used in non-outermost array type derivation");
2881 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2884 if (ta
.isTypeSArray() && ta
.isTypeSArray().isIncomplete() && !isVLA
)
2885 error("array type has incomplete element type `%s`", ta
.toChars());
2888 // Apply type qualifiers to the constructed type.
2889 if (mod
& MOD
.xconst
) // ignore the other bits
2891 insertTx(ts
, ta
, t
); // ts -> ... -> ta -> t
2895 case TOK
.leftParenthesis
:
2897 // New symbol table for parameter-list
2898 auto symbolsSave
= this.symbols
;
2899 this.symbols
= null;
2901 auto parameterList
= cparseParameterList();
2902 const lkg
= specifier
.mod
& MOD
.x__stdcall ? LINK
.windows
: linkage
;
2903 StorageClass
stc = specifier
._nothrow ? STC
.nothrow_
: 0;
2904 if (specifier
._pure
)
2906 AST
.Type tf
= new AST
.TypeFunction(parameterList
, t
, lkg
, stc);
2907 // tf = tf.addSTC(storageClass); // TODO
2908 insertTx(ts
, tf
, t
); // ts -> ... -> tf -> t
2911 this.symbols
= symbolsSave
;
2923 auto t
= parseDecl(tbase
);
2925 if (specifier
.vector_size
)
2927 auto length
= new AST
.IntegerExp(token
.loc
, specifier
.vector_size
/ tbase
.size(), AST
.Type
.tuns32
);
2928 auto tsa
= new AST
.TypeSArray(tbase
, length
);
2929 AST
.Type tv
= new AST
.TypeVector(tsa
);
2930 specifier
.vector_size
= 0; // used it up
2932 insertTx(t
, tv
, tbase
); // replace tbase with tv
2935 /* Because const is transitive, cannot assemble types from
2936 * fragments. Instead, types to be annotated with const are put
2937 * in constTypes[], and a bottom up scan of t is done to apply
2940 if (constTypes
.length
)
2942 AST
.Type
constApply(AST
.Type t
)
2946 auto tn
= cast(AST
.TypeNext
)t
; // t.nextOf() should return a ref instead of this
2947 tn
.next
= constApply(tn
.next
);
2949 foreach (tc
; constTypes
[])
2959 if (declarator
== DTR
.xparameter
&&
2962 /* Because there are instances in .h files of "const pointer to mutable",
2963 * skip applying transitive `const`
2964 * https://issues.dlang.org/show_bug.cgi?id=22534
2966 auto tn
= cast(AST
.TypeNext
)t
;
2967 tn
.next
= constApply(tn
.next
);
2973 //printf("result: %s\n", t.toChars());
2977 /******************************
2986 MOD
cparseTypeQualifierList()
2991 switch (token
.value
)
2993 case TOK
.const_
: mod |
= MOD
.xconst
; break;
2994 case TOK
.volatile: mod |
= MOD
.xvolatile
; break;
2995 case TOK
.restrict
: mod |
= MOD
.xrestrict
; break;
2996 case TOK
._Atomic
: mod |
= MOD
.x_Atomic
; break;
2997 case TOK
.__stdcall
: mod |
= MOD
.x__stdcall
; break;
3006 /***********************************
3009 AST
.Type
cparseTypeName()
3011 Specifier specifier
;
3012 specifier
.packalign
.setDefault();
3013 auto tspec
= cparseSpecifierQualifierList(LVL
.global
, specifier
);
3016 error("type-specifier is missing");
3017 tspec
= AST
.Type
.tint32
;
3019 if (tspec
&& specifier
.mod
& MOD
.xconst
)
3021 tspec
= toConst(tspec
);
3022 specifier
.mod
= MOD
.xnone
; // 'used' it
3025 return cparseDeclarator(DTR
.xabstract
, tspec
, id
, specifier
);
3028 /***********************************
3030 * specifier-qualifier-list:
3031 * type-specifier specifier-qualifier-list (opt)
3032 * type-qualifier specifier-qualifier-list (opt)
3034 * level = declaration context
3035 * specifier = specifiers in and out
3037 * resulting type, null if not specified
3039 AST
.Type
cparseSpecifierQualifierList(LVL level
, ref Specifier specifier
)
3041 auto t
= cparseDeclarationSpecifiers(level
, specifier
);
3043 error("storage class not allowed in specifier-qualified-list");
3047 /***********************************
3049 * ( parameter-type-list )
3050 * ( identifier-list (opt) )
3052 AST
.ParameterList
cparseParameterList()
3054 auto parameters
= new AST
.Parameters();
3055 AST
.VarArg varargs
= AST
.VarArg
.none
;
3056 StorageClass varargsStc
;
3058 check(TOK
.leftParenthesis
);
3059 if (token
.value
== TOK
.void_
&& peekNext() == TOK
.rightParenthesis
) // func(void)
3063 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3066 if (token
.value
== TOK
.rightParenthesis
) // func()
3069 return AST
.ParameterList(parameters
, AST
.VarArg
.KRvariadic
, varargsStc
);
3072 /* Create function prototype scope
3074 typedefTab
.push(null);
3076 AST
.ParameterList
finish()
3079 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3082 /* The check for identifier-list comes later,
3083 * when doing the trailing declaration-list (opt)
3087 if (token
.value
== TOK
.rightParenthesis
)
3089 if (token
.value
== TOK
.dotDotDot
)
3091 if (parameters
.length
== 0) // func(...)
3092 error("named parameter required before `...`");
3093 importBuiltins
= true; // will need __va_list_tag
3094 varargs
= AST
.VarArg
.variadic
; // C-style variadics
3096 check(TOK
.rightParenthesis
);
3100 Specifier specifier
;
3101 specifier
.packalign
.setDefault();
3102 auto tspec
= cparseDeclarationSpecifiers(LVL
.prototype
, specifier
);
3105 error("no type-specifier for parameter");
3106 tspec
= AST
.Type
.tint32
;
3109 if (specifier
.mod
& MOD
.xconst
)
3111 if ((token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.comma
) &&
3112 tspec
.isTypeIdentifier())
3113 error("type-specifier omitted for parameter `%s`", tspec
.isTypeIdentifier().ident
.toChars());
3115 tspec
= toConst(tspec
);
3116 specifier
.mod
= MOD
.xnone
; // 'used' it
3120 const paramLoc
= token
.loc
;
3121 auto t
= cparseDeclarator(DTR
.xparameter
, tspec
, id
, specifier
);
3122 if (token
.value
== TOK
.__attribute__
)
3123 cparseGnuAttributes(specifier
);
3124 if (specifier
.mod
& MOD
.xconst
)
3126 auto param
= new AST
.Parameter(paramLoc
, specifiersToSTC(LVL
.parameter
, specifier
),
3128 parameters
.push(param
);
3129 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
3133 check(TOK
.rightParenthesis
);
3137 /***********************************
3139 * _Static_assert ( constant-expression , string-literal ) ;
3141 private AST
.StaticAssert
cparseStaticAssert()
3143 const loc
= token
.loc
;
3145 //printf("cparseStaticAssert()\n");
3147 check(TOK
.leftParenthesis
);
3148 auto exp
= cparseConstantExp();
3150 if (token
.value
!= TOK
.string_
)
3151 error("string literal expected");
3152 auto msg
= cparsePrimaryExp();
3153 check(TOK
.rightParenthesis
);
3154 check(TOK
.semicolon
);
3155 return new AST
.StaticAssert(loc
, exp
, msg
);
3158 /*************************
3159 * Collect argument list.
3160 * Parser is on opening parenthesis.
3164 private AST
.Expressions
* cparseArguments()
3167 auto arguments
= new AST
.Expressions();
3168 while (token
.value
!= TOK
.rightParenthesis
&& token
.value
!= TOK
.endOfFile
)
3170 auto arg
= cparseAssignExp();
3171 arguments
.push(arg
);
3172 if (token
.value
!= TOK
.comma
)
3175 nextToken(); // consume comma
3178 check(TOK
.rightParenthesis
);
3183 /*************************
3185 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3187 * __declspec ( extended-decl-modifier-seq )
3189 * extended-decl-modifier-seq:
3190 * extended-decl-modifier (opt)
3191 * extended-decl-modifier extended-decl-modifier-seq
3193 * extended-decl-modifier:
3195 * deprecated(depMsg)
3204 * specifier = filled in with the attribute(s)
3206 private void cparseDeclspec(ref Specifier specifier
)
3208 //printf("cparseDeclspec()\n");
3209 /* Check for dllexport, dllimport
3212 nextToken(); // move past __declspec
3213 check(TOK
.leftParenthesis
);
3216 if (token
.value
== TOK
.rightParenthesis
)
3221 else if (token
.value
== TOK
.endOfFile
)
3223 else if (token
.value
== TOK
.identifier
)
3225 if (token
.ident
== Id
.dllimport
)
3227 specifier
.dllimport
= true;
3230 else if (token
.ident
== Id
.dllexport
)
3232 specifier
.dllexport
= true;
3235 else if (token
.ident
== Id
.naked
)
3237 specifier
.naked
= true;
3240 else if (token
.ident
== Id
.noinline
)
3242 specifier
.scw |
= SCW
.xnoinline
;
3245 else if (token
.ident
== Id
.noreturn
)
3247 specifier
.noreturn
= true;
3250 else if (token
.ident
== Id
._nothrow
)
3252 specifier
._nothrow
= true;
3255 else if (token
.ident
== Id
.thread
)
3257 specifier
.scw |
= SCW
.x_Thread_local
;
3260 else if (token
.ident
== Id
._align
)
3262 // Microsoft spec is very imprecise as to how this actually works
3264 check(TOK
.leftParenthesis
);
3265 if (token
.value
== TOK
.int32Literal
)
3267 const n
= token
.unsvalue
;
3268 if (n
< 1 || n
& (n
- 1) ||
8192 < n
)
3269 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n
);
3270 specifier
.packalign
.set(cast(uint)n
);
3271 specifier
.packalign
.setPack(true);
3276 error("alignment value expected, not `%s`", token
.toChars());
3280 check(TOK
.rightParenthesis
);
3282 else if (token
.ident
== Id
._deprecated
)
3284 specifier
._deprecated
= true;
3286 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3289 specifier
.depMsg
= cparseExpression();
3290 check(TOK
.rightParenthesis
);
3296 if (token
.value
== TOK
.leftParenthesis
)
3300 else if (token
.value
== TOK
.restrict
) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3304 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token
.toChars());
3306 if (token
.value
!= TOK
.rightParenthesis
)
3312 /*************************
3313 * Parser for asm label. It appears after the declarator, and has apparently
3314 * nothing to do with inline assembler.
3315 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3317 * asm ( asm-string-literal )
3319 * asm-string-literal:
3322 private AST
.StringExp
cparseGnuAsmLabel()
3324 nextToken(); // move past asm
3325 check(TOK
.leftParenthesis
);
3326 if (token
.value
!= TOK
.string_
)
3327 error("string literal expected for Asm Label, not `%s`", token
.toChars());
3328 auto label
= cparsePrimaryExp();
3329 check(TOK
.rightParenthesis
);
3330 return cast(AST
.StringExp
) label
;
3333 /********************
3334 * Parse C inline assembler statement in Gnu format.
3335 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3336 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3337 * Current token is on the `asm`.
3339 * inline assembler expression as a Statement
3341 private AST
.Statement
cparseGnuAsm()
3343 // Defer parsing of AsmStatements until semantic processing.
3344 const loc
= token
.loc
;
3348 // Consume all asm-qualifiers. As a future optimization, we could record
3349 // the `inline` and `volatile` storage classes against the statement.
3350 while (token
.value
== TOK
.goto_ ||
3351 token
.value
== TOK
.inline ||
3352 token
.value
== TOK
.volatile)
3355 check(TOK
.leftParenthesis
);
3356 if (token
.value
!= TOK
.string_
)
3357 error("string literal expected for Assembler Template, not `%s`", token
.toChars());
3358 Token
* toklist
= null;
3359 Token
** ptoklist
= &toklist
;
3360 //Identifier label = null;
3361 auto statements
= new AST
.Statements();
3366 switch (token
.value
)
3368 case TOK
.leftParenthesis
:
3372 case TOK
.rightParenthesis
:
3379 error("matching `)` expected, not `;`");
3384 error("matching `)` expected, not end of file");
3387 case TOK
.colonColon
: // treat as two separate : tokens for iasmgcc
3388 *ptoklist
= allocateToken();
3389 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3390 (*ptoklist
).value
= TOK
.colon
;
3391 ptoklist
= &(*ptoklist
).next
;
3393 *ptoklist
= allocateToken();
3394 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3395 (*ptoklist
).value
= TOK
.colon
;
3396 ptoklist
= &(*ptoklist
).next
;
3403 *ptoklist
= allocateToken();
3404 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3405 ptoklist
= &(*ptoklist
).next
;
3412 // Create AsmStatement from list of tokens we've saved
3413 AST
.Statement s
= new AST
.AsmStatement(token
.loc
, toklist
);
3419 auto s
= new AST
.CompoundAsmStatement(loc
, statements
, 0);
3423 /*************************
3424 * __attribute__ parser
3425 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3427 * gnu-attributes gnu-attribute-specifier
3429 * gnu-attribute-specifier:
3430 * __attribute__ (( gnu-attribute-list ))
3432 * gnu-attribute-list:
3433 * gnu-attribute (opt)
3434 * gnu-attribute-list , gnu-attribute
3437 * specifier = filled in with the attribute(s)
3439 private void cparseGnuAttributes(ref Specifier specifier
)
3441 while (token
.value
== TOK
.__attribute__
)
3443 nextToken(); // move past __attribute__
3444 check(TOK
.leftParenthesis
);
3445 check(TOK
.leftParenthesis
);
3447 if (token
.value
!= TOK
.rightParenthesis
)
3451 cparseGnuAttribute(specifier
);
3452 if (token
.value
!= TOK
.comma
)
3458 check(TOK
.rightParenthesis
);
3459 check(TOK
.rightParenthesis
);
3463 /*************************
3464 * Parse a single GNU attribute
3466 * gnu-attribute-name
3467 * gnu-attribute-name ( identifier )
3468 * gnu-attribute-name ( identifier , expression-list )
3469 * gnu-attribute-name ( expression-list (opt) )
3471 * gnu-attribute-name:
3476 * constant-expression
3477 * expression-list , constant-expression
3480 * specifier = filled in with the attribute(s)
3482 private void cparseGnuAttribute(ref Specifier specifier
)
3484 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3487 if (!isGnuAttributeName())
3490 if (token
.value
== TOK
.identifier
)
3492 if (token
.ident
== Id
.aligned
)
3495 if (token
.value
== TOK
.leftParenthesis
)
3498 if (token
.value
== TOK
.int32Literal
)
3500 const n
= token
.unsvalue
;
3501 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
3502 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n
);
3503 specifier
.packalign
.set(cast(uint)n
);
3504 specifier
.packalign
.setPack(true);
3509 error("alignment value expected, not `%s`", token
.toChars());
3513 check(TOK
.rightParenthesis
);
3515 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3516 * type on the target machine. It's the opposite of __attribute__((packed))
3519 else if (token
.ident
== Id
.always_inline
) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3521 specifier
.scw |
= SCW
.xinline
;
3524 else if (token
.ident
== Id
._deprecated
)
3526 specifier
._deprecated
= true;
3528 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3531 specifier
.depMsg
= cparseExpression();
3532 check(TOK
.rightParenthesis
);
3535 else if (token
.ident
== Id
.dllimport
)
3537 specifier
.dllimport
= true;
3540 else if (token
.ident
== Id
.dllexport
)
3542 specifier
.dllexport
= true;
3545 else if (token
.ident
== Id
.naked
)
3547 specifier
.naked
= true;
3550 else if (token
.ident
== Id
.noinline
)
3552 specifier
.scw |
= SCW
.xnoinline
;
3555 else if (token
.ident
== Id
.noreturn
)
3557 specifier
.noreturn
= true;
3560 else if (token
.ident
== Id
._nothrow
)
3562 specifier
._nothrow
= true;
3565 else if (token
.ident
== Id
._pure
)
3567 specifier
._pure
= true;
3570 else if (token
.ident
== Id
.vector_size
)
3573 check(TOK
.leftParenthesis
);
3574 if (token
.value
== TOK
.int32Literal
)
3576 const n
= token
.unsvalue
;
3577 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
3578 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n
);
3579 specifier
.vector_size
= cast(uint) n
;
3584 error("value for vector_size expected, not `%s`", token
.toChars());
3587 check(TOK
.rightParenthesis
);
3592 if (token
.value
== TOK
.leftParenthesis
)
3599 if (token
.value
== TOK
.leftParenthesis
)
3604 /*************************
3605 * See if match for GNU attribute name, which may be any identifier,
3606 * storage-class-specifier, type-specifier, or type-qualifier.
3608 * true if a valid GNU attribute name
3610 private bool isGnuAttributeName()
3612 switch (token
.value
)
3614 case TOK
.identifier
:
3630 case TOK
._Thread_local
:
3646 /***************************
3647 * Like skipParens(), but consume the tokens.
3649 private void cparseParens()
3651 check(TOK
.leftParenthesis
);
3656 switch (token
.value
)
3658 case TOK
.leftParenthesis
:
3662 case TOK
.rightParenthesis
:
3666 error("extra right parenthesis");
3677 error("end of file found before right parenthesis");
3688 /******************************************************************************/
3689 /***************************** Struct & Enum Parser ***************************/
3692 /*************************************
3695 * enum identifier (opt) { enumerator-list }
3696 * enum identifier (opt) { enumerator-list , }
3701 * enumerator-list , enumerator
3704 * enumeration-constant
3705 * enumeration-constant = constant-expression
3707 * enumeration-constant:
3711 * symbols = symbols to add enum declaration to
3715 private AST
.Type
cparseEnum(ref AST
.Dsymbols
* symbols
)
3717 const loc
= token
.loc
;
3722 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3723 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3724 * enum gnu-attributes (opt) identifier
3726 Specifier specifier
;
3727 specifier
.packalign
.setDefault();
3728 if (token
.value
== TOK
.__attribute__
)
3729 cparseGnuAttributes(specifier
);
3732 if (token
.value
== TOK
.identifier
)
3738 /* clang extension: add optional base type after the identifier
3739 * https://en.cppreference.com/w/cpp/language/enum
3740 * enum Identifier : Type
3742 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3743 AST
.Type base
= null; // C23 says base type is determined by enum member values
3744 if (token
.value
== TOK
.colon
)
3747 base
= cparseTypeName();
3750 AST
.Dsymbols
* members
;
3751 if (token
.value
== TOK
.leftCurly
)
3754 members
= new AST
.Dsymbols();
3756 if (token
.value
== TOK
.rightCurly
) // C11 6.7.2.2-1
3759 error("no members for `enum %s`", tag
.toChars());
3761 error("no members for anonymous enum");
3764 while (token
.value
== TOK
.identifier
)
3766 auto ident
= token
.ident
; // enumeration-constant
3768 auto mloc
= token
.loc
;
3770 if (token
.value
== TOK
.__attribute__
)
3772 /* gnu-attributes can appear here, but just scan and ignore them
3773 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3775 Specifier specifierx
;
3776 specifierx
.packalign
.setDefault();
3777 cparseGnuAttributes(specifierx
);
3780 AST
.Expression value
;
3781 if (token
.value
== TOK
.assign
)
3784 value
= cparseConstantExp();
3785 // TODO C11 6.7.2.2-2 value must fit into an int
3788 if (token
.value
== TOK
.__attribute__
)
3790 /* gnu-attributes can appear here, but just scan and ignore them
3791 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3793 Specifier specifierx
;
3794 specifierx
.packalign
.setDefault();
3795 cparseGnuAttributes(specifierx
);
3798 auto em
= new AST
.EnumMember(mloc
, ident
, value
, null, 0, null, null);
3801 if (token
.value
== TOK
.comma
)
3808 check(TOK
.rightCurly
);
3811 * Parse the postfix gnu-attributes (opt)
3813 if (token
.value
== TOK
.__attribute__
)
3814 cparseGnuAttributes(specifier
);
3817 error("missing `identifier` after `enum`");
3819 /* Need semantic information to determine if this is a declaration,
3820 * redeclaration, or reference to existing declaration.
3821 * Defer to the semantic() pass with a TypeTag.
3823 return new AST
.TypeTag(loc
, TOK
.enum_
, tag
, structalign_t
.init
, base
, members
);
3826 /*************************************
3828 * Parse struct and union specifiers.
3829 * Parser is advanced to the tag identifier or brace.
3830 * struct-or-union-specifier:
3831 * struct-or-union identifier (opt) { struct-declaration-list }
3832 * struct-or-union identifier
3838 * struct-declaration-list:
3839 * struct-declaration
3840 * struct-declaration-list struct-declaration
3843 * loc = location of `struct` or `union`
3844 * structOrUnion = TOK.struct_ or TOK.union_
3845 * packalign = alignment to use for struct members
3846 * symbols = symbols to add struct-or-union declaration to
3848 * type of the struct
3850 private AST
.Type
cparseStruct(Loc loc
, TOK structOrUnion
, structalign_t packalign
, ref AST
.Dsymbols
* symbols
)
3854 if (token
.value
== TOK
.identifier
)
3860 AST
.Dsymbols
* members
;
3861 if (token
.value
== TOK
.leftCurly
)
3864 members
= new AST
.Dsymbols(); // so `members` will be non-null even with 0 members
3865 while (token
.value
!= TOK
.rightCurly
)
3867 cparseStructDeclaration(members
);
3869 if (token
.value
== TOK
.endOfFile
)
3872 check(TOK
.rightCurly
);
3874 if ((*members
).length
== 0) // C11 6.7.2.1-8
3876 /* allow empty structs as an extension
3877 * struct-declarator-list:
3878 * struct-declarator (opt)
3883 error("missing tag `identifier` after `%s`", Token
.toChars(structOrUnion
));
3885 // many ways and places to declare alignment
3886 if (packalign
.isUnknown() && !this.packalign
.isUnknown())
3887 packalign
.set(this.packalign
.get());
3889 /* Need semantic information to determine if this is a declaration,
3890 * redeclaration, or reference to existing declaration.
3891 * Defer to the semantic() pass with a TypeTag.
3893 return new AST
.TypeTag(loc
, structOrUnion
, tag
, packalign
, null, members
);
3896 /*************************************
3898 * Parse a struct declaration member.
3899 * struct-declaration:
3900 * specifier-qualifier-list struct-declarator-list (opt) ;
3901 * static_assert-declaration
3903 * struct-declarator-list:
3905 * struct-declarator-list , struct-declarator
3907 * struct-declarator:
3909 * declarator (opt) : constant-expression
3911 * members = where to put the fields (members)
3913 void cparseStructDeclaration(AST
.Dsymbols
* members
)
3915 //printf("cparseStructDeclaration()\n");
3916 if (token
.value
== TOK
._Static_assert
)
3918 auto s
= cparseStaticAssert();
3923 Specifier specifier
;
3924 specifier
.packalign
= this.packalign
;
3925 auto tspec
= cparseSpecifierQualifierList(LVL
.member
, specifier
);
3928 error("no type-specifier for struct member");
3929 tspec
= AST
.Type
.tint32
;
3931 if (specifier
.mod
& MOD
.xconst
)
3933 tspec
= toConst(tspec
);
3934 specifier
.mod
= MOD
.xnone
; // 'used' it
3937 /* If a declarator does not follow, it is unnamed
3939 if (token
.value
== TOK
.semicolon
&& tspec
)
3942 auto tt
= tspec
.isTypeTag();
3945 if (auto ti
= tspec
.isTypeIdentifier())
3947 error("type-specifier omitted before declaration of `%s`", ti
.ident
.toChars());
3949 return; // legal but meaningless empty declaration
3952 /* If anonymous struct declaration
3953 * struct { ... members ... };
3956 if (!tt
.id
&& tt
.members
)
3958 /* members of anonymous struct are considered members of
3959 * the containing struct
3961 auto ad
= new AST
.AnonDeclaration(tt
.loc
, tt
.tok
== TOK
.union_
, tt
.members
);
3962 auto s
= applySpecifier(ad
, specifier
);
3966 if (!tt
.id
&& !tt
.members
)
3967 return; // already gave error in cparseStruct()
3969 /* `struct tag;` and `struct tag { ... };`
3970 * always result in a declaration in the current scope
3972 // TODO: merge in specifier
3973 auto stag
= (tt
.tok
== TOK
.struct_
)
3974 ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false)
3975 : new AST
.UnionDeclaration(tt
.loc
, tt
.id
);
3976 stag
.members
= tt
.members
;
3978 symbols
= new AST
.Dsymbols();
3979 auto s
= applySpecifier(stag
, specifier
);
3988 if (token
.value
== TOK
.colon
)
3990 if (auto ti
= tspec
.isTypeIdentifier())
3992 error("type-specifier omitted before bit field declaration of `%s`", ti
.ident
.toChars());
3993 tspec
= AST
.Type
.tint32
;
3996 // C11 6.7.2.1-12 unnamed bit-field
3997 id
= Identifier
.generateAnonymousId("BitField");
4002 dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
4007 break; // error recovery
4011 AST
.Expression width
;
4012 if (token
.value
== TOK
.colon
)
4014 // C11 6.7.2.1-10 bit-field
4016 width
= cparseConstantExp();
4020 * struct-declarator:
4021 * declarator gnu-attributes (opt)
4022 * declarator (opt) : constant-expression gnu-attributes (opt)
4024 if (token
.value
== TOK
.__attribute__
)
4025 cparseGnuAttributes(specifier
);
4027 if (!tspec
&& !specifier
.scw
&& !specifier
.mod
)
4028 error("specifier-qualifier-list required");
4031 if (specifier
.alignExps
)
4032 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4033 auto s
= new AST
.BitFieldDeclaration(width
.loc
, dt, id
, width
);
4038 if (dt.ty
== AST
.Tvoid
)
4039 error("`void` has no value");
4041 // declare the symbol
4042 // Give member variables an implicit void initializer
4043 auto initializer
= new AST
.VoidInitializer(token
.loc
);
4044 AST
.Dsymbol s
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(LVL
.member
, specifier
));
4045 s
= applySpecifier(s
, specifier
);
4049 switch (token
.value
)
4051 case TOK
.identifier
:
4052 error("missing comma");
4064 error("`;` or `,` expected");
4065 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
4074 /******************************************************************************/
4075 /********************************* Lookahead Parser ***************************/
4078 /************************************
4079 * Determine if the scanner is sitting on the start of a declaration.
4081 * t = current token of the scanner
4082 * needId = flag with additional requirements for a declaration
4083 * endtok = ending token
4084 * pt = will be set ending token (if not null)
4086 * true at start of a declaration
4088 private bool isCDeclaration(ref Token
* pt
)
4091 //printf("isCDeclaration() %s\n", t.toChars());
4092 if (!isDeclarationSpecifiers(t
))
4097 if (t
.value
== TOK
.semicolon
)
4103 if (!isCDeclarator(t
, DTR
.xdirect
))
4105 if (t
.value
== TOK
.asm_
)
4108 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4111 if (t
.value
== TOK
.__attribute__
)
4114 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4117 if (t
.value
== TOK
.assign
)
4120 if (!isInitializer(t
))
4140 /********************************
4141 * See if match for initializer.
4143 * pt = starting token, updated to one past end of initializer if true
4145 * true if initializer
4147 private bool isInitializer(ref Token
* pt
)
4149 //printf("isInitializer()\n");
4152 if (t
.value
== TOK
.leftCurly
)
4160 // skip over assignment-expression, ending before comma or semiColon or EOF
4161 if (!isAssignmentExpression(t
))
4167 /********************************
4169 * postfix-expression ( argument-expression-list(opt) )
4171 * pt = starting token, updated to one past end of initializer if true
4173 * true if function call
4175 private bool isFunctionCall(ref Token
* pt
)
4177 //printf("isFunctionCall()\n");
4180 if (!isPrimaryExpression(t
))
4182 if (t
.value
!= TOK
.leftParenthesis
)
4187 if (!isAssignmentExpression(t
))
4189 if (t
.value
== TOK
.comma
)
4194 if (t
.value
== TOK
.rightParenthesis
)
4201 if (t
.value
!= TOK
.semicolon
)
4207 /********************************
4208 * See if match for assignment-expression.
4210 * pt = starting token, updated to one past end of assignment-expression if true
4212 * true if assignment-expression
4214 private bool isAssignmentExpression(ref Token
* pt
)
4217 //printf("isAssignmentExpression() %s\n", t.toChars());
4219 /* This doesn't actually check for grammar matching an
4220 * assignment-expression. It just matches ( ) [ ] looking for
4221 * an ending token that would terminate one.
4230 case TOK
.rightParenthesis
:
4231 case TOK
.rightBracket
:
4237 case TOK
.leftParenthesis
:
4238 if (!skipParens(t
, &t
))
4241 https://issues.dlang.org/show_bug.cgi?id=22267
4242 If the parser encounters the following
4243 `identifier variableName = (expression);`
4244 the initializer is not identified as such since the parentheses
4245 cause the parser to keep walking indefinitely
4246 (whereas `(1) + 1` would not be affected.).
4251 case TOK
.leftBracket
:
4252 if (!skipBrackets(t
))
4262 any
= true; // assume token was part of an a-e
4271 /********************************
4272 * See if match for constant-expression.
4274 * pt = starting token, updated to one past end of constant-expression if true
4276 * true if constant-expression
4278 private bool isConstantExpression(ref Token
* pt
)
4280 return isAssignmentExpression(pt
);
4283 /********************************
4284 * See if match for declaration-specifiers.
4285 * No errors are diagnosed.
4287 * pt = starting token, updated to one past end of declaration-specifiers if true
4289 * true if declaration-specifiers
4291 private bool isDeclarationSpecifiers(ref Token
* pt
)
4293 //printf("isDeclarationSpecifiers()\n");
4315 //case TOK._Imaginary:
4322 case TOK
.identifier
: // typedef-name
4336 if (t
.value
== TOK
.__attribute__ ||
4337 t
.value
== TOK
.__declspec
)
4340 if (!skipParens(t
, &t
))
4343 if (t
.value
== TOK
.identifier
)
4346 if (t
.value
== TOK
.leftCurly
)
4352 else if (t
.value
== TOK
.leftCurly
)
4362 // storage-class-specifiers
4367 case TOK
._Thread_local
:
4371 // function-specifiers
4384 case TOK
._Alignas
: // alignment-specifier
4385 case TOK
.__declspec
: // decl-specifier
4386 case TOK
.__attribute__
: // attribute-specifier
4388 if (!skipParens(t
, &t
))
4393 // either atomic-type-specifier or type_qualifier
4394 case TOK
._Atomic
: // TODO _Atomic ( type-name )
4396 if (t
.value
== TOK
.leftParenthesis
) // maybe atomic-type-specifier
4400 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4401 { // it's a type-qualifier
4402 t
= tsave
; // back up parser
4406 t
= peek(t
); // move past right parenthesis of atomic-type-specifier
4425 /**************************************
4426 * See if declaration-list is present.
4428 * true if declaration-list is present, even an empty one
4430 bool isDeclarationList(ref Token
* pt
)
4435 if (t
.value
== TOK
.leftCurly
)
4440 if (!isCDeclaration(t
))
4445 /*******************************************
4448 * pt = enters on left brace, set to token past right bracket on true
4450 * true if successful
4452 private bool skipBraces(ref Token
* pt
)
4455 if (t
.value
!= TOK
.leftCurly
)
4469 case TOK
.rightCurly
:
4492 /*******************************************
4495 * pt = enters on left bracket, set to token past right bracket on true
4497 * true if successful
4499 private bool skipBrackets(ref Token
* pt
)
4502 if (t
.value
!= TOK
.leftBracket
)
4511 case TOK
.leftBracket
:
4516 case TOK
.rightBracket
:
4539 /*********************************
4540 * Check to see if tokens starting with *pt form a declarator.
4542 * pt = pointer to starting token, updated to point past declarator if true is returned
4543 * declarator = declarator kind
4547 private bool isCDeclarator(ref Token
* pt
, DTR declarator
)
4552 if (t
.value
== TOK
.mul) // pointer
4555 if (!isTypeQualifierList(t
))
4562 if (t
.value
== TOK
.identifier
)
4564 if (declarator
== DTR
.xabstract
)
4568 else if (t
.value
== TOK
.leftParenthesis
)
4571 if (!isCDeclarator(t
, declarator
))
4573 if (t
.value
!= TOK
.rightParenthesis
)
4577 else if (declarator
== DTR
.xdirect
)
4584 if (t
.value
== TOK
.leftBracket
)
4586 if (!skipBrackets(t
))
4589 else if (t
.value
== TOK
.leftParenthesis
)
4591 if (!skipParens(t
, &t
))
4601 /***************************
4602 * Is this the start of a type-qualifier-list?
4605 * pt = first token; updated with past end of type-qualifier-list if true
4607 * true if start of type-qualifier-list
4609 private bool isTypeQualifierList(ref Token
* pt
)
4633 /***************************
4634 * Is this the start of a type-name?
4636 * pt = first token; updated with past end of type-name if true
4638 * true if start of type-name
4640 private bool isTypeName(ref Token
* pt
)
4643 //printf("isTypeName() %s\n", t.toChars());
4644 if (!isSpecifierQualifierList(t
))
4646 if (!isCDeclarator(t
, DTR
.xabstract
))
4648 if (t
.value
!= TOK
.rightParenthesis
)
4654 /***************************
4655 * Is this the start of a specifier-qualifier-list?
4657 * pt = first token; updated with past end of specifier-qualifier-list if true
4659 * true if start of specifier-qualifier-list
4661 private bool isSpecifierQualifierList(ref Token
* pt
)
4687 //case TOK._Imaginary: // ? missing in Spec
4692 case TOK
.identifier
:
4693 // Use typedef table to disambiguate
4694 if (isTypedef(t
.ident
))
4704 // struct-or-union-specifier
4710 if (t
.value
== TOK
.identifier
)
4713 if (t
.value
== TOK
.leftCurly
)
4719 else if (t
.value
== TOK
.leftCurly
)
4728 // atomic-type-specifier
4731 case TOK
.__attribute__
:
4733 if (t
.value
!= TOK
.leftParenthesis ||
4747 /************************************
4748 * Looking at the leading left parenthesis, and determine if it is
4749 * either of the following:
4750 * ( type-name ) cast-expression
4751 * ( type-name ) { initializer-list }
4755 * pt = starting token, updated to one past end of constant-expression if true
4756 * afterParenType = true if already seen `( type-name )`
4758 * true if matches ( type-name ) ...
4760 private bool isCastExpression(ref Token
* pt
, bool afterParenType
= false)
4763 if (log
) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token
.toChars(pt
.value
), afterParenType
);
4767 case TOK
.leftParenthesis
:
4768 auto tk
= peek(t
); // move past left parenthesis
4769 if (!isTypeName(tk
) || tk
.value
!= TOK
.rightParenthesis
)
4772 goto default; // could be ( type-name ) ( unary-expression )
4775 tk
= peek(tk
); // move past right parenthesis
4777 if (tk
.value
== TOK
.leftCurly
)
4779 // ( type-name ) { initializer-list }
4780 if (!isInitializer(tk
))
4788 if (tk
.value
== TOK
.leftParenthesis
&& peek(tk
).value
== TOK
.rightParenthesis
)
4790 return false; // (type-name)() is not a cast (it might be a function call)
4793 if (!isCastExpression(tk
, true))
4795 if (afterParenType
) // could be ( type-name ) ( unary-expression )
4796 goto default; // where unary-expression also matched type-name
4799 // ( type-name ) cast-expression
4804 if (!afterParenType ||
!isUnaryExpression(t
, afterParenType
))
4808 // if we've already seen ( type-name ), then this is a cast
4812 if (log
) printf("isCastExpression true\n");
4816 /********************************
4817 * See if match for unary-expression.
4819 * pt = starting token, updated to one past end of constant-expression if true
4820 * afterParenType = true if already seen ( type-name ) of a cast-expression
4822 * true if unary-expression
4824 private bool isUnaryExpression(ref Token
* pt
, bool afterParenType
= false)
4830 case TOK
.minusMinus
:
4832 if (!isUnaryExpression(t
, afterParenType
))
4843 if (!isCastExpression(t
, afterParenType
))
4849 if (t
.value
== TOK
.leftParenthesis
)
4854 if (tk
.value
!= TOK
.rightParenthesis
)
4860 if (!isUnaryExpression(t
, afterParenType
))
4866 if (t
.value
!= TOK
.leftParenthesis
)
4869 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4874 // Compound literals are handled by cast and sizeof expressions,
4875 // so be content with just seeing a primary expression.
4876 if (!isPrimaryExpression(t
))
4884 /********************************
4885 * See if match for primary-expression.
4887 * pt = starting token, updated to one past end of constant-expression if true
4889 * true if primary-expression
4891 private bool isPrimaryExpression(ref Token
* pt
)
4896 case TOK
.identifier
:
4897 case TOK
.charLiteral
:
4898 case TOK
.int32Literal
:
4899 case TOK
.uns32Literal
:
4900 case TOK
.int64Literal
:
4901 case TOK
.uns64Literal
:
4902 case TOK
.float32Literal
:
4903 case TOK
.float64Literal
:
4904 case TOK
.float80Literal
:
4905 case TOK
.imaginary32Literal
:
4906 case TOK
.imaginary64Literal
:
4907 case TOK
.imaginary80Literal
:
4912 case TOK
.leftParenthesis
:
4914 if (!skipParens(t
, &t
))
4920 if (!skipParens(t
, &t
))
4932 /******************************************************************************/
4933 /********************************* More ***************************************/
4937 * Declaration context
4941 global
= 1, /// global
4942 parameter
= 2, /// function parameter (declarations for function identifier-list)
4943 prototype
= 4, /// function prototype
4944 local
= 8, /// local
4945 member
= 0x10, /// struct member
4948 /// Types of declarator to parse
4951 xdirect
= 1, /// C11 6.7.6 direct-declarator
4952 xabstract
= 2, /// C11 6.7.7 abstract-declarator
4953 xparameter
= 3, /// parameter declarator may be either direct or abstract
4956 /// C11 6.7.1 Storage-class specifiers
4966 // C11 6.7.4 Function specifiers
4973 /// C11 6.7.3 Type qualifiers
4981 x__stdcall
= 0x10, // Windows linkage extension
4984 /**********************************
4985 * Aggregate for all the various specifiers
4989 bool noreturn
; /// noreturn attribute
4990 bool naked
; /// naked attribute
4991 bool _nothrow
; /// nothrow attribute
4992 bool _pure
; /// pure attribute
4993 bool dllimport
; /// dllimport attribute
4994 bool dllexport
; /// dllexport attribute
4995 bool _deprecated
; /// deprecated attribute
4996 AST
.Expression depMsg
; /// deprecated message
4997 uint vector_size
; /// positive power of 2 multipe of base type size
4999 SCW scw
; /// storage-class specifiers
5000 MOD mod
; /// type qualifiers
5001 AST
.Expressions
* alignExps
; /// alignment
5002 structalign_t packalign
; /// #pragma pack alignment value
5005 /***********************
5006 * Convert from C specifiers to D storage class
5008 * level = declaration context
5009 * specifier = specifiers, context, etc.
5011 * corresponding D storage class
5013 StorageClass
specifiersToSTC(LVL level
, const ref Specifier specifier
)
5016 if (specifier
.scw
& SCW
.x_Thread_local
)
5018 if (level
== LVL
.global
)
5020 if (specifier
.scw
& SCW
.xextern
)
5021 stc = AST
.STC
.extern_
;
5022 else if (specifier
.scw
& SCW
.xstatic
)
5023 stc = AST
.STC
.static_
;
5025 else if (level
== LVL
.local
)
5027 if (specifier
.scw
& SCW
.xextern
)
5028 stc = AST
.STC
.extern_
;
5029 else if (specifier
.scw
& SCW
.xstatic
)
5030 stc = AST
.STC
.static_
;
5032 else if (level
== LVL
.member
)
5034 if (specifier
.scw
& SCW
.xextern
)
5035 stc = AST
.STC
.extern_
;
5036 else if (specifier
.scw
& SCW
.xstatic
)
5037 stc = AST
.STC
.static_
;
5042 if (level
== LVL
.global
)
5044 if (specifier
.scw
& SCW
.xextern
)
5045 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5046 else if (specifier
.scw
& SCW
.xstatic
)
5047 stc = AST
.STC
.gshared | AST
.STC
.static_
;
5049 stc = AST
.STC
.gshared
;
5051 else if (level
== LVL
.local
)
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
;
5057 else if (specifier
.scw
& SCW
.xregister
)
5058 stc = AST
.STC
.register
;
5060 else if (level
== LVL
.parameter
)
5062 if (specifier
.scw
& SCW
.xregister
)
5063 stc = AST
.STC
.register | AST
.STC
.parameter
;
5065 stc = AST
.STC
.parameter
;
5067 else if (level
== LVL
.member
)
5069 if (specifier
.scw
& SCW
.xextern
)
5070 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5071 else if (specifier
.scw
& SCW
.xstatic
)
5072 stc = AST
.STC
.gshared
;
5075 if (specifier
._deprecated
&& !specifier
.depMsg
)
5076 stc |
= AST
.STC
.deprecated_
;
5080 /***********************
5081 * Add attributes from Specifier to function
5083 * fd = function to apply them to
5084 * specifier = specifiers
5086 void specifiersToFuncDeclaration(AST
.FuncDeclaration fd
, const ref Specifier specifier
)
5088 fd
.isNaked
= specifier
.naked
;
5089 fd
.dllImport
= specifier
.dllimport
;
5090 fd
.dllExport
= specifier
.dllexport
;
5092 if (specifier
.scw
& SCW
.xnoinline
)
5093 fd
.inlining
= PINLINE
.never
;
5094 else if (specifier
.scw
& SCW
.xinline
)
5095 fd
.inlining
= PINLINE
.always
;
5098 /***********************
5099 * Add attributes from Specifier to variable
5101 * vd = function to apply them to
5102 * specifier = specifiers
5104 void specifiersToVarDeclaration(AST
.VarDeclaration vd
, const ref Specifier specifier
)
5106 vd
.dllImport
= specifier
.dllimport
;
5107 vd
.dllExport
= specifier
.dllexport
;
5110 /***********************
5111 * Return suitable signed integer type for the given size
5113 * size = size of type
5115 * corresponding signed D integer type
5117 private AST
.Type
integerTypeForSize(ubyte size
)
5120 return AST
.Type
.tint8
;
5122 return AST
.Type
.tint16
;
5124 return AST
.Type
.tint32
;
5126 return AST
.Type
.tint64
;
5129 error("__int128 not supported");
5130 return AST
.Type
.terror
;
5132 error("unsupported integer type");
5133 return AST
.Type
.terror
;
5136 /***********************
5137 * Return suitable unsigned integer type for the given size
5139 * size = size of type
5141 * corresponding unsigned D integer type
5143 private AST
.Type
unsignedTypeForSize(ubyte size
)
5146 return AST
.Type
.tuns8
;
5148 return AST
.Type
.tuns16
;
5150 return AST
.Type
.tuns32
;
5152 return AST
.Type
.tuns64
;
5155 error("unsigned __int128 not supported");
5156 return AST
.Type
.terror
;
5158 error("unsupported integer type");
5159 return AST
.Type
.terror
;
5162 /***********************
5163 * Return suitable D float type for C `long double`
5165 * flags = kind of float to return (real, imaginary, complex).
5167 * corresponding D type
5169 private AST
.Type
realType(RTFlags flags
)
5171 if (long_doublesize
== AST
.Type
.tfloat80
.size())
5173 // On GDC and LDC, D `real` types map to C `long double`, so never
5174 // return a double type when real.sizeof == double.sizeof.
5175 final switch (flags
)
5177 case RTFlags
.realfloat
: return AST
.Type
.tfloat80
;
5178 case RTFlags
.imaginary
: return AST
.Type
.timaginary80
;
5179 case RTFlags
.complex
: return AST
.Type
.tcomplex80
;
5184 final switch (flags
)
5186 case RTFlags
.realfloat
: return long_doublesize
== 8 ? AST
.Type
.tfloat64
: AST
.Type
.tfloat80
;
5187 case RTFlags
.imaginary
: return long_doublesize
== 8 ? AST
.Type
.timaginary64
: AST
.Type
.timaginary80
;
5188 case RTFlags
.complex
: return long_doublesize
== 8 ? AST
.Type
.tcomplex64
: AST
.Type
.tcomplex80
;
5194 * Flags for realType
5196 private enum RTFlags
5203 /********************
5204 * C11 6.4.2.2 Create declaration to predefine __func__
5205 * `static const char __func__[] = " function-name ";`
5207 * loc = location for this declaration
5208 * id = identifier of function
5210 * statement representing the declaration of __func__
5212 private AST
.Statement
createFuncName(Loc loc
, Identifier id
)
5214 const fn
= id
.toString(); // function-name
5215 auto efn
= new AST
.StringExp(loc
, fn
, fn
.length
, 1, 'c');
5216 auto ifn
= new AST
.ExpInitializer(loc
, efn
);
5217 auto lenfn
= new AST
.IntegerExp(loc
, fn
.length
+ 1, AST
.Type
.tuns32
); // +1 for terminating 0
5218 auto tfn
= new AST
.TypeSArray(AST
.Type
.tchar
, lenfn
);
5219 efn
.type
= tfn
.immutableOf();
5220 efn
.committed
= true;
5221 auto sfn
= new AST
.VarDeclaration(loc
, tfn
, Id
.__func__
, ifn
, STC
.gshared | STC
.immutable_
);
5222 auto e
= new AST
.DeclarationExp(loc
, sfn
);
5223 return new AST
.ExpStatement(loc
, e
);
5226 /************************
5227 * After encountering an error, scan forward until a right brace or ; is found
5228 * or the end of the file.
5232 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
5236 /**************************
5237 * Apply `const` to a type.
5239 * t = type to add const to
5243 private AST
.Type
toConst(AST
.Type t
)
5245 // `const` is always applied to the return type, not the
5246 // type function itself.
5247 if (auto tf
= t
.isTypeFunction())
5248 tf
.next
= tf
.next
.addSTC(STC
.const_
);
5249 else if (auto tt
= t
.isTypeTag())
5250 tt
.mod |
= MODFlags
.const_
;
5253 /* Ignore const if the result would be const pointer to mutable
5255 auto tn
= t
.nextOf();
5256 if (!tn || tn
.isConst())
5257 t
= t
.addSTC(STC
.const_
);
5262 /***************************
5263 * Apply specifier to a Dsymbol.
5266 * specifier = specifiers to apply
5268 * Dsymbol with specifiers applied
5270 private AST
.Dsymbol
applySpecifier(AST
.Dsymbol s
, ref Specifier specifier
)
5272 //printf("applySpecifier() %s\n", s.toChars());
5273 if (specifier
._deprecated
)
5275 if (specifier
.depMsg
)
5277 // Wrap declaration in a DeprecatedDeclaration
5278 auto decls
= new AST
.Dsymbols(1);
5280 s
= new AST
.DeprecatedDeclaration(specifier
.depMsg
, decls
);
5284 if (specifier
.alignExps
)
5286 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5287 // Wrap declaration in an AlignDeclaration
5288 auto decls
= new AST
.Dsymbols(1);
5290 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.alignExps
, decls
);
5292 else if (!specifier
.packalign
.isDefault())
5294 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5295 // Wrap #pragma pack in an AlignDeclaration
5296 auto decls
= new AST
.Dsymbols(1);
5298 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.packalign
, decls
);
5305 /******************************************************************************/
5306 /************************** typedefTab symbol table ***************************/
5309 /********************************
5310 * Determines if type t is a function type.
5314 * true if it represents a function
5316 bool isFunctionTypedef(AST
.Type t
)
5318 //printf("isFunctionTypedef() %s\n", t.toChars());
5319 if (t
.isTypeFunction())
5321 if (auto tid
= t
.isTypeIdentifier())
5323 auto pt
= lookupTypedef(tid
.ident
);
5326 return (*pt
).isTypeFunction() !is null;
5332 /********************************
5333 * Determine if `id` is a symbol for a Typedef.
5335 * id = possible typedef
5337 * true if id is a Type
5339 bool isTypedef(Identifier id
)
5341 auto pt
= lookupTypedef(id
);
5345 /*******************************
5346 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5347 * Params: id = identifier for non-typedef symbol
5349 void insertIdToTypedefTab(Identifier id
)
5351 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5352 if (isTypedef(id
)) // if existing typedef
5354 /* Add id as null, so we can later distinguish it from a non-null typedef
5356 auto tab
= cast(void*[void*])(typedefTab
[$ - 1]);
5357 tab
[cast(void*)id
] = cast(void*)null;
5361 /*******************************
5362 * Add `id` to typedefTab[]
5364 * id = identifier for typedef symbol
5365 * t = type of the typedef symbol
5367 void insertTypedefToTypedefTab(Identifier id
, AST
.Type t
)
5369 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5370 if (auto tid
= t
.isTypeIdentifier())
5372 // Try to resolve the TypeIdentifier to its type
5373 auto pt
= lookupTypedef(tid
.ident
);
5377 auto tab
= cast(void*[void*])(typedefTab
[$ - 1]);
5378 tab
[cast(void*)id
] = cast(void*)t
;
5379 typedefTab
[$ - 1] = cast(void*)tab
;
5382 /*********************************
5383 * Lookup id in typedefTab[].
5385 * if not found, then null.
5386 * if found, then Type*. Deferencing it will yield null if it is not
5387 * a typedef, and a type if it is a typedef.
5389 AST
.Type
* lookupTypedef(Identifier id
)
5391 foreach_reverse (tab
; typedefTab
[])
5393 if (auto pt
= cast(void*)id
in cast(void*[void*])tab
)
5395 return cast(AST
.Type
*)pt
;
5398 return null; // not found
5403 /******************************************************************************/
5404 /********************************* Directive Parser ***************************/
5407 override bool parseSpecialTokenSequence()
5411 if (n
.value
== TOK
.int32Literal
)
5416 if (n
.value
== TOK
.identifier
)
5418 if (n
.ident
== Id
.line
)
5420 poundLine(n
, false);
5423 else if (defines
&& (n
.ident
== Id
.define || n
.ident
== Id
.undef
))
5425 /* Append this line to `defines`.
5426 * Not canonicalizing it - assume it already is
5428 defines
.writeByte('#');
5429 defines
.writestring(n
.ident
.toString());
5430 skipToNextLine(defines
);
5431 defines
.writeByte('\n');
5434 else if (n
.ident
== Id
.__pragma
)
5436 pragmaDirective(scanloc
);
5439 else if (n
.ident
== Id
.ident
) // #ident "string"
5442 if (n
.value
== TOK
.string_
&& n
.ptr
[0] == '"' && n
.postfix
== 0)
5444 /* gcc inserts string into the .comment section in the object file.
5445 * Just ignore it for now, but can support it later by writing
5446 * the string to obj_exestr()
5448 //auto comment = n.ustring;
5451 if (n
.value
== TOK
.endOfFile || n
.value
== TOK
.endOfLine
)
5454 error("\"string\" expected after `#ident`");
5458 if (n
.ident
!= Id
.undef
)
5459 error("C preprocessor directive `#%s` is not supported", n
.toChars());
5463 /*********************************************
5465 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5466 * Scanner is on the `__pragma`
5468 * startloc = location to use for error messages
5470 private void uupragmaDirective(const ref Loc startloc
)
5472 const loc
= startloc
;
5473 nextToken(); // move past __pragma
5474 if (token
.value
!= TOK
.leftParenthesis
)
5476 error(loc
, "left parenthesis expected to follow `__pragma` instead of `%s`", token
.toChars());
5482 if (token
.value
== TOK
.identifier
)
5484 if (token
.ident
== Id
.pack
)
5485 pragmaPack(startloc
, false);
5489 if (token
.value
== TOK
.leftParenthesis
)
5494 else if (token
.value
== TOK
.endOfFile
)
5497 else if (token
.value
== TOK
.rightParenthesis
)
5501 error(loc
, "unrecognized `__pragma(%s)`", token
.toChars());
5503 if (token
.value
!= TOK
.rightParenthesis
)
5505 error(loc
, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token
.toChars());
5511 /*********************************************
5512 * C11 6.10.6 Pragma directive
5513 * # pragma pp-tokens(opt) new-line
5514 * The C preprocessor sometimes leaves pragma directives in
5515 * the preprocessed output. Ignore them.
5516 * Upon return, p is at start of next line.
5518 private void pragmaDirective(const ref Loc loc
)
5522 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pack
)
5523 return pragmaPack(loc
, true);
5524 if (n
.value
!= TOK
.endOfLine
)
5530 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5531 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5532 * Scanner is on the `pack`
5534 * startloc = location to use for error messages
5535 * useScan = use scan() to retrieve next token, instead of nextToken()
5537 private void pragmaPack(const ref Loc startloc
, bool useScan
)
5539 const loc
= startloc
;
5541 /* Pull tokens from scan() or nextToken()
5558 if (n
.value
!= TOK
.leftParenthesis
)
5560 error(loc
, "left parenthesis expected to follow `#pragma pack`");
5561 if (n
.value
!= TOK
.endOfLine
)
5568 if (n
.value
!= TOK
.rightParenthesis
)
5570 error(loc
, "right parenthesis expected to close `#pragma pack(`");
5572 if (n
.value
!= TOK
.endOfLine
)
5576 void setPackAlign(ref const Token t
)
5578 const n
= t
.unsvalue
;
5579 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
5580 error(loc
, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n
);
5581 packalign
.set(cast(uint)n
);
5582 packalign
.setPack(true);
5589 records
= new Array
!Identifier
;
5590 packs
= new Array
!structalign_t
;
5593 /* # pragma pack ( show )
5595 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.show
)
5597 if (packalign
.isDefault())
5598 eSink
.warning(startloc
, "current pack attribute is default");
5600 eSink
.warning(startloc
, "current pack attribute is %d", packalign
.get());
5602 return closingParen();
5604 /* # pragma pack ( push )
5605 * # pragma pack ( push , identifier )
5606 * # pragma pack ( push , integer )
5607 * # pragma pack ( push , identifier , integer )
5609 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.push)
5612 Identifier record
= null;
5613 if (n
.value
== TOK
.comma
)
5616 if (n
.value
== TOK
.identifier
)
5620 if (n
.value
== TOK
.comma
)
5623 if (n
.value
== TOK
.int32Literal
)
5629 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5632 else if (n
.value
== TOK
.int32Literal
)
5638 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5640 this.records
.push(record
);
5641 this.packs
.push(packalign
);
5642 return closingParen();
5644 /* # pragma pack ( pop )
5645 * # pragma pack ( pop PopList )
5647 * , IdentifierOrInteger
5648 * , IdentifierOrInteger PopList
5649 * IdentifierOrInteger:
5653 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pop)
5656 size_t len
= this.records
.length
;
5657 if (n
.value
== TOK
.rightParenthesis
) // #pragma pack ( pop )
5659 if (len
== 0) // nothing to pop
5660 return closingParen();
5662 this.records
.setDim(len
- 1);
5663 this.packs
.setDim(len
- 1);
5664 if (len
== 1) // stack is now empty
5665 packalign
.setDefault();
5667 packalign
= (*this.packs
)[len
- 1];
5668 return closingParen();
5670 while (n
.value
== TOK
.comma
) // #pragma pack ( pop ,
5673 if (n
.value
== TOK
.identifier
)
5675 /* pragma pack(pop, identifier
5676 * Pop until identifier is found, pop that one too, and set
5677 * alignment to the new top of the stack.
5678 * If identifier is not found, do nothing.
5682 if ((*this.records
)[len
- 1] == n
.ident
)
5684 this.records
.setDim(len
- 1);
5685 this.packs
.setDim(len
- 1);
5687 packalign
= (*this.packs
)[len
- 2];
5689 packalign
.setDefault(); // stack empty, use default
5695 else if (n
.value
== TOK
.int32Literal
)
5702 error(loc
, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n
.toChars());
5706 return closingParen();
5708 /* # pragma pack ( integer )
5709 * Sets alignment to integer
5711 if (n
.value
== TOK
.int32Literal
)
5715 return closingParen();
5717 /* # pragma pack ( )
5718 * Sets alignment to default
5720 if (n
.value
== TOK
.rightParenthesis
)
5722 packalign
.setDefault();
5723 return closingParen();
5726 error(loc
, "unrecognized `#pragma pack(%s)`", n
.toChars());
5727 if (n
.value
!= TOK
.endOfLine
)
5733 /******************************************************************************/
5734 /********************************* #define Parser *****************************/
5738 * Go through the #define's in the defines buffer and see what we can convert
5739 * to Dsymbols, which are then appended to symbols[]
5743 if (!defines || defines
.length
< 10) // minimum length of a #define line
5745 OutBuffer
* buf
= defines
;
5746 defines
= null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5747 // from appending to slice[]
5748 const length
= buf
.length
;
5750 auto slice
= buf
.peekChars()[0 .. length
];
5751 resetDefineLines(slice
); // reset lexer
5753 const(char)* endp
= &slice
[length
- 7];
5755 size_t
[void*] defineTab
; // hash table of #define's turned into Symbol's
5756 // indexed by Identifier, returns index into symbols[]
5757 // The memory for this is leaked
5759 void addVar(AST
.VarDeclaration v
)
5761 //printf("addVar() %s\n", v.toChars());
5762 v
.isCmacro(true); // mark it as coming from a C #define
5763 /* If it's already defined, replace the earlier
5766 if (size_t
* pd
= cast(void*)v
.ident
in defineTab
)
5768 //printf("replacing %s\n", v.toChars());
5769 (*symbols
)[*pd
] = v
;
5772 defineTab
[cast(void*)v
.ident
] = symbols
.length
;
5780 if (p
[0 .. 7] == "#define")
5784 //printf("%s\n", n.toChars());
5785 if (n
.value
== TOK
.identifier
)
5794 case TOK
.endOfLine
: // #define identifier
5798 case TOK
.int32Literal
:
5799 case TOK
.charLiteral
: t
= AST
.Type
.tint32
; goto Linteger
;
5800 case TOK
.uns32Literal
: t
= AST
.Type
.tuns32
; goto Linteger
;
5801 case TOK
.int64Literal
: t
= AST
.Type
.tint64
; goto Linteger
;
5802 case TOK
.uns64Literal
: t
= AST
.Type
.tuns64
; goto Linteger
;
5805 const intvalue
= n
.intvalue
;
5807 if (n
.value
== TOK
.endOfLine
)
5809 /* Declare manifest constant:
5810 * enum id = intvalue;
5812 AST
.Expression e
= new AST
.IntegerExp(scanloc
, intvalue
, t
);
5813 auto v
= new AST
.VarDeclaration(scanloc
, t
, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5820 case TOK
.float32Literal
: t
= AST
.Type
.tfloat32
; goto Lfloat
;
5821 case TOK
.float64Literal
: t
= AST
.Type
.tfloat64
; goto Lfloat
;
5822 case TOK
.float80Literal
: t
= AST
.Type
.tfloat80
; goto Lfloat
;
5823 case TOK
.imaginary32Literal
: t
= AST
.Type
.timaginary32
; goto Lfloat
;
5824 case TOK
.imaginary64Literal
: t
= AST
.Type
.timaginary64
; goto Lfloat
;
5825 case TOK
.imaginary80Literal
: t
= AST
.Type
.timaginary80
; goto Lfloat
;
5828 const floatvalue
= n
.floatvalue
;
5830 if (n
.value
== TOK
.endOfLine
)
5832 /* Declare manifest constant:
5833 * enum id = floatvalue;
5835 AST
.Expression e
= new AST
.RealExp(scanloc
, floatvalue
, t
);
5836 auto v
= new AST
.VarDeclaration(scanloc
, t
, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5844 const str = n
.ustring
;
5846 const postfix
= n
.postfix
;
5848 if (n
.value
== TOK
.endOfLine
)
5850 /* Declare manifest constant:
5851 * enum id = "string";
5853 AST
.Expression e
= new AST
.StringExp(scanloc
, str[0 .. len
], len
, 1, postfix
);
5854 auto v
= new AST
.VarDeclaration(scanloc
, null, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5870 if (n
.value
!= TOK
.endOfLine
)