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__
:
335 cparseDeclaration(LVL
.local
);
336 if (symbols
.length
> 1)
338 auto as
= new AST
.Statements();
339 as
.reserve(symbols
.length
);
340 foreach (d
; (*symbols
)[])
342 s
= new AST
.ExpStatement(loc
, d
);
345 s
= new AST
.CompoundDeclarationStatement(loc
, as
);
348 else if (symbols
.length
== 1)
350 auto d
= (*symbols
)[0];
351 s
= new AST
.ExpStatement(loc
, d
);
355 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
356 if (flags
& ParseStatementFlags
.scope_
)
357 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
361 case TOK
._Static_assert
: // _Static_assert ( constant-expression, string-literal ) ;
362 s
= new AST
.StaticAssertStatement(cparseStaticAssert());
368 * compound-statement:
369 * { block-item-list (opt) }
373 * block-item-list block-item
380 auto statements
= new AST
.Statements();
381 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.endOfFile
)
383 statements
.push(cparseStatement(ParseStatementFlags
.curlyScope
));
390 *pEndloc
= token
.loc
;
391 pEndloc
= null; // don't set it again
393 s
= new AST
.CompoundStatement(loc
, statements
);
394 if (flags
& (ParseStatementFlags
.scope_ | ParseStatementFlags
.curlyScope
))
395 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
396 check(TOK
.rightCurly
, "compound statement");
403 check(TOK
.leftParenthesis
);
404 auto condition
= cparseExpression();
405 check(TOK
.rightParenthesis
);
407 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
408 s
= new AST
.WhileStatement(loc
, condition
, _body
, endloc
, null);
413 /* C11 6.8.3 null statement
416 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
422 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
424 check(TOK
.leftParenthesis
);
425 auto condition
= cparseExpression();
426 check(TOK
.rightParenthesis
);
427 check(TOK
.semicolon
, "terminating `;` required after do-while statement");
428 s
= new AST
.DoStatement(loc
, _body
, condition
, token
.loc
);
435 AST
.Expression condition
;
436 AST
.Expression increment
;
439 check(TOK
.leftParenthesis
);
440 if (token
.value
== TOK
.semicolon
)
447 _init
= cparseStatement(0);
449 if (token
.value
== TOK
.semicolon
)
456 condition
= cparseExpression();
457 check(TOK
.semicolon
, "`for` condition");
459 if (token
.value
== TOK
.rightParenthesis
)
466 increment
= cparseExpression();
467 check(TOK
.rightParenthesis
);
470 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
471 s
= new AST
.ForStatement(loc
, _init
, condition
, increment
, _body
, endloc
);
478 check(TOK
.leftParenthesis
);
479 auto condition
= cparseExpression();
480 check(TOK
.rightParenthesis
);
481 auto ifbody
= cparseStatement(ParseStatementFlags
.scope_
);
482 AST
.Statement elsebody
;
483 if (token
.value
== TOK
.else_
)
486 elsebody
= cparseStatement(ParseStatementFlags
.scope_
);
490 if (condition
&& ifbody
)
491 s
= new AST
.IfStatement(loc
, null, condition
, ifbody
, elsebody
, token
.loc
);
493 s
= null; // don't propagate parsing errors
498 error("found `else` without a corresponding `if` statement");
504 check(TOK
.leftParenthesis
);
505 auto condition
= cparseExpression();
506 check(TOK
.rightParenthesis
);
507 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
508 s
= new AST
.SwitchStatement(loc
, null, condition
, _body
, false, token
.loc
);
516 auto exp
= cparseAssignExp();
517 AST
.Expression expHigh
;
518 if (token
.value
== TOK
.dotDotDot
)
520 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
523 expHigh
= cparseAssignExp();
527 if (flags
& ParseStatementFlags
.curlyScope
)
529 auto statements
= new AST
.Statements();
530 while (token
.value
!= TOK
.case_
&& token
.value
!= TOK
.default_
&& token
.value
!= TOK
.endOfFile
&& token
.value
!= TOK
.rightCurly
)
532 auto cur
= cparseStatement(ParseStatementFlags
.curlyScope
);
533 statements
.push(cur
);
535 // https://issues.dlang.org/show_bug.cgi?id=21739
536 // Stop at the last break s.t. the following non-case statements are
537 // not merged into the current case. This can happen for
538 // case 1: ... break;
539 // debug { case 2: ... }
540 if (cur
&& cur
.isBreakStatement())
543 s
= new AST
.CompoundStatement(loc
, statements
);
547 s
= cparseStatement(0);
549 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
551 s
= new AST
.CaseRangeStatement(loc
, exp
, expHigh
, s
);
553 s
= new AST
.CaseStatement(loc
, exp
, s
);
562 if (flags
& ParseStatementFlags
.curlyScope
)
564 auto statements
= new AST
.Statements();
565 while (token
.value
!= TOK
.case_
&& token
.value
!= TOK
.default_
&& token
.value
!= TOK
.endOfFile
&& token
.value
!= TOK
.rightCurly
)
567 statements
.push(cparseStatement(ParseStatementFlags
.curlyScope
));
569 s
= new AST
.CompoundStatement(loc
, statements
);
572 s
= cparseStatement(0);
573 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
574 s
= new AST
.DefaultStatement(loc
, s
);
581 * return expression ;
584 auto exp
= token
.value
== TOK
.semicolon ?
null : cparseExpression();
585 check(TOK
.semicolon
, "`return` statement");
586 s
= new AST
.ReturnStatement(loc
, exp
);
592 check(TOK
.semicolon
, "`break` statement");
593 s
= new AST
.BreakStatement(loc
, null);
598 check(TOK
.semicolon
, "`continue` statement");
599 s
= new AST
.ContinueStatement(loc
, null);
606 if (token
.value
!= TOK
.identifier
)
608 error("identifier expected following `goto`");
616 s
= new AST
.GotoStatement(loc
, ident
);
617 check(TOK
.semicolon
, "`goto` statement");
627 case TOK
.leftParenthesis
:
632 // ImportC extensions: parse as a D asm block.
633 s
= parseAsm(compileEnv
.masm
);
639 error("found `%s` instead of statement", token
.toChars());
644 if (token
.value
== TOK
.semicolon
)
651 symbols
= symbolsSave
;
652 typedefTab
.setDim(typedefTabLengthSave
);
657 /*******************************************************************************/
658 /********************************* Expression Parser ***************************/
664 * assignment-expression
665 * expression , assignment-expression
667 AST
.Expression
cparseExpression()
669 auto loc
= token
.loc
;
671 //printf("cparseExpression() loc = %d\n", loc.linnum);
672 auto e
= cparseAssignExp();
673 while (token
.value
== TOK
.comma
)
676 auto e2
= cparseAssignExp();
677 e
= new AST
.CommaExp(loc
, e
, e2
, false);
684 /*********************
686 * primary-expression:
692 * __builtin_va_arg(assign_expression, type)
694 AST
.Expression
cparsePrimaryExp()
697 const loc
= token
.loc
;
699 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
703 const id
= token
.ident
.toString();
704 if (id
.length
> 2 && id
[0] == '_' && id
[1] == '_') // leading double underscore
706 if (token
.ident
is Id
.__func__
)
708 addFuncName
= true; // implicitly declare __func__
710 else if (token
.ident
is Id
.builtin_va_arg
)
712 e
= cparseBuiltin_va_arg();
716 importBuiltins
= true; // probably one of those compiler extensions
718 e
= new AST
.IdentifierExp(loc
, token
.ident
);
722 case TOK
.charLiteral
:
723 case TOK
.int32Literal
:
724 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint32
);
728 case TOK
.uns32Literal
:
729 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns32
);
733 case TOK
.int64Literal
:
734 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint64
);
738 case TOK
.uns64Literal
:
739 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns64
);
743 case TOK
.float32Literal
:
744 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat32
);
748 case TOK
.float64Literal
:
749 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat64
);
753 case TOK
.float80Literal
:
754 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat80
);
758 case TOK
.imaginary32Literal
:
759 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary32
);
763 case TOK
.imaginary64Literal
:
764 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary64
);
768 case TOK
.imaginary80Literal
:
769 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary80
);
775 // cat adjacent strings
776 auto s
= token
.ustring
;
777 auto len
= token
.len
;
778 auto postfix
= token
.postfix
;
782 if (token
.value
== TOK
.string_
)
786 if (token
.postfix
!= postfix
)
787 error(token
.loc
, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix
, token
.postfix
);
788 postfix
= token
.postfix
;
792 const len2
= token
.len
;
794 auto s2
= cast(char*)mem
.xmalloc_noscan(len
* char.sizeof
);
795 memcpy(s2
, s
, len1
* char.sizeof
);
796 memcpy(s2
+ len1
, token
.ustring
, len2
* char.sizeof
);
802 e
= new AST
.StringExp(loc
, s
[0 .. len
], len
, 1, postfix
);
806 case TOK
.leftParenthesis
:
808 if (token
.value
== TOK
.leftCurly
)
809 e
= cparseStatementExpression(); // gcc extension
811 e
= cparseExpression();
812 check(TOK
.rightParenthesis
);
816 e
= cparseGenericSelection();
819 case TOK
._assert
: // __check(assign-exp) extension
821 check(TOK
.leftParenthesis
, "`__check`");
822 e
= parseAssignExp();
823 check(TOK
.rightParenthesis
);
824 e
= new AST
.AssertExp(loc
, e
, null);
828 error("expression expected, not `%s`", token
.toChars());
829 // Anything for e, as long as it's not NULL
830 e
= new AST
.IntegerExp(loc
, 0, AST
.Type
.tint32
);
837 /*********************************
839 * postfix-expression:
841 * postfix-expression [ expression ]
842 * postfix-expression ( argument-expression-list (opt) )
843 * postfix-expression . identifier
844 * postfix-expression -> identifier
845 * postfix-expression ++
846 * postfix-expression --
847 * ( type-name ) { initializer-list }
848 * ( type-name ) { initializer-list , }
850 * argument-expression-list:
851 * assignment-expression
852 * argument-expression-list , assignment-expression
854 private AST
.Expression
cparsePostfixExp(AST
.Expression e
)
856 e
= cparsePrimaryExp();
857 return cparsePostfixOperators(e
);
860 /********************************
862 * Parse a series of operators for a postfix expression after already parsing
863 * a primary-expression or compound literal expression.
865 * e = parsed primary or compound literal expression
867 * parsed postfix expression
869 private AST
.Expression
cparsePostfixOperators(AST
.Expression e
)
873 const loc
= token
.loc
;
878 if (token
.value
== TOK
.identifier
)
880 Identifier id
= token
.ident
;
881 e
= new AST
.DotIdExp(loc
, e
, id
);
884 error("identifier expected following `.`, not `%s`", token
.toChars());
889 if (token
.value
== TOK
.identifier
)
891 Identifier id
= token
.ident
;
892 auto die
= new AST
.DotIdExp(loc
, e
, id
);
897 error("identifier expected following `->`, not `%s`", token
.toChars());
901 e
= new AST
.PostExp(EXP
.plusPlus
, loc
, e
);
905 e
= new AST
.PostExp(EXP
.minusMinus
, loc
, e
);
908 case TOK
.leftParenthesis
:
909 e
= new AST
.CallExp(loc
, e
, cparseArguments());
912 case TOK
.leftBracket
:
914 // array dereferences:
916 AST
.Expression index
;
917 auto arguments
= new AST
.Expressions();
921 index
= cparseAssignExp();
922 arguments
.push(index
);
923 check(TOK
.rightBracket
);
925 e
= new AST
.ArrayExp(loc
, e
, arguments
);
935 /************************
939 * ++ unary-expression
940 * -- unary-expression
941 * unary-operator cast-expression
942 * sizeof unary-expression
943 * sizeof ( type-name )
944 * _Alignof ( type-name )
949 private AST
.Expression
cparseUnaryExp()
952 const loc
= token
.loc
;
958 // Parse `++` as an unary operator so that cast expressions only give
959 // an error for being non-lvalues.
961 e
= new AST
.PreExp(EXP
.prePlusPlus
, loc
, e
);
966 // Parse `--` as an unary operator, same as prefix increment.
968 e
= new AST
.PreExp(EXP
.preMinusMinus
, loc
, e
);
974 e
= new AST
.AddrExp(loc
, e
);
980 e
= new AST
.PtrExp(loc
, e
);
986 e
= new AST
.NegExp(loc
, e
);
992 e
= new AST
.UAddExp(loc
, e
);
998 e
= new AST
.NotExp(loc
, e
);
1003 e
= cparseCastExp();
1004 e
= new AST
.ComExp(loc
, e
);
1010 if (token
.value
== TOK
.leftParenthesis
)
1012 auto tk
= peek(&token
);
1015 /* Expression may be either be requesting the sizeof a type-name
1016 * or a compound literal, which requires checking whether
1017 * the next token is leftCurly
1020 auto t
= cparseTypeName();
1021 check(TOK
.rightParenthesis
);
1022 if (token
.value
== TOK
.leftCurly
)
1024 // ( type-name ) { initializer-list }
1025 auto ci
= cparseInitializer();
1026 e
= new AST
.CompoundLiteralExp(loc
, t
, ci
);
1027 e
= cparsePostfixOperators(e
);
1032 e
= new AST
.TypeExp(loc
, t
);
1037 // must be an expression
1038 e
= cparseUnaryExp();
1044 e
= cparseUnaryExp();
1047 e
= new AST
.DotIdExp(loc
, e
, Id
.__sizeof
);
1054 check(TOK
.leftParenthesis
);
1055 auto t
= cparseTypeName();
1056 check(TOK
.rightParenthesis
);
1057 e
= new AST
.TypeExp(loc
, t
);
1058 e
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
1063 e
= cparsePostfixExp(e
);
1074 * ( type-name ) cast-expression
1076 private AST
.Expression
cparseCastExp()
1078 if (token
.value
== TOK
.leftParenthesis
)
1080 //printf("cparseCastExp()\n");
1081 auto tk
= peek(&token
);
1084 if (tk
.value
== TOK
.identifier
)
1086 iscast
= isTypedef(tk
.ident
);
1091 // ( identifier ) is an expression
1092 return cparseUnaryExp();
1098 if (isCastExpression(pt
))
1100 // Expression may be either a cast or a compound literal, which
1101 // requires checking whether the next token is leftCurly
1102 const loc
= token
.loc
;
1104 auto t
= cparseTypeName();
1105 check(TOK
.rightParenthesis
);
1108 if (token
.value
== TOK
.leftCurly
)
1110 // C11 6.5.2.5 ( type-name ) { initializer-list }
1111 auto ci
= cparseInitializer();
1112 auto ce
= new AST
.CompoundLiteralExp(loc
, t
, ci
);
1113 return cparsePostfixOperators(ce
);
1118 // ( type-name ) cast-expression
1119 auto ce
= cparseCastExp();
1120 return new AST
.CastExp(loc
, ce
, t
);
1123 if (t
.isTypeIdentifier() &&
1125 token
.value
== TOK
.leftParenthesis
&&
1126 !isCastExpression(pt
))
1128 /* (t)(...)... might be a cast expression or a function call,
1129 * with different grammars: a cast would be cparseCastExp(),
1130 * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1131 * We can't know until t is known. So, parse it as a function call
1132 * and let semantic() rewrite the AST as a CastExp if it turns out
1135 auto ie
= new AST
.IdentifierExp(loc
, t
.isTypeIdentifier().ident
);
1136 ie
.parens
= true; // let semantic know it might be a CastExp
1137 AST
.Expression e
= new AST
.CallExp(loc
, ie
, cparseArguments());
1138 return cparsePostfixOperators(e
);
1141 // ( type-name ) cast-expression
1142 auto ce
= cparseCastExp();
1143 return new AST
.CastExp(loc
, ce
, t
);
1146 return cparseUnaryExp();
1151 * multiplicative-expression
1153 * multiplicative-expression * cast-expression
1154 * multiplicative-expression / cast-expression
1155 * multiplicative-expression % cast-expression
1157 private AST
.Expression
cparseMulExp()
1159 const loc
= token
.loc
;
1160 auto e
= cparseCastExp();
1164 switch (token
.value
)
1168 auto e2
= cparseCastExp();
1169 e
= new AST
.MulExp(loc
, e
, e2
);
1174 auto e2
= cparseCastExp();
1175 e
= new AST
.DivExp(loc
, e
, e2
);
1180 auto e2
= cparseCastExp();
1181 e
= new AST
.ModExp(loc
, e
, e2
);
1194 * additive-expression
1195 * multiplicative-expression
1196 * additive-expression + multiplicative-expression
1197 * additive-expression - multiplicative-expression
1199 private AST
.Expression
cparseAddExp()
1201 const loc
= token
.loc
;
1202 auto e
= cparseMulExp();
1206 switch (token
.value
)
1210 auto e2
= cparseMulExp();
1211 e
= new AST
.AddExp(loc
, e
, e2
);
1216 auto e2
= cparseMulExp();
1217 e
= new AST
.MinExp(loc
, e
, e2
);
1231 * additive-expression
1232 * shift-expression << additive-expression
1233 * shift-expression >> additive-expression
1235 private AST
.Expression
cparseShiftExp()
1237 const loc
= token
.loc
;
1238 auto e
= cparseAddExp();
1242 switch (token
.value
)
1246 auto e2
= cparseAddExp();
1247 e
= new AST
.ShlExp(loc
, e
, e2
);
1250 case TOK
.rightShift
:
1252 auto e2
= cparseAddExp();
1253 e
= new AST
.ShrExp(loc
, e
, e2
);
1266 * relational-expression
1268 * relational-expression < shift-expression
1269 * relational-expression > shift-expression
1270 * relational-expression <= shift-expression
1271 * relational-expression >= shift-expression
1273 private AST
.Expression
cparseRelationalExp()
1275 const loc
= token
.loc
;
1277 auto e
= cparseShiftExp();
1279 EXP op
= EXP
.reserved
;
1280 switch (token
.value
)
1282 case TOK
.lessThan
: op
= EXP
.lessThan
; goto Lcmp
;
1283 case TOK
.lessOrEqual
: op
= EXP
.lessOrEqual
; goto Lcmp
;
1284 case TOK
.greaterThan
: op
= EXP
.greaterThan
; goto Lcmp
;
1285 case TOK
.greaterOrEqual
: op
= EXP
.greaterOrEqual
; goto Lcmp
;
1288 auto e2
= cparseShiftExp();
1289 e
= new AST
.CmpExp(op
, loc
, e
, e2
);
1300 * equality-expression
1301 * relational-expression
1302 * equality-expression == relational-expression
1303 * equality-expression != relational-expression
1305 private AST
.Expression
cparseEqualityExp()
1307 const loc
= token
.loc
;
1309 auto e
= cparseRelationalExp();
1311 EXP op
= EXP
.reserved
;
1312 switch (token
.value
)
1314 case TOK
.equal
: op
= EXP
.equal
; goto Lequal
;
1315 case TOK
.notEqual
: op
= EXP
.notEqual
; goto Lequal
;
1318 auto e2
= cparseRelationalExp();
1319 e
= new AST
.EqualExp(op
, loc
, e
, e2
);
1331 * equality-expression
1332 * AND-expression & equality-expression
1334 private AST
.Expression
cparseAndExp()
1336 Loc loc
= token
.loc
;
1337 auto e
= cparseEqualityExp();
1338 while (token
.value
== TOK
.and)
1341 auto e2
= cparseEqualityExp();
1342 e
= new AST
.AndExp(loc
, e
, e2
);
1350 * exclusive-OR-expression
1352 * exclusive-OR-expression ^ AND-expression
1354 private AST
.Expression
cparseXorExp()
1356 const loc
= token
.loc
;
1358 auto e
= cparseAndExp();
1359 while (token
.value
== TOK
.xor)
1362 auto e2
= cparseAndExp();
1363 e
= new AST
.XorExp(loc
, e
, e2
);
1370 * inclusive-OR-expression
1371 * exclusive-OR-expression
1372 * inclusive-OR-expression | exclusive-OR-expression
1374 private AST
.Expression
cparseOrExp()
1376 const loc
= token
.loc
;
1378 auto e
= cparseXorExp();
1379 while (token
.value
== TOK
.or)
1382 auto e2
= cparseXorExp();
1383 e
= new AST
.OrExp(loc
, e
, e2
);
1390 * logical-AND-expression
1391 * inclusive-OR-expression
1392 * logical-AND-expression && inclusive-OR-expression
1394 private AST
.Expression
cparseAndAndExp()
1396 const loc
= token
.loc
;
1398 auto e
= cparseOrExp();
1399 while (token
.value
== TOK
.andAnd
)
1402 auto e2
= cparseOrExp();
1403 e
= new AST
.LogicalExp(loc
, EXP
.andAnd
, e
, e2
);
1410 * logical-OR-expression
1411 * logical-AND-expression
1412 * logical-OR-expression || logical-AND-expression
1414 private AST
.Expression
cparseOrOrExp()
1416 const loc
= token
.loc
;
1418 auto e
= cparseAndAndExp();
1419 while (token
.value
== TOK
.orOr
)
1422 auto e2
= cparseAndAndExp();
1423 e
= new AST
.LogicalExp(loc
, EXP
.orOr
, e
, e2
);
1430 * conditional-expression:
1431 * logical-OR-expression
1432 * logical-OR-expression ? expression : conditional-expression
1434 private AST
.Expression
cparseCondExp()
1436 const loc
= token
.loc
;
1438 auto e
= cparseOrOrExp();
1439 if (token
.value
== TOK
.question
)
1442 auto e1
= cparseExpression();
1444 auto e2
= cparseCondExp();
1445 e
= new AST
.CondExp(loc
, e
, e1
, e2
);
1452 * assignment-expression:
1453 * conditional-expression
1454 * unary-expression assignment-operator assignment-expression
1456 * assignment-operator:
1457 * = *= /= %= += -= <<= >>= &= ^= |=
1459 AST
.Expression
cparseAssignExp()
1462 e
= cparseCondExp(); // constrain it to being unary-expression in semantic pass
1466 const loc
= token
.loc
;
1467 switch (token
.value
)
1471 auto e2
= cparseAssignExp();
1472 e
= new AST
.AssignExp(loc
, e
, e2
);
1477 auto e2
= cparseAssignExp();
1478 e
= new AST
.AddAssignExp(loc
, e
, e2
);
1483 auto e2
= cparseAssignExp();
1484 e
= new AST
.MinAssignExp(loc
, e
, e2
);
1489 auto e2
= cparseAssignExp();
1490 e
= new AST
.MulAssignExp(loc
, e
, e2
);
1495 auto e2
= cparseAssignExp();
1496 e
= new AST
.DivAssignExp(loc
, e
, e2
);
1501 auto e2
= cparseAssignExp();
1502 e
= new AST
.ModAssignExp(loc
, e
, e2
);
1507 auto e2
= cparseAssignExp();
1508 e
= new AST
.AndAssignExp(loc
, e
, e2
);
1513 auto e2
= cparseAssignExp();
1514 e
= new AST
.OrAssignExp(loc
, e
, e2
);
1519 auto e2
= cparseAssignExp();
1520 e
= new AST
.XorAssignExp(loc
, e
, e2
);
1523 case TOK
.leftShiftAssign
:
1525 auto e2
= cparseAssignExp();
1526 e
= new AST
.ShlAssignExp(loc
, e
, e2
);
1529 case TOK
.rightShiftAssign
:
1531 auto e2
= cparseAssignExp();
1532 e
= new AST
.ShrAssignExp(loc
, e
, e2
);
1542 /***********************
1544 * _Generic ( assignment-expression, generic-assoc-list )
1546 * generic-assoc-list:
1547 * generic-association
1548 * generic-assoc-list generic-association
1550 * generic-association:
1551 * type-name : assignment-expression
1552 * default : assignment-expression
1554 private AST
.Expression
cparseGenericSelection()
1556 const loc
= token
.loc
;
1558 check(TOK
.leftParenthesis
);
1559 auto cntlExp
= cparseAssignExp();
1561 auto types
= new AST
.Types();
1562 auto exps
= new AST
.Expressions();
1567 if (token
.value
== TOK
.default_
)
1571 error("only one `default` allowed in generic-assoc-list");
1576 t
= cparseTypeName();
1580 auto e
= cparseAssignExp();
1582 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
1586 check(TOK
.rightParenthesis
);
1587 return new AST
.GenericExp(loc
, cntlExp
, types
, exps
);
1590 /***********************
1591 * C11 6.6 Constant expressions
1592 * constant-expression:
1593 * conditional-expression
1595 private AST
.Expression
cparseConstantExp()
1597 return cparseAssignExp();
1600 /*****************************
1602 * type __builtin_va_arg(assign-expression, type)
1603 * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1604 * va_arg!(type)(assign-expression);
1605 * Lexer is on `__builtin_va_arg`
1607 private AST
.Expression
cparseBuiltin_va_arg()
1609 importBuiltins
= true; // need core.stdc.stdarg
1612 check(TOK
.leftParenthesis
);
1614 auto arguments
= new AST
.Expressions();
1615 auto arg
= cparseAssignExp();
1616 arguments
.push(arg
);
1620 auto t
= cparseTypeName();
1621 auto tiargs
= new AST
.Objects();
1625 auto ti
= new AST
.TemplateInstance(loc
, Id
.va_arg
, tiargs
);
1626 auto tie
= new AST
.ScopeExp(loc
, ti
);
1628 AST
.Expression e
= new AST
.CallExp(loc
, tie
, arguments
);
1630 check(TOK
.rightParenthesis
);
1634 /*****************************
1635 * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1636 * Represent as a function literal, then call the function literal.
1637 * Parser is on opening curly brace.
1639 private AST
.Expression
cparseStatementExpression()
1641 AST
.ParameterList parameterList
;
1642 StorageClass
stc = 0;
1643 const loc
= token
.loc
;
1644 typedefTab
.push(null);
1645 auto fbody
= cparseStatement(ParseStatementFlags
.scope_
);
1646 typedefTab
.pop(); // end of function scope
1648 // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1649 auto ss
= fbody
.isScopeStatement();
1650 auto cs
= ss
.statement
.isCompoundStatement();
1652 if (const len
= (*cs
.statements
).length
)
1654 auto s
= (*cs
.statements
)[len
- 1];
1655 if (auto es
= s
.isExpStatement())
1656 (*cs
.statements
)[len
- 1] = new AST
.ReturnStatement(es
.loc
, es
.exp
);
1659 auto tf
= new AST
.TypeFunction(parameterList
, null, LINK
.d
, stc);
1660 auto fd
= new AST
.FuncLiteralDeclaration(loc
, token
.loc
, tf
, TOK
.delegate_
, null, null, 0);
1663 auto fe
= new AST
.FuncExp(loc
, fd
);
1664 auto args
= new AST
.Expressions();
1665 auto e
= new AST
.CallExp(loc
, fe
, args
); // call the function literal
1670 /********************************************************************************/
1671 /********************************* Declaration Parser ***************************/
1674 /*************************************
1677 * declaration-specifiers init-declarator-list (opt) ;
1678 * static_assert-declaration
1680 * init-declarator-list:
1682 * init-declarator-list , init-declarator
1686 * declarator = initializer
1689 * level = declaration context
1691 void cparseDeclaration(LVL level
)
1693 //printf("cparseDeclaration(level = %d)\n", level);
1694 if (token
.value
== TOK
._Static_assert
)
1696 auto s
= cparseStaticAssert();
1701 if (token
.value
== TOK
.__pragma
)
1703 uupragmaDirective(scanloc
);
1707 if (token
.value
== TOK
._import
) // import declaration extension
1709 auto a
= parseImport();
1715 const typedefTabLengthSave
= typedefTab
.length
;
1716 auto symbolsSave
= symbols
;
1717 Specifier specifier
;
1718 specifier
.packalign
= this.packalign
;
1719 auto tspec
= cparseDeclarationSpecifiers(level
, specifier
);
1721 AST
.Dsymbol
declareTag(AST
.TypeTag tt
, ref Specifier specifier
)
1723 /* `struct tag;` and `struct tag { ... };`
1724 * always result in a declaration in the current scope
1726 auto stag
= (tt
.tok
== TOK
.struct_
) ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false) :
1727 (tt
.tok
== TOK
.union_
) ?
new AST
.UnionDeclaration(tt
.loc
, tt
.id
) :
1728 new AST
.EnumDeclaration(tt
.loc
, tt
.id
, tt
.base
);
1729 if (!tt
.packalign
.isUnknown())
1731 // saw `struct __declspec(align(N)) Tag ...`
1732 auto st
= stag
.isStructDeclaration();
1733 st
.alignment
= tt
.packalign
;
1735 stag
.members
= tt
.members
;
1738 symbols
= new AST
.Dsymbols();
1739 auto stags
= applySpecifier(stag
, specifier
);
1740 symbols
.push(stags
);
1744 /* If a declarator does not follow, it is unnamed
1746 if (token
.value
== TOK
.semicolon
)
1751 return; // accept empty declaration as an extension
1754 if (auto ti
= tspec
.isTypeIdentifier())
1757 error("type-specifier missing for declaration of `%s`", ti
.ident
.toChars());
1763 auto tt
= tspec
.isTypeTag();
1765 !tt
.id
&& (tt
.tok
== TOK
.struct_ || tt
.tok
== TOK
.union_
))
1766 return; // legal but meaningless empty declaration, ignore it
1768 auto stags
= declareTag(tt
, specifier
);
1770 if (0 && tt
.tok
== TOK
.enum_
) // C11 proscribes enums with no members, but we allow it
1773 error(tt
.loc
, "`enum %s` has no members", stags
.toChars());
1780 error("no type for declarator before `%s`", token
.toChars());
1786 if (tspec
&& specifier
.mod
& MOD
.xconst
)
1788 tspec
= toConst(tspec
);
1789 specifier
.mod
&= ~MOD
.xnone
; // 'used' it
1792 void scanPastSemicolon()
1794 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
1799 if (token
.value
== TOK
.assign
&& tspec
&& tspec
.isTypeIdentifier())
1802 * Special check for `const b = 1;` because some compilers allow it
1804 error("type-specifier omitted for declaration of `%s`", tspec
.isTypeIdentifier().ident
.toChars());
1805 return scanPastSemicolon();
1812 AST
.StringExp asmName
;
1813 auto dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
1818 break; // error recovery
1823 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1824 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1826 switch (token
.value
)
1832 case TOK
.__attribute__
:
1833 if (token
.value
== TOK
.asm_
)
1834 asmName
= cparseGnuAsmLabel();
1835 if (token
.value
== TOK
.__attribute__
)
1837 cparseGnuAttributes(specifier
);
1838 if (token
.value
== TOK
.leftCurly
)
1839 break; // function definition
1841 /* This is a data definition, there cannot now be a
1842 * function definition.
1851 if (specifier
.alignExps
&& dt.isTypeFunction())
1852 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1853 if (specifier
.alignExps
&& specifier
.scw
== SCW
.xregister
)
1854 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1856 /* C11 6.9.1 Function Definitions
1857 * function-definition:
1858 * declaration-specifiers declarator declaration-list (opt) compound-statement
1862 * declaration-list declaration
1865 if (first
&& // first declarator
1867 dt.isTypeFunction() && // function type not inherited from a typedef
1868 isDeclarationList(t
) && // optional declaration-list
1869 level
== LVL
.global
&& // function definitions only at global scope
1870 t
.value
== TOK
.leftCurly
) // start of compound-statement
1872 auto s
= cparseFunctionDefinition(id
, dt.isTypeFunction(), specifier
);
1873 typedefTab
.setDim(typedefTabLengthSave
);
1874 symbols
= symbolsSave
;
1878 AST
.Dsymbol s
= null;
1879 typedefTab
.setDim(typedefTabLengthSave
);
1880 symbols
= symbolsSave
;
1882 symbols
= new AST
.Dsymbols
; // lazilly create it
1884 if (level
!= LVL
.global
&& !tspec
&& !specifier
.scw
&& !specifier
.mod
)
1885 error("declaration-specifier-seq required");
1886 else if (specifier
.scw
== SCW
.xtypedef
)
1888 if (token
.value
== TOK
.assign
)
1889 error("no initializer for typedef declaration");
1890 if (specifier
.alignExps
)
1891 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1893 bool isalias
= true;
1894 if (auto ts
= dt.isTypeStruct())
1896 if (ts
.sym
.isAnonymous())
1898 // This is a typedef for an anonymous struct-or-union.
1899 // Directly set the ident for the struct-or-union.
1904 else if (auto te
= dt.isTypeEnum())
1906 if (te
.sym
.isAnonymous())
1908 // This is a typedef for an anonymous enum.
1913 else if (auto tt
= dt.isTypeTag())
1915 if (tt
.id || tt
.tok
== TOK
.enum_
)
1918 /* This applies for enums declared as
1919 * typedef enum {A} E;
1923 declareTag(tt
, spec
);
1928 auto ad
= new AST
.AliasDeclaration(token
.loc
, id
, dt);
1929 ad
.adFlags |
= ad
.hidden
; // do not print when generating .di files
1933 insertTypedefToTypedefTab(id
, dt); // remember typedefs
1937 if (auto tt
= dt.isTypeTag())
1939 if (tt
.members
&& (tt
.id || tt
.tok
== TOK
.enum_
))
1942 declareTag(tt
, spec
);
1946 if (level
== LVL
.prototype
)
1947 break; // declared later as Parameter, not VarDeclaration
1949 if (dt.ty
== AST
.Tvoid
)
1950 error("`void` has no value");
1952 AST
.Initializer initializer
;
1953 bool hasInitializer
;
1954 if (token
.value
== TOK
.assign
)
1957 hasInitializer
= true;
1958 initializer
= cparseInitializer();
1960 // declare the symbol
1963 if (isFunctionTypedef(dt))
1966 error("no initializer for function declaration");
1967 if (specifier
.scw
& SCW
.x_Thread_local
)
1968 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1969 StorageClass
stc = specifiersToSTC(level
, specifier
);
1970 stc &= ~STC
.gshared
; // no gshared functions
1971 auto fd
= new AST
.FuncDeclaration(token
.loc
, Loc
.initial
, id
, stc, dt, specifier
.noreturn
);
1972 specifiersToFuncDeclaration(fd
, specifier
);
1977 // Give non-extern variables an implicit void initializer
1978 // if one has not been explicitly set.
1979 if (!hasInitializer
&&
1980 !(specifier
.scw
& (SCW
.xextern | SCW
.xstatic | SCW
.x_Thread_local
) || level
== LVL
.global
))
1981 initializer
= new AST
.VoidInitializer(token
.loc
);
1982 auto vd
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(level
, specifier
));
1983 specifiersToVarDeclaration(vd
, specifier
);
1986 if (level
!= LVL
.global
)
1987 insertIdToTypedefTab(id
); // non-typedef declarations can hide typedefs in outer scopes
1991 // Saw `asm("name")` in the function, type, or variable definition.
1992 // This is equivalent to `pragma(mangle, "name")` in D
1996 https://issues.dlang.org/show_bug.cgi?id=23012
1997 Ideally this would be translated to a pragma(mangle)
1998 decl. This is not possible because ImportC symbols are
1999 (currently) merged before semantic analysis is performed,
2000 so the pragma(mangle) never effects any change on the declarations
2003 Writing to mangleOverride directly avoids this, and is possible
2004 because C only a StringExp is allowed unlike a full fat pragma(mangle)
2005 which is more liberal.
2007 if (auto p
= s
.isDeclaration())
2009 auto str = asmName
.peekString();
2010 p
.mangleOverride
= str;
2011 // p.adFlags |= AST.VarDeclaration.nounderscore;
2012 p
.adFlags |
= 4; // cannot get above line to compile on Ubuntu
2015 s
= applySpecifier(s
, specifier
);
2016 if (level
== LVL
.local
)
2018 // Wrap the declaration in `extern (C) { declaration }`
2019 // Necessary for function pointers, but harmless to apply to all.
2020 auto decls
= new AST
.Dsymbols(1);
2022 s
= new AST
.LinkDeclaration(s
.loc
, linkage
, decls
);
2026 if (level
== LVL
.global
&& !id
)
2027 error("expected identifier for declaration");
2031 switch (token
.value
)
2033 case TOK
.identifier
:
2036 error(token
.loc
, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s
.toChars(), token
.toChars());
2047 symbolsSave
= symbols
;
2052 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token
.toChars());
2054 return scanPastSemicolon();
2059 /***************************************
2060 * C11 Function Definitions
2061 * function-definition
2062 * declaration-specifiers declarator declaration-list (opt) compound-statement
2066 * declaration-list declaration
2068 * It's already been parsed up to the declaration-list (opt).
2069 * Pick it up from there.
2071 * id = function identifier
2072 * ft = function type
2073 * specifier = function specifiers
2075 * Dsymbol for the function
2077 AST
.Dsymbol
cparseFunctionDefinition(Identifier id
, AST
.TypeFunction ft
, ref Specifier specifier
)
2079 /* Start function scope
2081 typedefTab
.push(null);
2083 if (token
.value
!= TOK
.leftCurly
) // if not start of a compound-statement
2085 // Do declaration-list
2088 cparseDeclaration(LVL
.parameter
);
2089 } while (token
.value
!= TOK
.leftCurly
);
2091 /* Since there were declarations, the parameter-list must have been
2092 * an identifier-list.
2094 ft
.parameterList
.hasIdentifierList
= true; // semantic needs to know to adjust parameter types
2095 auto pl
= ft
.parameterList
;
2096 if (pl
.varargs
!= AST
.VarArg
.none
&& pl
.length
)
2097 error("function identifier-list cannot end with `...`");
2098 ft
.parameterList
.varargs
= AST
.VarArg
.KRvariadic
; // but C11 allows extra arguments
2099 auto plLength
= pl
.length
;
2100 if (symbols
.length
!= plLength
)
2101 error(token
.loc
, "%d identifiers does not match %d declarations", cast(int)plLength
, cast(int)symbols
.length
);
2103 /* Transfer the types and storage classes from symbols[] to pl[]
2105 foreach (i
; 0 .. plLength
)
2107 auto p
= pl
[i
]; // yes, quadratic
2109 // Convert typedef-identifier to identifier
2112 if (auto t
= p
.type
.isTypeIdentifier())
2119 if (p
.type ||
!(p
.storageClass
& STC
.parameter
))
2120 error("storage class and type are not allowed in identifier-list");
2121 foreach (s
; (*symbols
)[]) // yes, quadratic
2123 auto ad
= s
.isAttribDeclaration();
2125 s
= (*ad
.decl
)[0]; // AlignDeclaration wrapping the declaration
2127 auto d
= s
.isDeclaration();
2128 if (d
&& p
.ident
== d
.ident
&& d
.type
)
2131 p
.storageClass
= d
.storage_class
;
2132 d
.type
= null; // don't reuse
2138 error("no declaration for identifier `%s`", p
.ident
.toChars());
2139 p
.type
= AST
.Type
.terror
;
2144 addFuncName
= false; // gets set to true if somebody references __func__ in this function
2145 const locFunc
= token
.loc
;
2147 auto body = cparseStatement(ParseStatementFlags
.curly
); // don't start a new scope; continue with parameter scope
2148 typedefTab
.pop(); // end of function scope
2150 StorageClass
stc = specifiersToSTC(LVL
.global
, specifier
);
2151 stc &= ~STC
.gshared
; // no gshared functions
2152 auto fd
= new AST
.FuncDeclaration(locFunc
, prevloc
, id
, stc, ft
, specifier
.noreturn
);
2153 specifiersToFuncDeclaration(fd
, specifier
);
2157 auto s
= createFuncName(locFunc
, id
);
2158 body = new AST
.CompoundStatement(locFunc
, s
, body);
2162 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2167 /***************************************
2168 * C11 Initialization
2170 * assignment-expression
2171 * { } // C23 6.7.10 addition
2172 * { initializer-list }
2173 * { initializer-list , }
2176 * designation (opt) initializer
2177 * initializer-list , designation (opt) initializer
2184 * designator-list designator
2187 * [ constant-expression ]
2192 AST
.Initializer
cparseInitializer()
2194 if (token
.value
!= TOK
.leftCurly
)
2196 auto ae
= cparseAssignExp(); // assignment-expression
2197 return new AST
.ExpInitializer(token
.loc
, ae
);
2200 const loc
= token
.loc
;
2202 if (token
.value
== TOK
.rightCurly
) // { }
2205 return new AST
.DefaultInitializer(loc
);
2208 /* Collect one or more `designation (opt) initializer`
2209 * into ci.initializerList, but lazily create ci
2211 AST
.CInitializer ci
;
2214 /* There can be 0 or more designators preceding an initializer.
2215 * Collect them in desigInit
2217 AST
.DesigInit desigInit
;
2220 if (token
.value
== TOK
.leftBracket
) // [ constant-expression ]
2223 auto e
= cparseConstantExp();
2224 check(TOK
.rightBracket
);
2225 if (!desigInit
.designatorList
)
2226 desigInit
.designatorList
= new AST
.Designators
;
2227 desigInit
.designatorList
.push(AST
.Designator(e
));
2229 else if (token
.value
== TOK
.dot
) // . identifier
2232 if (token
.value
!= TOK
.identifier
)
2234 error("identifier expected following `.` designator");
2237 if (!desigInit
.designatorList
)
2238 desigInit
.designatorList
= new AST
.Designators
;
2239 desigInit
.designatorList
.push(AST
.Designator(token
.ident
));
2244 if (desigInit
.designatorList
)
2250 desigInit
.initializer
= cparseInitializer();
2252 ci
= new AST
.CInitializer(loc
);
2253 ci
.initializerList
.push(desigInit
);
2254 if (token
.value
== TOK
.comma
)
2257 if (token
.value
!= TOK
.rightCurly
)
2262 check(TOK
.rightCurly
);
2263 //printf("ci: %s\n", ci.toChars());
2267 /*************************************
2269 * declaration-specifier:
2270 * storage-class-specifier declaration-specifiers (opt)
2271 * type-specifier declaration-specifiers (opt)
2272 * type-qualifier declaration-specifiers (opt)
2273 * function-specifier declaration-specifiers (opt)
2274 * alignment-specifier declaration-specifiers (opt)
2276 * level = declaration context
2277 * specifier = specifiers in and out
2279 * resulting type, null if not specified
2281 private AST
.Type
cparseDeclarationSpecifiers(LVL level
, ref Specifier specifier
)
2300 ximaginary
= 0x8000,
2308 //printf("parseDeclarationSpecifiers()\n");
2311 SCW scw
= specifier
.scw
& SCW
.xtypedef
;
2319 //printf("token %s\n", token.toChars());
2323 switch (token
.value
)
2325 // Storage class specifiers
2326 case TOK
.static_
: scwx
= SCW
.xstatic
; break;
2327 case TOK
.extern_
: scwx
= SCW
.xextern
; break;
2328 case TOK
.auto_
: scwx
= SCW
.xauto
; break;
2329 case TOK
.register
: scwx
= SCW
.xregister
; break;
2330 case TOK
.typedef_
: scwx
= SCW
.xtypedef
; break;
2331 case TOK
.inline
: scwx
= SCW
.xinline
; break;
2332 case TOK
._Noreturn
: scwx
= SCW
.x_Noreturn
; break;
2334 case TOK
._Thread_local
: scwx
= SCW
.x_Thread_local
; break;
2337 case TOK
.const_
: modx
= MOD
.xconst
; break;
2338 case TOK
.volatile: modx
= MOD
.xvolatile
; break;
2339 case TOK
.restrict
: modx
= MOD
.xrestrict
; break;
2340 case TOK
.__stdcall
: modx
= MOD
.x__stdcall
; break;
2343 case TOK
.char_
: tkwx
= TKW
.xchar
; break;
2344 case TOK
.signed
: tkwx
= TKW
.xsigned
; break;
2345 case TOK
.unsigned
: tkwx
= TKW
.xunsigned
; break;
2346 case TOK
.int16
: tkwx
= TKW
.xshort
; break;
2347 case TOK
.int32
: tkwx
= TKW
.xint
; break;
2348 case TOK
.int64
: tkwx
= TKW
.xlong
; break;
2349 case TOK
.__int128
: tkwx
= TKW
.xint128
; break;
2350 case TOK
.float32
: tkwx
= TKW
.xfloat
; break;
2351 case TOK
.float64
: tkwx
= TKW
.xdouble
; break;
2352 case TOK
.void_
: tkwx
= TKW
.xvoid
; break;
2353 case TOK
._Bool
: tkwx
= TKW
.xbool
; break;
2354 case TOK
._Imaginary
: tkwx
= TKW
.ximaginary
; break;
2355 case TOK
._Complex
: tkwx
= TKW
.xcomplex
; break;
2357 case TOK
.identifier
:
2365 const structOrUnion
= token
.value
;
2366 const sloc
= token
.loc
;
2369 Specifier tagSpecifier
;
2372 * struct-or-union-specifier:
2373 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2374 * struct-or-union gnu-attribute (opt) identifier
2378 if (token
.value
== TOK
.__attribute__
)
2379 cparseGnuAttributes(tagSpecifier
);
2380 else if (token
.value
== TOK
.__declspec
)
2381 cparseDeclspec(tagSpecifier
);
2382 else if (token
.value
== TOK
.__pragma
)
2383 uupragmaDirective(sloc
);
2387 t
= cparseStruct(sloc
, structOrUnion
, tagSpecifier
.packalign
, symbols
);
2393 t
= cparseEnum(symbols
);
2400 // type-specifier if followed by `( type-name )`
2401 auto tk
= peek(&token
);
2402 if (tk
.value
== TOK
.leftParenthesis
)
2405 if (isTypeName(tk
) && tk
.value
== TOK
.rightParenthesis
)
2409 t
= cparseTypeName();
2410 tkwx
= TKW
.x_Atomic
;
2414 // C11 6.7.3 type-qualifier if not
2415 modx
= MOD
.x_Atomic
;
2422 * _Alignas ( type-name )
2423 * _Alignas ( constant-expression )
2426 if (level
& (LVL
.parameter | LVL
.prototype
))
2427 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2430 check(TOK
.leftParenthesis
);
2433 if (isTypeName(tk
)) // _Alignas ( type-name )
2435 auto talign
= cparseTypeName();
2436 /* Convert type to expression: `talign.alignof`
2438 auto e
= new AST
.TypeExp(loc
, talign
);
2439 exp
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
2441 else // _Alignas ( constant-expression )
2443 exp
= cparseConstantExp();
2446 if (!specifier
.alignExps
)
2447 specifier
.alignExps
= new AST
.Expressions(0);
2448 specifier
.alignExps
.push(exp
);
2450 check(TOK
.rightParenthesis
);
2454 case TOK
.__attribute__
:
2457 * declaration-specifiers:
2458 * gnu-attributes declaration-specifiers (opt)
2460 cparseGnuAttributes(specifier
);
2464 case TOK
.__declspec
:
2466 /* Microsoft extension
2468 cparseDeclspec(specifier
);
2475 check(TOK
.leftParenthesis
);
2480 e
= new AST
.TypeExp(loc
, cparseTypeName());
2482 e
= cparseExpression();
2483 t
= new AST
.TypeTypeof(loc
, e
);
2485 if(token
.value
== TOK
.rightParenthesis
)
2489 t
= AST
.Type
.terror
;
2490 error("`typeof` operator expects an expression or type name in parentheses");
2492 // skipParens et. al expect to be on the opening parenthesis
2498 case TOK
.leftParenthesis
:
2501 case TOK
.rightParenthesis
:
2524 if (tkw
& TKW
.xlong
&& tkwx
& TKW
.xlong
)
2529 if (tkw
&& tkwx
& TKW
.xident
)
2531 // 2nd identifier can't be a typedef
2532 break Lwhile
; // leave parser on the identifier for the following declarator
2534 else if (tkwx
& TKW
.xident
)
2536 // 1st identifier, save it for TypeIdentifier
2539 if (tkw
& TKW
.xident
&& tkwx ||
// typedef-name followed by type-specifier
2540 tkw
& tkwx
) // duplicate type-specifiers
2542 error("illegal combination of type specifiers");
2546 if (!(tkwx
& TKW
.xtag
)) // if parser already advanced
2561 error("duplicate storage class");
2563 // C11 6.7.1-2 At most one storage-class may be given, except that
2564 // _Thread_local may appear with static or extern.
2565 const scw2
= scw
& (SCW
.xstatic | SCW
.xextern | SCW
.xauto | SCW
.xregister | SCW
.xtypedef
);
2566 if (scw2
& (scw2
- 1) ||
2567 scw
& (SCW
.x_Thread_local
) && scw
& (SCW
.xauto | SCW
.xregister | SCW
.xtypedef
))
2569 error("multiple storage classes in declaration specifiers");
2572 if (level
== LVL
.local
&&
2573 scw
& (SCW
.x_Thread_local
) && scw
& (SCW
.xinline | SCW
.x_Noreturn
))
2575 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2578 if (level
== LVL
.local
&&
2579 scw
& (SCW
.x_Thread_local
) && !(scw
& (SCW
.xstatic | SCW
.xextern
)))
2581 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2584 if (level
& (LVL
.parameter | LVL
.prototype
) &&
2585 scw
& ~SCW
.xregister
)
2587 error("only `register` storage class allowed for function parameters");
2590 if (level
== LVL
.global
&&
2591 scw
& (SCW
.xauto | SCW
.xregister
))
2593 error("`auto` and `register` storage class not allowed for global");
2601 specifier
.scw
= scw
;
2602 specifier
.mod
= mod
;
2604 // Convert TKW bits to type t
2607 case TKW
.xnone
: t
= null; break;
2609 case TKW
.xchar
: t
= AST
.Type
.tchar
; break;
2610 case TKW
.xsigned | TKW
.xchar
: t
= AST
.Type
.tint8
; break;
2611 case TKW
.xunsigned | TKW
.xchar
: t
= AST
.Type
.tuns8
; break;
2614 case TKW
.xsigned | TKW
.xshort
:
2615 case TKW
.xsigned | TKW
.xshort | TKW
.xint
:
2616 case TKW
.xshort | TKW
.xint
: t
= integerTypeForSize(shortsize
); break;
2618 case TKW
.xunsigned | TKW
.xshort | TKW
.xint
:
2619 case TKW
.xunsigned | TKW
.xshort
: t
= unsignedTypeForSize(shortsize
); break;
2623 case TKW
.xsigned | TKW
.xint
: t
= integerTypeForSize(intsize
); break;
2626 case TKW
.xunsigned | TKW
.xint
: t
= unsignedTypeForSize(intsize
); break;
2629 case TKW
.xsigned | TKW
.xlong
:
2630 case TKW
.xsigned | TKW
.xlong | TKW
.xint
:
2631 case TKW
.xlong | TKW
.xint
: t
= integerTypeForSize(longsize
); break;
2633 case TKW
.xunsigned | TKW
.xlong | TKW
.xint
:
2634 case TKW
.xunsigned | TKW
.xlong
: t
= unsignedTypeForSize(longsize
); break;
2637 case TKW
.xsigned | TKW
.xllong
:
2638 case TKW
.xsigned | TKW
.xllong | TKW
.xint
:
2639 case TKW
.xllong | TKW
.xint
: t
= integerTypeForSize(long_longsize
); break;
2641 case TKW
.xunsigned | TKW
.xllong | TKW
.xint
:
2642 case TKW
.xunsigned | TKW
.xllong
: t
= unsignedTypeForSize(long_longsize
); break;
2645 case TKW
.xsigned | TKW
.xint128
: t
= integerTypeForSize(16); break;
2647 case TKW
.xunsigned | TKW
.xint128
: t
= unsignedTypeForSize(16); break;
2649 case TKW
.xvoid
: t
= AST
.Type
.tvoid
; break;
2650 case TKW
.xbool
: t
= boolsize
== 1 ? AST
.Type
.tbool
: integerTypeForSize(boolsize
); break;
2652 case TKW
.xfloat
: t
= AST
.Type
.tfloat32
; break;
2653 case TKW
.xdouble
: t
= AST
.Type
.tfloat64
; break;
2654 case TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.realfloat
); break;
2656 case TKW
.ximaginary | TKW
.xfloat
: t
= AST
.Type
.timaginary32
; break;
2657 case TKW
.ximaginary | TKW
.xdouble
: t
= AST
.Type
.timaginary64
; break;
2658 case TKW
.ximaginary | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.imaginary
); break;
2660 case TKW
.xcomplex | TKW
.xfloat
: t
= AST
.Type
.tcomplex32
; break;
2661 case TKW
.xcomplex | TKW
.xdouble
: t
= AST
.Type
.tcomplex64
; break;
2662 case TKW
.xcomplex | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.complex
); break;
2666 const idx
= previd
.toString();
2667 if (idx
.length
> 2 && idx
[0] == '_' && idx
[1] == '_') // leading double underscore
2668 importBuiltins
= true; // probably one of those compiler extensions
2671 /* Punch through to what the typedef is, to support things like:
2674 auto pt
= lookupTypedef(previd
);
2675 if (pt
&& *pt
) // if previd is a known typedef
2679 t
= new AST
.TypeIdentifier(loc
, previd
);
2684 case TKW
.x_Atomic
: // no atomics for you
2685 break; // t is already set
2688 error("illegal type combination");
2689 t
= AST
.Type
.terror
;
2696 /********************************
2698 * Parse a declarator (including function definitions).
2700 * pointer (opt) direct-declarator
2702 * direct-declarator :
2705 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2706 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2707 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2708 * direct-declarator [ type-qualifier-list (opt) * ]
2709 * direct-declarator ( parameter-type-list )
2710 * direct-declarator ( identifier-list (opt) )
2713 * * type-qualifier-list (opt)
2714 * * type-qualifier-list (opt) pointer
2716 * type-qualifier-list :
2718 * type-qualifier-list type-qualifier
2720 * parameter-type-list :
2722 * parameter-list , ...
2725 * parameter-declaration
2726 * parameter-list , parameter-declaration
2728 * parameter-declaration :
2729 * declaration-specifiers declarator
2730 * declaration-specifiers abstract-declarator (opt)
2734 * identifier-list , identifier
2737 * declarator = declarator kind
2738 * tbase = base type to start with
2739 * pident = set to Identifier if there is one, null if not
2740 * specifier = specifiers in and out
2742 * type declared. If a TypeFunction is returned, this.symbols is the
2743 * symbol table for the parameter-type-list, which will contain any
2744 * declared struct, union or enum tags.
2746 private AST
.Type
cparseDeclarator(DTR declarator
, AST
.Type tbase
,
2747 out Identifier pident
, ref Specifier specifier
)
2749 //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
2750 AST
.Types constTypes
; // all the Types that will need `const` applied to them
2752 /* Insert tx -> t into
2755 * ts -> ... -> tx -> t
2757 static void insertTx(ref AST
.Type ts
, AST
.Type tx
, AST
.Type t
)
2760 for (pt
= &ts
; *pt
!= t
; pt
= &(cast(AST
.TypeNext
)*pt
).next
)
2766 AST
.Type
parseDecl(AST
.Type t
)
2768 //printf("parseDecl() t: %s\n", t.toChars());
2772 switch (token
.value
)
2774 case TOK
.identifier
: // identifier
2775 //printf("identifier %s\n", token.ident.toChars());
2776 if (declarator
== DTR
.xabstract
)
2777 error("identifier not allowed in abstract-declarator");
2778 pident
= token
.ident
;
2783 case TOK
.leftParenthesis
: // ( declarator )
2784 //printf("leftParen\n");
2789 if (!isCDeclarator(tk
, declarator
))
2791 /* Not ( declarator ), might be parameter-list
2798 if (token
.value
== TOK
.__stdcall
) // T (__stdcall*fp)();
2800 specifier
.mod |
= MOD
.x__stdcall
;
2805 check(TOK
.rightParenthesis
);
2808 case TOK
.mul: // pointer
2810 t
= new AST
.TypePointer(t
);
2812 // add post fixes const/volatile/restrict/_Atomic
2813 const mod
= cparseTypeQualifierList();
2814 if (mod
& MOD
.xconst
)
2816 if (token
.value
== TOK
.__attribute__
)
2817 cparseGnuAttributes(specifier
);
2821 //printf("default %s\n", token.toChars());
2822 if (declarator
== DTR
.xdirect
)
2824 if (!t || t
.isTypeIdentifier())
2827 error("no type-specifier for declarator");
2828 t
= AST
.Type
.tint32
;
2831 error("identifier or `(` expected"); // )
2840 // parse DeclaratorSuffixes
2843 switch (token
.value
)
2845 case TOK
.leftBracket
:
2847 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2851 auto mod
= cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2855 if (token
.value
== TOK
.static_
)
2857 isStatic
= true; // `static`
2859 if (!mod
) // type qualifiers after `static`
2860 mod
= cparseTypeQualifierList();
2862 else if (token
.value
== TOK
.mul)
2864 if (peekNext() == TOK
.rightBracket
)
2866 isVLA
= true; // `*`
2871 if (isStatic || token
.value
!= TOK
.rightBracket
)
2873 //printf("It's a static array\n");
2874 AST
.Expression e
= cparseAssignExp(); // [ expression ]
2875 ta
= new AST
.TypeSArray(t
, e
);
2879 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2881 ta
= new AST
.TypeSArray(t
);
2883 check(TOK
.rightBracket
);
2885 // Issue errors for unsupported types.
2886 if (isVLA
) // C11 6.7.6.2
2888 error("variable length arrays are not supported");
2890 if (isStatic
) // C11 6.7.6.3
2892 error("static array parameters are not supported");
2894 if (declarator
!= DTR
.xparameter
)
2896 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2899 error("variable length array used outside of function prototype");
2900 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2901 * in a declaration of a function parameter with an array type.
2903 if (isStatic || mod
)
2904 error("static or type qualifier used outside of function prototype");
2906 if (ts
.isTypeSArray() || ts
.isTypeDArray())
2908 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2909 * in the outermost array type derivation.
2911 if (isStatic || mod
)
2912 error("static or type qualifier used in non-outermost array type derivation");
2913 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2916 if (ta
.isTypeSArray() && ta
.isTypeSArray().isIncomplete() && !isVLA
)
2917 error("array type has incomplete element type `%s`", ta
.toChars());
2920 // Apply type qualifiers to the constructed type.
2921 if (mod
& MOD
.xconst
) // ignore the other bits
2923 insertTx(ts
, ta
, t
); // ts -> ... -> ta -> t
2927 case TOK
.leftParenthesis
:
2929 // New symbol table for parameter-list
2930 auto symbolsSave
= this.symbols
;
2931 this.symbols
= null;
2933 auto parameterList
= cparseParameterList();
2934 const lkg
= specifier
.mod
& MOD
.x__stdcall ? LINK
.windows
: linkage
;
2935 StorageClass
stc = specifier
._nothrow ? STC
.nothrow_
: 0;
2936 if (specifier
._pure
)
2938 AST
.Type tf
= new AST
.TypeFunction(parameterList
, t
, lkg
, stc);
2939 //tf = tf.addSTC(storageClass); // TODO
2940 insertTx(ts
, tf
, t
); // ts -> ... -> tf -> t
2943 this.symbols
= symbolsSave
;
2952 if (declarator
== DTR
.xdirect
&& !pident
)
2953 error("expected identifier for declarator");
2957 auto t
= parseDecl(tbase
);
2959 if (specifier
.vector_size
)
2961 auto length
= new AST
.IntegerExp(token
.loc
, specifier
.vector_size
/ tbase
.size(), AST
.Type
.tuns32
);
2962 auto tsa
= new AST
.TypeSArray(tbase
, length
);
2963 AST
.Type tv
= new AST
.TypeVector(tsa
);
2964 specifier
.vector_size
= 0; // used it up
2966 insertTx(t
, tv
, tbase
); // replace tbase with tv
2969 /* Because const is transitive, cannot assemble types from
2970 * fragments. Instead, types to be annotated with const are put
2971 * in constTypes[], and a bottom up scan of t is done to apply
2974 if (constTypes
.length
)
2976 AST
.Type
constApply(AST
.Type t
)
2980 auto tn
= cast(AST
.TypeNext
)t
; // t.nextOf() should return a ref instead of this
2981 tn
.next
= constApply(tn
.next
);
2983 foreach (tc
; constTypes
[])
2993 if (declarator
== DTR
.xparameter
&&
2996 /* Because there are instances in .h files of "const pointer to mutable",
2997 * skip applying transitive `const`
2998 * https://issues.dlang.org/show_bug.cgi?id=22534
3000 auto tn
= cast(AST
.TypeNext
)t
;
3001 tn
.next
= constApply(tn
.next
);
3007 //printf("result: %s\n", t.toChars());
3011 /******************************
3020 MOD
cparseTypeQualifierList()
3025 switch (token
.value
)
3027 case TOK
.const_
: mod |
= MOD
.xconst
; break;
3028 case TOK
.volatile: mod |
= MOD
.xvolatile
; break;
3029 case TOK
.restrict
: mod |
= MOD
.xrestrict
; break;
3030 case TOK
._Atomic
: mod |
= MOD
.x_Atomic
; break;
3031 case TOK
.__stdcall
: mod |
= MOD
.x__stdcall
; break;
3040 /***********************************
3043 AST
.Type
cparseTypeName()
3045 Specifier specifier
;
3046 specifier
.packalign
.setDefault();
3047 auto tspec
= cparseSpecifierQualifierList(LVL
.global
, specifier
);
3050 error("type-specifier is missing");
3051 tspec
= AST
.Type
.tint32
;
3053 if (tspec
&& specifier
.mod
& MOD
.xconst
)
3055 tspec
= toConst(tspec
);
3056 specifier
.mod
= MOD
.xnone
; // 'used' it
3059 return cparseDeclarator(DTR
.xabstract
, tspec
, id
, specifier
);
3062 /***********************************
3064 * specifier-qualifier-list:
3065 * type-specifier specifier-qualifier-list (opt)
3066 * type-qualifier specifier-qualifier-list (opt)
3068 * level = declaration context
3069 * specifier = specifiers in and out
3071 * resulting type, null if not specified
3073 AST
.Type
cparseSpecifierQualifierList(LVL level
, ref Specifier specifier
)
3075 auto t
= cparseDeclarationSpecifiers(level
, specifier
);
3077 error("storage class not allowed in specifier-qualified-list");
3081 /***********************************
3083 * ( parameter-type-list )
3084 * ( identifier-list (opt) )
3086 AST
.ParameterList
cparseParameterList()
3088 auto parameters
= new AST
.Parameters();
3089 AST
.VarArg varargs
= AST
.VarArg
.none
;
3090 StorageClass varargsStc
;
3092 check(TOK
.leftParenthesis
);
3093 if (token
.value
== TOK
.void_
&& peekNext() == TOK
.rightParenthesis
) // func(void)
3097 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3100 if (token
.value
== TOK
.rightParenthesis
) // func()
3103 return AST
.ParameterList(parameters
, AST
.VarArg
.KRvariadic
, varargsStc
);
3106 /* Create function prototype scope
3108 typedefTab
.push(null);
3110 AST
.ParameterList
finish()
3113 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
3116 /* The check for identifier-list comes later,
3117 * when doing the trailing declaration-list (opt)
3121 if (token
.value
== TOK
.rightParenthesis
)
3123 if (token
.value
== TOK
.dotDotDot
)
3125 if (parameters
.length
== 0) // func(...)
3126 error("named parameter required before `...`");
3127 importBuiltins
= true; // will need __va_list_tag
3128 varargs
= AST
.VarArg
.variadic
; // C-style variadics
3130 check(TOK
.rightParenthesis
);
3134 Specifier specifier
;
3135 specifier
.packalign
.setDefault();
3136 auto tspec
= cparseDeclarationSpecifiers(LVL
.prototype
, specifier
);
3139 error("no type-specifier for parameter");
3140 tspec
= AST
.Type
.tint32
;
3143 if (specifier
.mod
& MOD
.xconst
)
3145 if ((token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.comma
) &&
3146 tspec
.isTypeIdentifier())
3147 error("type-specifier omitted for parameter `%s`", tspec
.isTypeIdentifier().ident
.toChars());
3149 tspec
= toConst(tspec
);
3150 specifier
.mod
= MOD
.xnone
; // 'used' it
3154 const paramLoc
= token
.loc
;
3155 auto t
= cparseDeclarator(DTR
.xparameter
, tspec
, id
, specifier
);
3156 if (token
.value
== TOK
.__attribute__
)
3157 cparseGnuAttributes(specifier
);
3158 if (specifier
.mod
& MOD
.xconst
)
3160 auto param
= new AST
.Parameter(paramLoc
, specifiersToSTC(LVL
.parameter
, specifier
),
3162 parameters
.push(param
);
3163 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
3167 check(TOK
.rightParenthesis
);
3171 /***********************************
3173 * _Static_assert ( constant-expression , string-literal ) ;
3175 private AST
.StaticAssert
cparseStaticAssert()
3177 const loc
= token
.loc
;
3179 //printf("cparseStaticAssert()\n");
3181 check(TOK
.leftParenthesis
);
3182 auto exp
= cparseConstantExp();
3184 if (token
.value
!= TOK
.string_
)
3185 error("string literal expected");
3186 auto msg
= cparsePrimaryExp();
3187 check(TOK
.rightParenthesis
);
3188 check(TOK
.semicolon
);
3189 return new AST
.StaticAssert(loc
, exp
, msg
);
3192 /*************************
3193 * Collect argument list.
3194 * Parser is on opening parenthesis.
3198 private AST
.Expressions
* cparseArguments()
3201 auto arguments
= new AST
.Expressions();
3202 while (token
.value
!= TOK
.rightParenthesis
&& token
.value
!= TOK
.endOfFile
)
3204 auto arg
= cparseAssignExp();
3205 arguments
.push(arg
);
3206 if (token
.value
!= TOK
.comma
)
3209 nextToken(); // consume comma
3212 check(TOK
.rightParenthesis
);
3217 /*************************
3219 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3221 * __declspec ( extended-decl-modifier-seq )
3223 * extended-decl-modifier-seq:
3224 * extended-decl-modifier (opt)
3225 * extended-decl-modifier extended-decl-modifier-seq
3227 * extended-decl-modifier:
3229 * deprecated(depMsg)
3238 * specifier = filled in with the attribute(s)
3240 private void cparseDeclspec(ref Specifier specifier
)
3242 //printf("cparseDeclspec()\n");
3243 /* Check for dllexport, dllimport
3246 nextToken(); // move past __declspec
3247 check(TOK
.leftParenthesis
);
3250 if (token
.value
== TOK
.rightParenthesis
)
3255 else if (token
.value
== TOK
.endOfFile
)
3257 else if (token
.value
== TOK
.identifier
)
3259 if (token
.ident
== Id
.dllimport
)
3261 specifier
.dllimport
= true;
3264 else if (token
.ident
== Id
.dllexport
)
3266 specifier
.dllexport
= true;
3269 else if (token
.ident
== Id
.naked
)
3271 specifier
.naked
= true;
3274 else if (token
.ident
== Id
.noinline
)
3276 specifier
.scw |
= SCW
.xnoinline
;
3279 else if (token
.ident
== Id
.noreturn
)
3281 specifier
.noreturn
= true;
3284 else if (token
.ident
== Id
._nothrow
)
3286 specifier
._nothrow
= true;
3289 else if (token
.ident
== Id
.thread
)
3291 specifier
.scw |
= SCW
.x_Thread_local
;
3294 else if (token
.ident
== Id
._align
)
3296 // Microsoft spec is very imprecise as to how this actually works
3298 check(TOK
.leftParenthesis
);
3299 if (token
.value
== TOK
.int32Literal
)
3301 const n
= token
.unsvalue
;
3302 if (n
< 1 || n
& (n
- 1) ||
8192 < n
)
3303 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n
);
3304 specifier
.packalign
.set(cast(uint)n
);
3305 specifier
.packalign
.setPack(true);
3310 error("alignment value expected, not `%s`", token
.toChars());
3314 check(TOK
.rightParenthesis
);
3316 else if (token
.ident
== Id
._deprecated
)
3318 specifier
._deprecated
= true;
3320 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3323 specifier
.depMsg
= cparseExpression();
3324 check(TOK
.rightParenthesis
);
3330 if (token
.value
== TOK
.leftParenthesis
)
3334 else if (token
.value
== TOK
.restrict
) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3338 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token
.toChars());
3340 if (token
.value
!= TOK
.rightParenthesis
)
3346 /*************************
3347 * Parser for asm label. It appears after the declarator, and has apparently
3348 * nothing to do with inline assembler.
3349 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3351 * asm ( asm-string-literal )
3353 * asm-string-literal:
3356 private AST
.StringExp
cparseGnuAsmLabel()
3358 nextToken(); // move past asm
3359 check(TOK
.leftParenthesis
);
3360 if (token
.value
!= TOK
.string_
)
3361 error("string literal expected for Asm Label, not `%s`", token
.toChars());
3362 auto label
= cparsePrimaryExp();
3363 check(TOK
.rightParenthesis
);
3364 return cast(AST
.StringExp
) label
;
3367 /********************
3368 * Parse C inline assembler statement in Gnu format.
3369 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3370 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3371 * Current token is on the `asm`.
3373 * inline assembler expression as a Statement
3375 private AST
.Statement
cparseGnuAsm()
3377 // Defer parsing of AsmStatements until semantic processing.
3378 const loc
= token
.loc
;
3382 // Consume all asm-qualifiers. As a future optimization, we could record
3383 // the `inline` and `volatile` storage classes against the statement.
3384 while (token
.value
== TOK
.goto_ ||
3385 token
.value
== TOK
.inline ||
3386 token
.value
== TOK
.volatile)
3389 check(TOK
.leftParenthesis
);
3390 if (token
.value
!= TOK
.string_
)
3391 error("string literal expected for Assembler Template, not `%s`", token
.toChars());
3392 Token
* toklist
= null;
3393 Token
** ptoklist
= &toklist
;
3394 //Identifier label = null;
3395 auto statements
= new AST
.Statements();
3400 switch (token
.value
)
3402 case TOK
.leftParenthesis
:
3406 case TOK
.rightParenthesis
:
3413 error("matching `)` expected, not `;`");
3418 error("matching `)` expected, not end of file");
3421 case TOK
.colonColon
: // treat as two separate : tokens for iasmgcc
3422 *ptoklist
= allocateToken();
3423 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3424 (*ptoklist
).value
= TOK
.colon
;
3425 ptoklist
= &(*ptoklist
).next
;
3427 *ptoklist
= allocateToken();
3428 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3429 (*ptoklist
).value
= TOK
.colon
;
3430 ptoklist
= &(*ptoklist
).next
;
3437 *ptoklist
= allocateToken();
3438 memcpy(*ptoklist
, &token
, Token
.sizeof
);
3439 ptoklist
= &(*ptoklist
).next
;
3446 // Create AsmStatement from list of tokens we've saved
3447 AST
.Statement s
= new AST
.AsmStatement(token
.loc
, toklist
);
3453 auto s
= new AST
.CompoundAsmStatement(loc
, statements
, 0);
3457 /*************************
3458 * __attribute__ parser
3459 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3461 * gnu-attributes gnu-attribute-specifier
3463 * gnu-attribute-specifier:
3464 * __attribute__ (( gnu-attribute-list ))
3466 * gnu-attribute-list:
3467 * gnu-attribute (opt)
3468 * gnu-attribute-list , gnu-attribute
3471 * specifier = filled in with the attribute(s)
3473 private void cparseGnuAttributes(ref Specifier specifier
)
3475 while (token
.value
== TOK
.__attribute__
)
3477 nextToken(); // move past __attribute__
3478 check(TOK
.leftParenthesis
);
3479 check(TOK
.leftParenthesis
);
3481 if (token
.value
!= TOK
.rightParenthesis
)
3485 cparseGnuAttribute(specifier
);
3486 if (token
.value
!= TOK
.comma
)
3492 check(TOK
.rightParenthesis
);
3493 check(TOK
.rightParenthesis
);
3497 /*************************
3498 * Parse a single GNU attribute
3500 * gnu-attribute-name
3501 * gnu-attribute-name ( identifier )
3502 * gnu-attribute-name ( identifier , expression-list )
3503 * gnu-attribute-name ( expression-list (opt) )
3505 * gnu-attribute-name:
3510 * constant-expression
3511 * expression-list , constant-expression
3514 * specifier = filled in with the attribute(s)
3516 private void cparseGnuAttribute(ref Specifier specifier
)
3518 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3521 if (!isGnuAttributeName())
3524 if (token
.value
== TOK
.identifier
)
3526 if (token
.ident
== Id
.aligned
)
3529 if (token
.value
== TOK
.leftParenthesis
)
3532 if (token
.value
== TOK
.int32Literal
)
3534 const n
= token
.unsvalue
;
3535 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
3536 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n
);
3537 specifier
.packalign
.set(cast(uint)n
);
3538 specifier
.packalign
.setPack(true);
3543 error("alignment value expected, not `%s`", token
.toChars());
3547 check(TOK
.rightParenthesis
);
3549 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3550 * type on the target machine. It's the opposite of __attribute__((packed))
3553 else if (token
.ident
== Id
.always_inline
) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3555 specifier
.scw |
= SCW
.xinline
;
3558 else if (token
.ident
== Id
._deprecated
)
3560 specifier
._deprecated
= true;
3562 if (token
.value
== TOK
.leftParenthesis
) // optional deprecation message
3565 specifier
.depMsg
= cparseExpression();
3566 check(TOK
.rightParenthesis
);
3569 else if (token
.ident
== Id
.dllimport
)
3571 specifier
.dllimport
= true;
3574 else if (token
.ident
== Id
.dllexport
)
3576 specifier
.dllexport
= true;
3579 else if (token
.ident
== Id
.naked
)
3581 specifier
.naked
= true;
3584 else if (token
.ident
== Id
.noinline
)
3586 specifier
.scw |
= SCW
.xnoinline
;
3589 else if (token
.ident
== Id
.noreturn
)
3591 specifier
.noreturn
= true;
3594 else if (token
.ident
== Id
._nothrow
)
3596 specifier
._nothrow
= true;
3599 else if (token
.ident
== Id
._pure
)
3601 specifier
._pure
= true;
3604 else if (token
.ident
== Id
.vector_size
)
3607 check(TOK
.leftParenthesis
);
3608 if (token
.value
== TOK
.int32Literal
)
3610 const n
= token
.unsvalue
;
3611 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
3612 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n
);
3613 specifier
.vector_size
= cast(uint) n
;
3618 error("value for vector_size expected, not `%s`", token
.toChars());
3621 check(TOK
.rightParenthesis
);
3626 if (token
.value
== TOK
.leftParenthesis
)
3633 if (token
.value
== TOK
.leftParenthesis
)
3638 /*************************
3639 * See if match for GNU attribute name, which may be any identifier,
3640 * storage-class-specifier, type-specifier, or type-qualifier.
3642 * true if a valid GNU attribute name
3644 private bool isGnuAttributeName()
3646 switch (token
.value
)
3648 case TOK
.identifier
:
3664 case TOK
._Thread_local
:
3680 /***************************
3681 * Like skipParens(), but consume the tokens.
3683 private void cparseParens()
3685 check(TOK
.leftParenthesis
);
3690 switch (token
.value
)
3692 case TOK
.leftParenthesis
:
3696 case TOK
.rightParenthesis
:
3700 error("extra right parenthesis");
3711 error("end of file found before right parenthesis");
3722 /******************************************************************************/
3723 /***************************** Struct & Enum Parser ***************************/
3726 /*************************************
3729 * enum identifier (opt) { enumerator-list }
3730 * enum identifier (opt) { enumerator-list , }
3735 * enumerator-list , enumerator
3738 * enumeration-constant
3739 * enumeration-constant = constant-expression
3741 * enumeration-constant:
3745 * symbols = symbols to add enum declaration to
3749 private AST
.Type
cparseEnum(ref AST
.Dsymbols
* symbols
)
3751 const loc
= token
.loc
;
3756 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3757 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3758 * enum gnu-attributes (opt) identifier
3760 Specifier specifier
;
3761 specifier
.packalign
.setDefault();
3762 if (token
.value
== TOK
.__attribute__
)
3763 cparseGnuAttributes(specifier
);
3766 if (token
.value
== TOK
.identifier
)
3772 /* clang extension: add optional base type after the identifier
3773 * https://en.cppreference.com/w/cpp/language/enum
3774 * enum Identifier : Type
3776 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3777 AST
.Type base
= null; // C23 says base type is determined by enum member values
3778 if (token
.value
== TOK
.colon
)
3781 base
= cparseTypeName();
3784 AST
.Dsymbols
* members
;
3785 if (token
.value
== TOK
.leftCurly
)
3788 members
= new AST
.Dsymbols();
3790 if (token
.value
== TOK
.rightCurly
) // C11 6.7.2.2-1
3793 error("no members for `enum %s`", tag
.toChars());
3795 error("no members for anonymous enum");
3798 while (token
.value
== TOK
.identifier
)
3800 auto ident
= token
.ident
; // enumeration-constant
3802 auto mloc
= token
.loc
;
3804 if (token
.value
== TOK
.__attribute__
)
3806 /* gnu-attributes can appear here, but just scan and ignore them
3807 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3809 Specifier specifierx
;
3810 specifierx
.packalign
.setDefault();
3811 cparseGnuAttributes(specifierx
);
3814 AST
.Expression value
;
3815 if (token
.value
== TOK
.assign
)
3818 value
= cparseConstantExp();
3819 // TODO C11 6.7.2.2-2 value must fit into an int
3822 if (token
.value
== TOK
.__attribute__
)
3824 /* gnu-attributes can appear here, but just scan and ignore them
3825 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3827 Specifier specifierx
;
3828 specifierx
.packalign
.setDefault();
3829 cparseGnuAttributes(specifierx
);
3832 auto em
= new AST
.EnumMember(mloc
, ident
, value
, null, 0, null, null);
3835 if (token
.value
== TOK
.comma
)
3842 check(TOK
.rightCurly
);
3845 * Parse the postfix gnu-attributes (opt)
3847 if (token
.value
== TOK
.__attribute__
)
3848 cparseGnuAttributes(specifier
);
3851 error("missing `identifier` after `enum`");
3853 /* Need semantic information to determine if this is a declaration,
3854 * redeclaration, or reference to existing declaration.
3855 * Defer to the semantic() pass with a TypeTag.
3857 return new AST
.TypeTag(loc
, TOK
.enum_
, tag
, structalign_t
.init
, base
, members
);
3860 /*************************************
3862 * Parse struct and union specifiers.
3863 * Parser is advanced to the tag identifier or brace.
3864 * struct-or-union-specifier:
3865 * struct-or-union identifier (opt) { struct-declaration-list }
3866 * struct-or-union identifier
3872 * struct-declaration-list:
3873 * struct-declaration
3874 * struct-declaration-list struct-declaration
3877 * loc = location of `struct` or `union`
3878 * structOrUnion = TOK.struct_ or TOK.union_
3879 * packalign = alignment to use for struct members
3880 * symbols = symbols to add struct-or-union declaration to
3882 * type of the struct
3884 private AST
.Type
cparseStruct(Loc loc
, TOK structOrUnion
, structalign_t packalign
, ref AST
.Dsymbols
* symbols
)
3888 if (token
.value
== TOK
.identifier
)
3894 AST
.Dsymbols
* members
;
3895 if (token
.value
== TOK
.leftCurly
)
3898 members
= new AST
.Dsymbols(); // so `members` will be non-null even with 0 members
3899 while (token
.value
!= TOK
.rightCurly
)
3901 cparseStructDeclaration(members
);
3903 if (token
.value
== TOK
.endOfFile
)
3906 check(TOK
.rightCurly
);
3908 if ((*members
).length
== 0) // C11 6.7.2.1-8
3910 /* allow empty structs as an extension
3911 * struct-declarator-list:
3912 * struct-declarator (opt)
3917 error("missing tag `identifier` after `%s`", Token
.toChars(structOrUnion
));
3919 // many ways and places to declare alignment
3920 if (packalign
.isUnknown() && !this.packalign
.isUnknown())
3921 packalign
.set(this.packalign
.get());
3923 /* Need semantic information to determine if this is a declaration,
3924 * redeclaration, or reference to existing declaration.
3925 * Defer to the semantic() pass with a TypeTag.
3927 return new AST
.TypeTag(loc
, structOrUnion
, tag
, packalign
, null, members
);
3930 /*************************************
3932 * Parse a struct declaration member.
3933 * struct-declaration:
3934 * specifier-qualifier-list struct-declarator-list (opt) ;
3935 * static_assert-declaration
3937 * struct-declarator-list:
3939 * struct-declarator-list , struct-declarator
3941 * struct-declarator:
3943 * declarator (opt) : constant-expression
3945 * members = where to put the fields (members)
3947 void cparseStructDeclaration(AST
.Dsymbols
* members
)
3949 //printf("cparseStructDeclaration()\n");
3950 if (token
.value
== TOK
._Static_assert
)
3952 auto s
= cparseStaticAssert();
3957 Specifier specifier
;
3958 specifier
.packalign
= this.packalign
;
3959 auto tspec
= cparseSpecifierQualifierList(LVL
.member
, specifier
);
3962 error("no type-specifier for struct member");
3963 tspec
= AST
.Type
.tint32
;
3965 if (specifier
.mod
& MOD
.xconst
)
3967 tspec
= toConst(tspec
);
3968 specifier
.mod
= MOD
.xnone
; // 'used' it
3971 /* If a declarator does not follow, it is unnamed
3973 if (token
.value
== TOK
.semicolon
&& tspec
)
3976 auto tt
= tspec
.isTypeTag();
3979 if (auto ti
= tspec
.isTypeIdentifier())
3981 error("type-specifier omitted before declaration of `%s`", ti
.ident
.toChars());
3983 return; // legal but meaningless empty declaration
3986 /* If anonymous struct declaration
3987 * struct { ... members ... };
3990 if (!tt
.id
&& tt
.members
)
3992 /* members of anonymous struct are considered members of
3993 * the containing struct
3995 auto ad
= new AST
.AnonDeclaration(tt
.loc
, tt
.tok
== TOK
.union_
, tt
.members
);
3996 auto s
= applySpecifier(ad
, specifier
);
4000 if (!tt
.id
&& !tt
.members
)
4001 return; // already gave error in cparseStruct()
4003 /* `struct tag;` and `struct tag { ... };`
4004 * always result in a declaration in the current scope
4006 // TODO: merge in specifier
4007 auto stag
= (tt
.tok
== TOK
.struct_
)
4008 ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false)
4009 : new AST
.UnionDeclaration(tt
.loc
, tt
.id
);
4010 stag
.members
= tt
.members
;
4012 symbols
= new AST
.Dsymbols();
4013 auto s
= applySpecifier(stag
, specifier
);
4022 if (token
.value
== TOK
.colon
)
4024 if (auto ti
= tspec
.isTypeIdentifier())
4026 error("type-specifier omitted before bit field declaration of `%s`", ti
.ident
.toChars());
4027 tspec
= AST
.Type
.tint32
;
4030 // C11 6.7.2.1-12 unnamed bit-field
4031 id
= Identifier
.generateAnonymousId("BitField");
4036 dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
4041 break; // error recovery
4045 AST
.Expression width
;
4046 if (token
.value
== TOK
.colon
)
4048 // C11 6.7.2.1-10 bit-field
4050 width
= cparseConstantExp();
4054 * struct-declarator:
4055 * declarator gnu-attributes (opt)
4056 * declarator (opt) : constant-expression gnu-attributes (opt)
4058 if (token
.value
== TOK
.__attribute__
)
4059 cparseGnuAttributes(specifier
);
4061 if (!tspec
&& !specifier
.scw
&& !specifier
.mod
)
4062 error("specifier-qualifier-list required");
4065 if (specifier
.alignExps
)
4066 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4067 auto s
= new AST
.BitFieldDeclaration(width
.loc
, dt, id
, width
);
4072 if (dt.ty
== AST
.Tvoid
)
4073 error("`void` has no value");
4075 // declare the symbol
4076 // Give member variables an implicit void initializer
4077 auto initializer
= new AST
.VoidInitializer(token
.loc
);
4078 AST
.Dsymbol s
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(LVL
.member
, specifier
));
4079 s
= applySpecifier(s
, specifier
);
4083 switch (token
.value
)
4085 case TOK
.identifier
:
4086 error("missing comma");
4098 error("`;` or `,` expected");
4099 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
4108 /******************************************************************************/
4109 /********************************* Lookahead Parser ***************************/
4112 /************************************
4113 * Determine if the scanner is sitting on the start of a declaration.
4115 * t = current token of the scanner
4116 * needId = flag with additional requirements for a declaration
4117 * endtok = ending token
4118 * pt = will be set ending token (if not null)
4120 * true at start of a declaration
4122 private bool isCDeclaration(ref Token
* pt
)
4125 //printf("isCDeclaration() %s\n", t.toChars());
4126 if (!isDeclarationSpecifiers(t
))
4131 if (t
.value
== TOK
.semicolon
)
4137 if (!isCDeclarator(t
, DTR
.xdirect
))
4139 if (t
.value
== TOK
.asm_
)
4142 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4145 if (t
.value
== TOK
.__attribute__
)
4148 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
4151 if (t
.value
== TOK
.assign
)
4154 if (!isInitializer(t
))
4174 /********************************
4175 * See if match for initializer.
4177 * pt = starting token, updated to one past end of initializer if true
4179 * true if initializer
4181 private bool isInitializer(ref Token
* pt
)
4183 //printf("isInitializer()\n");
4186 if (t
.value
== TOK
.leftCurly
)
4194 // skip over assignment-expression, ending before comma or semiColon or EOF
4195 if (!isAssignmentExpression(t
))
4201 /********************************
4203 * postfix-expression ( argument-expression-list(opt) )
4205 * pt = starting token, updated to one past end of initializer if true
4207 * true if function call
4209 private bool isFunctionCall(ref Token
* pt
)
4211 //printf("isFunctionCall()\n");
4214 if (!isPrimaryExpression(t
))
4216 if (t
.value
!= TOK
.leftParenthesis
)
4221 if (!isAssignmentExpression(t
))
4223 if (t
.value
== TOK
.comma
)
4228 if (t
.value
== TOK
.rightParenthesis
)
4235 if (t
.value
!= TOK
.semicolon
)
4241 /********************************
4242 * See if match for assignment-expression.
4244 * pt = starting token, updated to one past end of assignment-expression if true
4246 * true if assignment-expression
4248 private bool isAssignmentExpression(ref Token
* pt
)
4251 //printf("isAssignmentExpression() %s\n", t.toChars());
4253 /* This doesn't actually check for grammar matching an
4254 * assignment-expression. It just matches ( ) [ ] looking for
4255 * an ending token that would terminate one.
4264 case TOK
.rightParenthesis
:
4265 case TOK
.rightBracket
:
4271 case TOK
.leftParenthesis
:
4272 if (!skipParens(t
, &t
))
4275 https://issues.dlang.org/show_bug.cgi?id=22267
4276 If the parser encounters the following
4277 `identifier variableName = (expression);`
4278 the initializer is not identified as such since the parentheses
4279 cause the parser to keep walking indefinitely
4280 (whereas `(1) + 1` would not be affected.).
4285 case TOK
.leftBracket
:
4286 if (!skipBrackets(t
))
4296 any
= true; // assume token was part of an a-e
4305 /********************************
4306 * See if match for constant-expression.
4308 * pt = starting token, updated to one past end of constant-expression if true
4310 * true if constant-expression
4312 private bool isConstantExpression(ref Token
* pt
)
4314 return isAssignmentExpression(pt
);
4317 /********************************
4318 * See if match for declaration-specifiers.
4319 * No errors are diagnosed.
4321 * pt = starting token, updated to one past end of declaration-specifiers if true
4323 * true if declaration-specifiers
4325 private bool isDeclarationSpecifiers(ref Token
* pt
)
4327 //printf("isDeclarationSpecifiers()\n");
4349 //case TOK._Imaginary:
4356 case TOK
.identifier
: // typedef-name
4370 if (t
.value
== TOK
.__attribute__ ||
4371 t
.value
== TOK
.__declspec
)
4374 if (!skipParens(t
, &t
))
4377 if (t
.value
== TOK
.identifier
)
4380 if (t
.value
== TOK
.leftCurly
)
4386 else if (t
.value
== TOK
.leftCurly
)
4396 // storage-class-specifiers
4401 case TOK
._Thread_local
:
4405 // function-specifiers
4418 case TOK
._Alignas
: // alignment-specifier
4419 case TOK
.__declspec
: // decl-specifier
4420 case TOK
.__attribute__
: // attribute-specifier
4422 if (!skipParens(t
, &t
))
4427 // either atomic-type-specifier or type_qualifier
4428 case TOK
._Atomic
: // TODO _Atomic ( type-name )
4430 if (t
.value
== TOK
.leftParenthesis
) // maybe atomic-type-specifier
4434 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4435 { // it's a type-qualifier
4436 t
= tsave
; // back up parser
4440 t
= peek(t
); // move past right parenthesis of atomic-type-specifier
4459 /**************************************
4460 * See if declaration-list is present.
4462 * true if declaration-list is present, even an empty one
4464 bool isDeclarationList(ref Token
* pt
)
4469 if (t
.value
== TOK
.leftCurly
)
4474 if (!isCDeclaration(t
))
4479 /*******************************************
4482 * pt = enters on left brace, set to token past right bracket on true
4484 * true if successful
4486 private bool skipBraces(ref Token
* pt
)
4489 if (t
.value
!= TOK
.leftCurly
)
4503 case TOK
.rightCurly
:
4526 /*******************************************
4529 * pt = enters on left bracket, set to token past right bracket on true
4531 * true if successful
4533 private bool skipBrackets(ref Token
* pt
)
4536 if (t
.value
!= TOK
.leftBracket
)
4545 case TOK
.leftBracket
:
4550 case TOK
.rightBracket
:
4573 /*********************************
4574 * Check to see if tokens starting with *pt form a declarator.
4576 * pt = pointer to starting token, updated to point past declarator if true is returned
4577 * declarator = declarator kind
4581 private bool isCDeclarator(ref Token
* pt
, DTR declarator
)
4583 //printf("isCDeclarator()\n");
4587 if (t
.value
== TOK
.mul) // pointer
4590 if (!isTypeQualifierList(t
))
4597 if (t
.value
== TOK
.identifier
)
4599 if (declarator
== DTR
.xabstract
)
4603 else if (t
.value
== TOK
.leftParenthesis
)
4606 if (t
.value
== TOK
.__stdcall
)
4608 if (!isCDeclarator(t
, declarator
))
4610 if (t
.value
!= TOK
.rightParenthesis
)
4614 else if (declarator
== DTR
.xdirect
)
4621 if (t
.value
== TOK
.leftBracket
)
4623 if (!skipBrackets(t
))
4626 else if (t
.value
== TOK
.leftParenthesis
)
4628 if (!skipParens(t
, &t
))
4638 /***************************
4639 * Is this the start of a type-qualifier-list?
4642 * pt = first token; updated with past end of type-qualifier-list if true
4644 * true if start of type-qualifier-list
4646 private bool isTypeQualifierList(ref Token
* pt
)
4670 /***************************
4671 * Is this the start of a type-name?
4673 * pt = first token; updated with past end of type-name if true
4675 * true if start of type-name
4677 private bool isTypeName(ref Token
* pt
)
4680 //printf("isTypeName() %s\n", t.toChars());
4681 if (!isSpecifierQualifierList(t
))
4683 if (!isCDeclarator(t
, DTR
.xabstract
))
4685 if (t
.value
!= TOK
.rightParenthesis
)
4691 /***************************
4692 * Is this the start of a specifier-qualifier-list?
4694 * pt = first token; updated with past end of specifier-qualifier-list if true
4696 * true if start of specifier-qualifier-list
4698 private bool isSpecifierQualifierList(ref Token
* pt
)
4724 //case TOK._Imaginary: // ? missing in Spec
4729 case TOK
.identifier
:
4730 // Use typedef table to disambiguate
4731 if (isTypedef(t
.ident
))
4741 // struct-or-union-specifier
4747 if (t
.value
== TOK
.identifier
)
4750 if (t
.value
== TOK
.leftCurly
)
4756 else if (t
.value
== TOK
.leftCurly
)
4765 // atomic-type-specifier
4768 case TOK
.__attribute__
:
4770 if (t
.value
!= TOK
.leftParenthesis ||
4784 /************************************
4785 * Looking at the leading left parenthesis, and determine if it is
4786 * either of the following:
4787 * ( type-name ) cast-expression
4788 * ( type-name ) { initializer-list }
4792 * pt = starting token, updated to one past end of constant-expression if true
4793 * afterParenType = true if already seen `( type-name )`
4795 * true if matches ( type-name ) ...
4797 private bool isCastExpression(ref Token
* pt
, bool afterParenType
= false)
4800 if (log
) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token
.toChars(pt
.value
), afterParenType
);
4804 case TOK
.leftParenthesis
:
4805 auto tk
= peek(t
); // move past left parenthesis
4806 if (!isTypeName(tk
) || tk
.value
!= TOK
.rightParenthesis
)
4809 goto default; // could be ( type-name ) ( unary-expression )
4812 tk
= peek(tk
); // move past right parenthesis
4814 if (tk
.value
== TOK
.leftCurly
)
4816 // ( type-name ) { initializer-list }
4817 if (!isInitializer(tk
))
4825 if (tk
.value
== TOK
.leftParenthesis
&& peek(tk
).value
== TOK
.rightParenthesis
)
4827 return false; // (type-name)() is not a cast (it might be a function call)
4830 if (!isCastExpression(tk
, true))
4832 if (afterParenType
) // could be ( type-name ) ( unary-expression )
4833 goto default; // where unary-expression also matched type-name
4836 // ( type-name ) cast-expression
4841 if (!afterParenType ||
!isUnaryExpression(t
, afterParenType
))
4845 // if we've already seen ( type-name ), then this is a cast
4849 if (log
) printf("isCastExpression true\n");
4853 /********************************
4854 * See if match for unary-expression.
4856 * pt = starting token, updated to one past end of constant-expression if true
4857 * afterParenType = true if already seen ( type-name ) of a cast-expression
4859 * true if unary-expression
4861 private bool isUnaryExpression(ref Token
* pt
, bool afterParenType
= false)
4867 case TOK
.minusMinus
:
4869 if (!isUnaryExpression(t
, afterParenType
))
4880 if (!isCastExpression(t
, afterParenType
))
4886 if (t
.value
== TOK
.leftParenthesis
)
4891 if (tk
.value
!= TOK
.rightParenthesis
)
4897 if (!isUnaryExpression(t
, afterParenType
))
4903 if (t
.value
!= TOK
.leftParenthesis
)
4906 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4911 // Compound literals are handled by cast and sizeof expressions,
4912 // so be content with just seeing a primary expression.
4913 if (!isPrimaryExpression(t
))
4921 /********************************
4922 * See if match for primary-expression.
4924 * pt = starting token, updated to one past end of constant-expression if true
4926 * true if primary-expression
4928 private bool isPrimaryExpression(ref Token
* pt
)
4933 case TOK
.identifier
:
4934 case TOK
.charLiteral
:
4935 case TOK
.int32Literal
:
4936 case TOK
.uns32Literal
:
4937 case TOK
.int64Literal
:
4938 case TOK
.uns64Literal
:
4939 case TOK
.float32Literal
:
4940 case TOK
.float64Literal
:
4941 case TOK
.float80Literal
:
4942 case TOK
.imaginary32Literal
:
4943 case TOK
.imaginary64Literal
:
4944 case TOK
.imaginary80Literal
:
4949 case TOK
.leftParenthesis
:
4951 if (!skipParens(t
, &t
))
4957 if (!skipParens(t
, &t
))
4969 /******************************************************************************/
4970 /********************************* More ***************************************/
4974 * Declaration context
4978 global
= 1, /// global
4979 parameter
= 2, /// function parameter (declarations for function identifier-list)
4980 prototype
= 4, /// function prototype
4981 local
= 8, /// local
4982 member
= 0x10, /// struct member
4985 /// Types of declarator to parse
4988 xdirect
= 1, /// C11 6.7.6 direct-declarator
4989 xabstract
= 2, /// C11 6.7.7 abstract-declarator
4990 xparameter
= 3, /// parameter declarator may be either direct or abstract
4993 /// C11 6.7.1 Storage-class specifiers
5003 // C11 6.7.4 Function specifiers
5010 /// C11 6.7.3 Type qualifiers
5018 x__stdcall
= 0x10, // Windows linkage extension
5021 /**********************************
5022 * Aggregate for all the various specifiers
5026 bool noreturn
; /// noreturn attribute
5027 bool naked
; /// naked attribute
5028 bool _nothrow
; /// nothrow attribute
5029 bool _pure
; /// pure attribute
5030 bool dllimport
; /// dllimport attribute
5031 bool dllexport
; /// dllexport attribute
5032 bool _deprecated
; /// deprecated attribute
5033 AST
.Expression depMsg
; /// deprecated message
5034 uint vector_size
; /// positive power of 2 multipe of base type size
5036 SCW scw
; /// storage-class specifiers
5037 MOD mod
; /// type qualifiers
5038 AST
.Expressions
* alignExps
; /// alignment
5039 structalign_t packalign
; /// #pragma pack alignment value
5042 /***********************
5043 * Convert from C specifiers to D storage class
5045 * level = declaration context
5046 * specifier = specifiers, context, etc.
5048 * corresponding D storage class
5050 StorageClass
specifiersToSTC(LVL level
, const ref Specifier specifier
)
5053 if (specifier
.scw
& SCW
.x_Thread_local
)
5055 if (level
== LVL
.global
)
5057 if (specifier
.scw
& SCW
.xextern
)
5058 stc = AST
.STC
.extern_
;
5059 else if (specifier
.scw
& SCW
.xstatic
)
5060 stc = AST
.STC
.static_
;
5062 else if (level
== LVL
.local
)
5064 if (specifier
.scw
& SCW
.xextern
)
5065 stc = AST
.STC
.extern_
;
5066 else if (specifier
.scw
& SCW
.xstatic
)
5067 stc = AST
.STC
.static_
;
5069 else if (level
== LVL
.member
)
5071 if (specifier
.scw
& SCW
.xextern
)
5072 stc = AST
.STC
.extern_
;
5073 else if (specifier
.scw
& SCW
.xstatic
)
5074 stc = AST
.STC
.static_
;
5079 if (level
== LVL
.global
)
5081 if (specifier
.scw
& SCW
.xextern
)
5082 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5083 else if (specifier
.scw
& SCW
.xstatic
)
5084 stc = AST
.STC
.gshared | AST
.STC
.static_
;
5086 stc = AST
.STC
.gshared
;
5088 else if (level
== LVL
.local
)
5090 if (specifier
.scw
& SCW
.xextern
)
5091 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5092 else if (specifier
.scw
& SCW
.xstatic
)
5093 stc = AST
.STC
.gshared
;
5094 else if (specifier
.scw
& SCW
.xregister
)
5095 stc = AST
.STC
.register
;
5097 else if (level
== LVL
.parameter
)
5099 if (specifier
.scw
& SCW
.xregister
)
5100 stc = AST
.STC
.register | AST
.STC
.parameter
;
5102 stc = AST
.STC
.parameter
;
5104 else if (level
== LVL
.member
)
5106 if (specifier
.scw
& SCW
.xextern
)
5107 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
5108 else if (specifier
.scw
& SCW
.xstatic
)
5109 stc = AST
.STC
.gshared
;
5112 if (specifier
._deprecated
&& !specifier
.depMsg
)
5113 stc |
= AST
.STC
.deprecated_
;
5117 /***********************
5118 * Add attributes from Specifier to function
5120 * fd = function to apply them to
5121 * specifier = specifiers
5123 void specifiersToFuncDeclaration(AST
.FuncDeclaration fd
, const ref Specifier specifier
)
5125 fd
.isNaked
= specifier
.naked
;
5126 fd
.dllImport
= specifier
.dllimport
;
5127 fd
.dllExport
= specifier
.dllexport
;
5129 if (specifier
.scw
& SCW
.xnoinline
)
5130 fd
.inlining
= PINLINE
.never
;
5131 else if (specifier
.scw
& SCW
.xinline
)
5132 fd
.inlining
= PINLINE
.always
;
5135 /***********************
5136 * Add attributes from Specifier to variable
5138 * vd = function to apply them to
5139 * specifier = specifiers
5141 void specifiersToVarDeclaration(AST
.VarDeclaration vd
, const ref Specifier specifier
)
5143 vd
.dllImport
= specifier
.dllimport
;
5144 vd
.dllExport
= specifier
.dllexport
;
5147 /***********************
5148 * Return suitable signed integer type for the given size
5150 * size = size of type
5152 * corresponding signed D integer type
5154 private AST
.Type
integerTypeForSize(ubyte size
)
5157 return AST
.Type
.tint8
;
5159 return AST
.Type
.tint16
;
5161 return AST
.Type
.tint32
;
5163 return AST
.Type
.tint64
;
5166 error("__int128 not supported");
5167 return AST
.Type
.terror
;
5169 error("unsupported integer type");
5170 return AST
.Type
.terror
;
5173 /***********************
5174 * Return suitable unsigned integer type for the given size
5176 * size = size of type
5178 * corresponding unsigned D integer type
5180 private AST
.Type
unsignedTypeForSize(ubyte size
)
5183 return AST
.Type
.tuns8
;
5185 return AST
.Type
.tuns16
;
5187 return AST
.Type
.tuns32
;
5189 return AST
.Type
.tuns64
;
5192 error("unsigned __int128 not supported");
5193 return AST
.Type
.terror
;
5195 error("unsupported integer type");
5196 return AST
.Type
.terror
;
5199 /***********************
5200 * Return suitable D float type for C `long double`
5202 * flags = kind of float to return (real, imaginary, complex).
5204 * corresponding D type
5206 private AST
.Type
realType(RTFlags flags
)
5208 if (long_doublesize
== AST
.Type
.tfloat80
.size())
5210 // On GDC and LDC, D `real` types map to C `long double`, so never
5211 // return a double type when real.sizeof == double.sizeof.
5212 final switch (flags
)
5214 case RTFlags
.realfloat
: return AST
.Type
.tfloat80
;
5215 case RTFlags
.imaginary
: return AST
.Type
.timaginary80
;
5216 case RTFlags
.complex
: return AST
.Type
.tcomplex80
;
5221 final switch (flags
)
5223 case RTFlags
.realfloat
: return long_doublesize
== 8 ? AST
.Type
.tfloat64
: AST
.Type
.tfloat80
;
5224 case RTFlags
.imaginary
: return long_doublesize
== 8 ? AST
.Type
.timaginary64
: AST
.Type
.timaginary80
;
5225 case RTFlags
.complex
: return long_doublesize
== 8 ? AST
.Type
.tcomplex64
: AST
.Type
.tcomplex80
;
5231 * Flags for realType
5233 private enum RTFlags
5240 /********************
5241 * C11 6.4.2.2 Create declaration to predefine __func__
5242 * `static const char __func__[] = " function-name ";`
5244 * loc = location for this declaration
5245 * id = identifier of function
5247 * statement representing the declaration of __func__
5249 private AST
.Statement
createFuncName(Loc loc
, Identifier id
)
5251 const fn
= id
.toString(); // function-name
5252 auto efn
= new AST
.StringExp(loc
, fn
, fn
.length
, 1, 'c');
5253 auto ifn
= new AST
.ExpInitializer(loc
, efn
);
5254 auto lenfn
= new AST
.IntegerExp(loc
, fn
.length
+ 1, AST
.Type
.tuns32
); // +1 for terminating 0
5255 auto tfn
= new AST
.TypeSArray(AST
.Type
.tchar
, lenfn
);
5256 efn
.type
= tfn
.immutableOf();
5257 efn
.committed
= true;
5258 auto sfn
= new AST
.VarDeclaration(loc
, tfn
, Id
.__func__
, ifn
, STC
.gshared | STC
.immutable_
);
5259 auto e
= new AST
.DeclarationExp(loc
, sfn
);
5260 return new AST
.ExpStatement(loc
, e
);
5263 /************************
5264 * After encountering an error, scan forward until a right brace or ; is found
5265 * or the end of the file.
5269 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
5273 /**************************
5274 * Apply `const` to a type.
5276 * t = type to add const to
5280 private AST
.Type
toConst(AST
.Type t
)
5282 // `const` is always applied to the return type, not the
5283 // type function itself.
5284 if (auto tf
= t
.isTypeFunction())
5285 tf
.next
= tf
.next
.addSTC(STC
.const_
);
5286 else if (auto tt
= t
.isTypeTag())
5287 tt
.mod |
= MODFlags
.const_
;
5290 /* Ignore const if the result would be const pointer to mutable
5292 auto tn
= t
.nextOf();
5293 if (!tn || tn
.isConst())
5294 t
= t
.addSTC(STC
.const_
);
5299 /***************************
5300 * Apply specifier to a Dsymbol.
5303 * specifier = specifiers to apply
5305 * Dsymbol with specifiers applied
5307 private AST
.Dsymbol
applySpecifier(AST
.Dsymbol s
, ref Specifier specifier
)
5309 //printf("applySpecifier() %s\n", s.toChars());
5310 if (specifier
._deprecated
)
5312 if (specifier
.depMsg
)
5314 // Wrap declaration in a DeprecatedDeclaration
5315 auto decls
= new AST
.Dsymbols(1);
5317 s
= new AST
.DeprecatedDeclaration(specifier
.depMsg
, decls
);
5321 if (specifier
.alignExps
)
5323 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5324 // Wrap declaration in an AlignDeclaration
5325 auto decls
= new AST
.Dsymbols(1);
5327 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.alignExps
, decls
);
5329 else if (!specifier
.packalign
.isDefault() && !specifier
.packalign
.isUnknown())
5331 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5332 // Wrap #pragma pack in an AlignDeclaration
5333 auto decls
= new AST
.Dsymbols(1);
5335 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.packalign
, decls
);
5342 /******************************************************************************/
5343 /************************** typedefTab symbol table ***************************/
5346 /********************************
5347 * Determines if type t is a function type.
5351 * true if it represents a function
5353 bool isFunctionTypedef(AST
.Type t
)
5355 //printf("isFunctionTypedef() %s\n", t.toChars());
5356 if (t
.isTypeFunction())
5358 if (auto tid
= t
.isTypeIdentifier())
5360 auto pt
= lookupTypedef(tid
.ident
);
5363 return (*pt
).isTypeFunction() !is null;
5369 /********************************
5370 * Determine if `id` is a symbol for a Typedef.
5372 * id = possible typedef
5374 * true if id is a Type
5376 bool isTypedef(Identifier id
)
5378 auto pt
= lookupTypedef(id
);
5382 /*******************************
5383 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5384 * Params: id = identifier for non-typedef symbol
5386 void insertIdToTypedefTab(Identifier id
)
5388 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5389 if (isTypedef(id
)) // if existing typedef
5391 /* Add id as null, so we can later distinguish it from a non-null typedef
5393 auto tab
= cast(void*[void*])(typedefTab
[$ - 1]);
5394 tab
[cast(void*)id
] = cast(void*)null;
5398 /*******************************
5399 * Add `id` to typedefTab[]
5401 * id = identifier for typedef symbol
5402 * t = type of the typedef symbol
5404 void insertTypedefToTypedefTab(Identifier id
, AST
.Type t
)
5406 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5407 if (auto tid
= t
.isTypeIdentifier())
5409 // Try to resolve the TypeIdentifier to its type
5410 auto pt
= lookupTypedef(tid
.ident
);
5414 auto tab
= cast(void*[void*])(typedefTab
[$ - 1]);
5415 tab
[cast(void*)id
] = cast(void*)t
;
5416 typedefTab
[$ - 1] = cast(void*)tab
;
5419 /*********************************
5420 * Lookup id in typedefTab[].
5422 * if not found, then null.
5423 * if found, then Type*. Deferencing it will yield null if it is not
5424 * a typedef, and a type if it is a typedef.
5426 AST
.Type
* lookupTypedef(Identifier id
)
5428 foreach_reverse (tab
; typedefTab
[])
5430 if (auto pt
= cast(void*)id
in cast(void*[void*])tab
)
5432 return cast(AST
.Type
*)pt
;
5435 return null; // not found
5440 /******************************************************************************/
5441 /********************************* Directive Parser ***************************/
5444 override bool parseSpecialTokenSequence()
5448 if (n
.value
== TOK
.int32Literal
)
5453 if (n
.value
== TOK
.identifier
)
5455 if (n
.ident
== Id
.line
)
5457 poundLine(n
, false);
5460 else if (defines
&& (n
.ident
== Id
.define || n
.ident
== Id
.undef
))
5462 /* Append this line to `defines`.
5463 * Not canonicalizing it - assume it already is
5465 defines
.writeByte('#');
5466 defines
.writestring(n
.ident
.toString());
5467 skipToNextLine(defines
);
5468 defines
.writeByte('\n');
5471 else if (n
.ident
== Id
.__pragma
)
5473 pragmaDirective(scanloc
);
5476 else if (n
.ident
== Id
.ident
) // #ident "string"
5479 if (n
.value
== TOK
.string_
&& n
.ptr
[0] == '"' && n
.postfix
== 0)
5481 /* gcc inserts string into the .comment section in the object file.
5482 * Just ignore it for now, but can support it later by writing
5483 * the string to obj_exestr()
5485 //auto comment = n.ustring;
5488 if (n
.value
== TOK
.endOfFile || n
.value
== TOK
.endOfLine
)
5491 error("\"string\" expected after `#ident`");
5495 if (n
.ident
!= Id
.undef
)
5496 error("C preprocessor directive `#%s` is not supported", n
.toChars());
5500 /*********************************************
5502 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5503 * Scanner is on the `__pragma`
5505 * startloc = location to use for error messages
5507 private void uupragmaDirective(const ref Loc startloc
)
5509 const loc
= startloc
;
5510 nextToken(); // move past __pragma
5511 if (token
.value
!= TOK
.leftParenthesis
)
5513 error(loc
, "left parenthesis expected to follow `__pragma` instead of `%s`", token
.toChars());
5519 if (token
.value
== TOK
.identifier
)
5521 if (token
.ident
== Id
.pack
)
5522 pragmaPack(startloc
, false);
5526 if (token
.value
== TOK
.leftParenthesis
)
5531 else if (token
.value
== TOK
.endOfFile
)
5534 else if (token
.value
== TOK
.rightParenthesis
)
5538 error(loc
, "unrecognized `__pragma(%s)`", token
.toChars());
5540 if (token
.value
!= TOK
.rightParenthesis
)
5542 error(loc
, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token
.toChars());
5548 /*********************************************
5549 * C11 6.10.6 Pragma directive
5550 * # pragma pp-tokens(opt) new-line
5551 * The C preprocessor sometimes leaves pragma directives in
5552 * the preprocessed output. Ignore them.
5553 * Upon return, p is at start of next line.
5555 private void pragmaDirective(const ref Loc loc
)
5559 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pack
)
5560 return pragmaPack(loc
, true);
5561 if (n
.value
!= TOK
.endOfLine
)
5567 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5568 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5569 * Scanner is on the `pack`
5571 * startloc = location to use for error messages
5572 * useScan = use scan() to retrieve next token, instead of nextToken()
5574 private void pragmaPack(const ref Loc startloc
, bool useScan
)
5576 const loc
= startloc
;
5578 /* Pull tokens from scan() or nextToken()
5595 if (n
.value
!= TOK
.leftParenthesis
)
5597 error(loc
, "left parenthesis expected to follow `#pragma pack`");
5598 if (n
.value
!= TOK
.endOfLine
)
5605 if (n
.value
!= TOK
.rightParenthesis
)
5607 error(loc
, "right parenthesis expected to close `#pragma pack(`");
5609 if (n
.value
!= TOK
.endOfLine
)
5613 void setPackAlign(ref const Token t
)
5615 const n
= t
.unsvalue
;
5616 if (n
< 1 || n
& (n
- 1) ||
ushort.max
< n
)
5617 error(loc
, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n
);
5618 packalign
.set(cast(uint)n
);
5619 packalign
.setPack(true);
5626 records
= new Array
!Identifier
;
5627 packs
= new Array
!structalign_t
;
5630 /* # pragma pack ( show )
5632 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.show
)
5634 if (packalign
.isDefault())
5635 eSink
.warning(startloc
, "current pack attribute is default");
5637 eSink
.warning(startloc
, "current pack attribute is %d", packalign
.get());
5639 return closingParen();
5641 /* # pragma pack ( push )
5642 * # pragma pack ( push , identifier )
5643 * # pragma pack ( push , integer )
5644 * # pragma pack ( push , identifier , integer )
5646 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.push)
5649 Identifier record
= null;
5650 if (n
.value
== TOK
.comma
)
5653 if (n
.value
== TOK
.identifier
)
5657 if (n
.value
== TOK
.comma
)
5660 if (n
.value
== TOK
.int32Literal
)
5666 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5669 else if (n
.value
== TOK
.int32Literal
)
5675 error(loc
, "alignment value expected, not `%s`", n
.toChars());
5677 this.records
.push(record
);
5678 this.packs
.push(packalign
);
5679 return closingParen();
5681 /* # pragma pack ( pop )
5682 * # pragma pack ( pop PopList )
5684 * , IdentifierOrInteger
5685 * , IdentifierOrInteger PopList
5686 * IdentifierOrInteger:
5690 if (n
.value
== TOK
.identifier
&& n
.ident
== Id
.pop)
5693 size_t len
= this.records
.length
;
5694 if (n
.value
== TOK
.rightParenthesis
) // #pragma pack ( pop )
5696 if (len
== 0) // nothing to pop
5697 return closingParen();
5699 this.records
.setDim(len
- 1);
5700 this.packs
.setDim(len
- 1);
5701 if (len
== 1) // stack is now empty
5702 packalign
.setDefault();
5704 packalign
= (*this.packs
)[len
- 1];
5705 return closingParen();
5707 while (n
.value
== TOK
.comma
) // #pragma pack ( pop ,
5710 if (n
.value
== TOK
.identifier
)
5712 /* pragma pack(pop, identifier
5713 * Pop until identifier is found, pop that one too, and set
5714 * alignment to the new top of the stack.
5715 * If identifier is not found, do nothing.
5719 if ((*this.records
)[len
- 1] == n
.ident
)
5721 this.records
.setDim(len
- 1);
5722 this.packs
.setDim(len
- 1);
5724 packalign
= (*this.packs
)[len
- 2];
5726 packalign
.setDefault(); // stack empty, use default
5732 else if (n
.value
== TOK
.int32Literal
)
5739 error(loc
, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n
.toChars());
5743 return closingParen();
5745 /* # pragma pack ( integer )
5746 * Sets alignment to integer
5748 if (n
.value
== TOK
.int32Literal
)
5752 return closingParen();
5754 /* # pragma pack ( )
5755 * Sets alignment to default
5757 if (n
.value
== TOK
.rightParenthesis
)
5759 packalign
.setDefault();
5760 return closingParen();
5763 error(loc
, "unrecognized `#pragma pack(%s)`", n
.toChars());
5764 if (n
.value
!= TOK
.endOfLine
)
5770 /******************************************************************************/
5771 /********************************* #define Parser *****************************/
5775 * Go through the #define's in the defines buffer and see what we can convert
5776 * to Dsymbols, which are then appended to symbols[]
5780 if (!defines || defines
.length
< 10) // minimum length of a #define line
5782 OutBuffer
* buf
= defines
;
5783 defines
= null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5784 // from appending to slice[]
5785 const length
= buf
.length
;
5787 auto slice
= buf
.peekChars()[0 .. length
];
5788 resetDefineLines(slice
); // reset lexer
5790 const(char)* endp
= &slice
[length
- 7];
5792 size_t
[void*] defineTab
; // hash table of #define's turned into Symbol's
5793 // indexed by Identifier, returns index into symbols[]
5794 // The memory for this is leaked
5796 void addVar(AST
.VarDeclaration v
)
5798 //printf("addVar() %s\n", v.toChars());
5799 v
.isCmacro(true); // mark it as coming from a C #define
5800 /* If it's already defined, replace the earlier
5803 if (size_t
* pd
= cast(void*)v
.ident
in defineTab
)
5805 //printf("replacing %s\n", v.toChars());
5806 (*symbols
)[*pd
] = v
;
5809 defineTab
[cast(void*)v
.ident
] = symbols
.length
;
5817 if (p
[0 .. 7] == "#define")
5821 //printf("%s\n", n.toChars());
5822 if (n
.value
== TOK
.identifier
)
5831 case TOK
.endOfLine
: // #define identifier
5835 case TOK
.int32Literal
:
5836 case TOK
.charLiteral
: t
= AST
.Type
.tint32
; goto Linteger
;
5837 case TOK
.uns32Literal
: t
= AST
.Type
.tuns32
; goto Linteger
;
5838 case TOK
.int64Literal
: t
= AST
.Type
.tint64
; goto Linteger
;
5839 case TOK
.uns64Literal
: t
= AST
.Type
.tuns64
; goto Linteger
;
5842 const intvalue
= n
.intvalue
;
5844 if (n
.value
== TOK
.endOfLine
)
5846 /* Declare manifest constant:
5847 * enum id = intvalue;
5849 AST
.Expression e
= new AST
.IntegerExp(scanloc
, intvalue
, t
);
5850 auto v
= new AST
.VarDeclaration(scanloc
, t
, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5857 case TOK
.float32Literal
: t
= AST
.Type
.tfloat32
; goto Lfloat
;
5858 case TOK
.float64Literal
: t
= AST
.Type
.tfloat64
; goto Lfloat
;
5859 case TOK
.float80Literal
: t
= AST
.Type
.tfloat80
; goto Lfloat
;
5860 case TOK
.imaginary32Literal
: t
= AST
.Type
.timaginary32
; goto Lfloat
;
5861 case TOK
.imaginary64Literal
: t
= AST
.Type
.timaginary64
; goto Lfloat
;
5862 case TOK
.imaginary80Literal
: t
= AST
.Type
.timaginary80
; goto Lfloat
;
5865 const floatvalue
= n
.floatvalue
;
5867 if (n
.value
== TOK
.endOfLine
)
5869 /* Declare manifest constant:
5870 * enum id = floatvalue;
5872 AST
.Expression e
= new AST
.RealExp(scanloc
, floatvalue
, t
);
5873 auto v
= new AST
.VarDeclaration(scanloc
, t
, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5881 const str = n
.ustring
;
5883 const postfix
= n
.postfix
;
5885 if (n
.value
== TOK
.endOfLine
)
5887 /* Declare manifest constant:
5888 * enum id = "string";
5890 AST
.Expression e
= new AST
.StringExp(scanloc
, str[0 .. len
], len
, 1, postfix
);
5891 auto v
= new AST
.VarDeclaration(scanloc
, null, id
, new AST
.ExpInitializer(scanloc
, e
), STC
.manifest
);
5907 if (n
.value
!= TOK
.endOfLine
)