2 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://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
;
21 import dmd
.identifier
;
25 import dmd
.root
.filename
;
26 import dmd
.common
.outbuffer
;
28 import dmd
.root
.rootobject
;
29 import dmd
.root
.string
;
32 /***********************************************************
34 final class CParser(AST
) : Parser
!AST
36 AST
.Dsymbols
* symbols
; // symbols declared in current scope
38 bool addFuncName
; /// add declaration of __func__ to function symbol table
40 extern (D
) this(TARGET
)(AST
.Module _module
, const(char)[] input
, bool doDocComment
,
41 const ref TARGET target
)
43 super(_module
, input
, doDocComment
);
45 //printf("CParser.this()\n");
50 // Configure sizes for C `long`, `long double`, `wchar_t`
51 this.longsize
= target
.longsize
;
52 this.long_doublesize
= target
.long_doublesize
;
53 this.wchar_tsize
= target
.wchar_tsize
;
55 // C `char` is always unsigned in ImportC
58 /********************************************
59 * Parse translation unit.
62 * external-declaration
63 * translation-unit external-declaration
65 * external-declaration:
69 * array of Dsymbols that were declared
71 override AST
.Dsymbols
* parseModule()
73 //printf("cparseTranslationUnit()\n");
74 symbols
= new AST
.Dsymbols();
75 addBuiltinDeclarations();
78 if (token
.value
== TOK
.endOfFile
)
80 // wrap the symbols in `extern (C) { symbols }`
81 auto wrap
= new AST
.Dsymbols();
82 auto ld
= new AST
.LinkDeclaration(token
.loc
, LINK
.c
, symbols
);
88 cparseDeclaration(LVL
.global
);
92 /******************************************************************************/
93 /********************************* Statement Parser ***************************/
96 /**********************
101 * expression-statement
102 * selection-statement
103 * iteration-statement
108 * endPtr = store location of closing brace
109 * pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
113 AST
.Statement
cparseStatement(int flags
, const(char)** endPtr
= null, Loc
* pEndloc
= null)
116 const loc
= token
.loc
;
118 //printf("cparseStatement()\n");
120 auto symbolsSave
= symbols
;
121 if (!(flags
& (ParseStatementFlags
.scope_ | ParseStatementFlags
.curlyScope
)))
122 symbols
= new AST
.Dsymbols();
127 /* A leading identifier can be a declaration, label, or expression.
128 * A quick check of the next token can disambiguate most cases.
135 auto ident
= token
.ident
;
136 nextToken(); // advance to `:`
137 nextToken(); // advance past `:`
138 if (token
.value
== TOK
.rightCurly
)
140 else if (token
.value
== TOK
.leftCurly
)
141 s
= cparseStatement(ParseStatementFlags
.curly | ParseStatementFlags
.scope_
);
143 s
= cparseStatement(ParseStatementFlags
.semiOk
);
144 s
= new AST
.LabelStatement(loc
, ident
, s
);
152 case TOK
.leftBracket
:
163 case TOK
.leftShiftAssign
:
164 case TOK
.rightShiftAssign
:
167 case TOK
.leftParenthesis
:
169 /* If tokens look like a function call, assume it is one,
170 * As any type-name won't be resolved until semantic, this
171 * could be rewritten later.
174 if (isFunctionCall(tk
))
181 /* If tokens look like a declaration, assume it is one
184 if (isCDeclaration(tk
))
191 case TOK
.int32Literal
:
192 case TOK
.uns32Literal
:
193 case TOK
.int64Literal
:
194 case TOK
.uns64Literal
:
195 case TOK
.int128Literal
:
196 case TOK
.uns128Literal
:
197 case TOK
.float32Literal
:
198 case TOK
.float64Literal
:
199 case TOK
.float80Literal
:
200 case TOK
.imaginary32Literal
:
201 case TOK
.imaginary64Literal
:
202 case TOK
.imaginary80Literal
:
203 case TOK
.leftParenthesis
:
214 auto exp
= cparseExpression();
215 if (token
.value
== TOK
.identifier
&& exp
.op
== EXP
.identifier
)
217 error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token
).toChars(), exp
.toChars(), token
.toChars(), peek(peek(&token
)).toChars());
221 check(TOK
.semicolon
, "statement");
222 s
= new AST
.ExpStatement(loc
, exp
);
236 //case TOK._Imaginary:
242 // storage-class-specifiers
246 case TOK
._Thread_local
:
250 // function-specifiers
259 // alignment-specifier
262 // atomic-type-specifier or type_qualifier
267 cparseDeclaration(LVL
.local
);
268 if (symbols
.length
> 1)
270 auto as
= new AST
.Statements();
271 as
.reserve(symbols
.length
);
272 foreach (d
; (*symbols
)[])
274 s
= new AST
.ExpStatement(loc
, d
);
277 s
= new AST
.CompoundDeclarationStatement(loc
, as
);
280 else if (symbols
.length
== 1)
282 auto d
= (*symbols
)[0];
283 s
= new AST
.ExpStatement(loc
, d
);
287 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
288 if (flags
& ParseStatementFlags
.scope_
)
289 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
293 case TOK
._Static_assert
: // _Static_assert ( constant-expression, string-literal ) ;
294 s
= new AST
.StaticAssertStatement(cparseStaticAssert());
300 * compound-statement:
301 * { block-item-list (opt) }
305 * block-item-list block-item
312 auto statements
= new AST
.Statements();
313 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.endOfFile
)
315 statements
.push(cparseStatement(ParseStatementFlags
.semi | ParseStatementFlags
.curlyScope
));
322 *pEndloc
= token
.loc
;
323 pEndloc
= null; // don't set it again
325 s
= new AST
.CompoundStatement(loc
, statements
);
326 if (flags
& (ParseStatementFlags
.scope_ | ParseStatementFlags
.curlyScope
))
327 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
328 check(TOK
.rightCurly
, "compound statement");
335 check(TOK
.leftParenthesis
);
336 auto condition
= cparseExpression();
337 check(TOK
.rightParenthesis
);
339 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
340 s
= new AST
.WhileStatement(loc
, condition
, _body
, endloc
, null);
345 /* C11 6.8.3 null statement
348 s
= new AST
.ExpStatement(loc
, cast(AST
.Expression
)null);
354 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
356 check(TOK
.leftParenthesis
);
357 auto condition
= cparseExpression();
358 check(TOK
.rightParenthesis
);
359 check(TOK
.semicolon
, "terminating `;` required after do-while statement");
360 s
= new AST
.DoStatement(loc
, _body
, condition
, token
.loc
);
367 AST
.Expression condition
;
368 AST
.Expression increment
;
371 check(TOK
.leftParenthesis
);
372 if (token
.value
== TOK
.semicolon
)
379 _init
= cparseStatement(0);
381 if (token
.value
== TOK
.semicolon
)
388 condition
= cparseExpression();
389 check(TOK
.semicolon
, "`for` condition");
391 if (token
.value
== TOK
.rightParenthesis
)
398 increment
= cparseExpression();
399 check(TOK
.rightParenthesis
);
402 auto _body
= cparseStatement(ParseStatementFlags
.scope_
, null, &endloc
);
403 s
= new AST
.ForStatement(loc
, _init
, condition
, increment
, _body
, endloc
);
410 check(TOK
.leftParenthesis
);
411 auto condition
= cparseExpression();
412 check(TOK
.rightParenthesis
);
413 auto ifbody
= cparseStatement(ParseStatementFlags
.scope_
);
414 AST
.Statement elsebody
;
415 if (token
.value
== TOK
.else_
)
418 elsebody
= cparseStatement(ParseStatementFlags
.scope_
);
422 if (condition
&& ifbody
)
423 s
= new AST
.IfStatement(loc
, null, condition
, ifbody
, elsebody
, token
.loc
);
425 s
= null; // don't propagate parsing errors
430 error("found `else` without a corresponding `if` statement");
436 check(TOK
.leftParenthesis
);
437 auto condition
= cparseExpression();
438 check(TOK
.rightParenthesis
);
439 auto _body
= cparseStatement(ParseStatementFlags
.scope_
);
440 s
= new AST
.SwitchStatement(loc
, condition
, _body
, false);
448 auto exp
= cparseAssignExp();
451 if (flags
& ParseStatementFlags
.curlyScope
)
453 auto statements
= new AST
.Statements();
454 while (token
.value
!= TOK
.case_
&& token
.value
!= TOK
.default_
&& token
.value
!= TOK
.endOfFile
&& token
.value
!= TOK
.rightCurly
)
456 auto cur
= cparseStatement(ParseStatementFlags
.semi | ParseStatementFlags
.curlyScope
);
457 statements
.push(cur
);
459 // https://issues.dlang.org/show_bug.cgi?id=21739
460 // Stop at the last break s.t. the following non-case statements are
461 // not merged into the current case. This can happen for
462 // case 1: ... break;
463 // debug { case 2: ... }
464 if (cur
&& cur
.isBreakStatement())
467 s
= new AST
.CompoundStatement(loc
, statements
);
471 s
= cparseStatement(ParseStatementFlags
.semi
);
473 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
474 s
= new AST
.CaseStatement(loc
, exp
, s
);
483 if (flags
& ParseStatementFlags
.curlyScope
)
485 auto statements
= new AST
.Statements();
486 while (token
.value
!= TOK
.case_
&& token
.value
!= TOK
.default_
&& token
.value
!= TOK
.endOfFile
&& token
.value
!= TOK
.rightCurly
)
488 statements
.push(cparseStatement(ParseStatementFlags
.semi | ParseStatementFlags
.curlyScope
));
490 s
= new AST
.CompoundStatement(loc
, statements
);
493 s
= cparseStatement(ParseStatementFlags
.semi
);
494 s
= new AST
.ScopeStatement(loc
, s
, token
.loc
);
495 s
= new AST
.DefaultStatement(loc
, s
);
502 * return expression ;
505 auto exp
= token
.value
== TOK
.semicolon ?
null : cparseExpression();
506 check(TOK
.semicolon
, "`return` statement");
507 s
= new AST
.ReturnStatement(loc
, exp
);
513 check(TOK
.semicolon
, "`break` statement");
514 s
= new AST
.BreakStatement(loc
, null);
519 check(TOK
.semicolon
, "`continue` statement");
520 s
= new AST
.ContinueStatement(loc
, null);
527 if (token
.value
!= TOK
.identifier
)
529 error("identifier expected following `goto`");
537 s
= new AST
.GotoStatement(loc
, ident
);
538 check(TOK
.semicolon
, "`goto` statement");
547 error("found `%s` instead of statement", token
.toChars());
552 if (token
.value
== TOK
.semicolon
)
559 symbols
= symbolsSave
;
564 /*******************************************************************************/
565 /********************************* Expression Parser ***************************/
571 * assignment-expression
572 * expression , assignment-expression
574 AST
.Expression
cparseExpression()
576 auto loc
= token
.loc
;
578 //printf("cparseExpression() loc = %d\n", loc.linnum);
579 auto e
= cparseAssignExp();
580 while (token
.value
== TOK
.comma
)
583 auto e2
= cparseAssignExp();
584 e
= new AST
.CommaExp(loc
, e
, e2
, false);
591 /*********************
593 * primary-expression:
600 AST
.Expression
cparsePrimaryExp()
603 const loc
= token
.loc
;
605 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
609 if (token
.ident
is Id
.__func__
)
611 addFuncName
= true; // implicitly declare __func__
613 e
= new AST
.IdentifierExp(loc
, token
.ident
);
617 case TOK
.int32Literal
:
618 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint32
);
622 case TOK
.uns32Literal
:
623 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns32
);
627 case TOK
.int64Literal
:
628 e
= new AST
.IntegerExp(loc
, token
.intvalue
, AST
.Type
.tint64
);
632 case TOK
.uns64Literal
:
633 e
= new AST
.IntegerExp(loc
, token
.unsvalue
, AST
.Type
.tuns64
);
637 case TOK
.float32Literal
:
638 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat32
);
642 case TOK
.float64Literal
:
643 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat64
);
647 case TOK
.float80Literal
:
648 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.tfloat80
);
652 case TOK
.imaginary32Literal
:
653 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary32
);
657 case TOK
.imaginary64Literal
:
658 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary64
);
662 case TOK
.imaginary80Literal
:
663 e
= new AST
.RealExp(loc
, token
.floatvalue
, AST
.Type
.timaginary80
);
669 // cat adjacent strings
670 auto s
= token
.ustring
;
671 auto len
= token
.len
;
672 auto postfix
= token
.postfix
;
676 if (token
.value
== TOK
.string_
)
680 if (token
.postfix
!= postfix
)
681 error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix
, token
.postfix
);
682 postfix
= token
.postfix
;
686 const len2
= token
.len
;
688 auto s2
= cast(char*)mem
.xmalloc_noscan(len
* char.sizeof
);
689 memcpy(s2
, s
, len1
* char.sizeof
);
690 memcpy(s2
+ len1
, token
.ustring
, len2
* char.sizeof
);
696 e
= new AST
.StringExp(loc
, s
[0 .. len
], len
, 1, postfix
);
700 case TOK
.leftParenthesis
:
702 e
= cparseExpression();
703 check(TOK
.rightParenthesis
);
707 e
= cparseGenericSelection();
711 error("expression expected, not `%s`", token
.toChars());
712 // Anything for e, as long as it's not NULL
713 e
= new AST
.IntegerExp(loc
, 0, AST
.Type
.tint32
);
720 /*********************************
722 * postfix-expression:
724 * postfix-expression [ expression ]
725 * postfix-expression ( argument-expression-list (opt) )
726 * postfix-expression . identifier
727 * postfix-expression -> identifier
728 * postfix-expression ++
729 * postfix-expression --
730 * ( type-name ) { initializer-list }
731 * ( type-name ) { initializer-list , }
733 * argument-expression-list:
734 * assignment-expression
735 * argument-expression-list , assignment-expression
737 private AST
.Expression
cparsePostfixExp(AST
.Expression e
)
739 e
= cparsePrimaryExp();
740 return cparsePostfixOperators(e
);
743 /********************************
745 * Parse a series of operators for a postfix expression after already parsing
746 * a primary-expression or compound literal expression.
748 * e = parsed primary or compound literal expression
750 * parsed postfix expression
752 private AST
.Expression
cparsePostfixOperators(AST
.Expression e
)
756 const loc
= token
.loc
;
761 if (token
.value
== TOK
.identifier
)
763 Identifier id
= token
.ident
;
764 e
= new AST
.DotIdExp(loc
, e
, id
);
767 error("identifier expected following `.`, not `%s`", token
.toChars());
772 if (token
.value
== TOK
.identifier
)
774 Identifier id
= token
.ident
;
775 auto die
= new AST
.DotIdExp(loc
, e
, id
);
780 error("identifier expected following `->`, not `%s`", token
.toChars());
784 e
= new AST
.PostExp(EXP
.plusPlus
, loc
, e
);
788 e
= new AST
.PostExp(EXP
.minusMinus
, loc
, e
);
791 case TOK
.leftParenthesis
:
792 e
= new AST
.CallExp(loc
, e
, cparseArguments());
795 case TOK
.leftBracket
:
797 // array dereferences:
799 AST
.Expression index
;
800 auto arguments
= new AST
.Expressions();
804 index
= cparseAssignExp();
805 arguments
.push(index
);
806 check(TOK
.rightBracket
);
808 e
= new AST
.ArrayExp(loc
, e
, arguments
);
818 /************************
822 * ++ unary-expression
823 * -- unary-expression
824 * unary-operator cast-expression
825 * sizeof unary-expression
826 * sizeof ( type-name )
827 * _Alignof ( type-name )
832 private AST
.Expression
cparseUnaryExp()
835 const loc
= token
.loc
;
841 // Parse `++` as an unary operator so that cast expressions only give
842 // an error for being non-lvalues.
844 e
= new AST
.PreExp(EXP
.prePlusPlus
, loc
, e
);
849 // Parse `--` as an unary operator, same as prefix increment.
851 e
= new AST
.PreExp(EXP
.preMinusMinus
, loc
, e
);
857 e
= new AST
.AddrExp(loc
, e
);
863 e
= new AST
.PtrExp(loc
, e
);
869 e
= new AST
.NegExp(loc
, e
);
875 e
= new AST
.UAddExp(loc
, e
);
881 e
= new AST
.NotExp(loc
, e
);
887 e
= new AST
.ComExp(loc
, e
);
893 if (token
.value
== TOK
.leftParenthesis
)
895 auto tk
= peek(&token
);
898 /* Expression may be either be requesting the sizeof a type-name
899 * or a compound literal, which requires checking whether
900 * the next token is leftCurly
903 auto t
= cparseTypeName();
904 check(TOK
.rightParenthesis
);
905 if (token
.value
== TOK
.leftCurly
)
907 // ( type-name ) { initializer-list }
908 auto ci
= cparseInitializer();
909 e
= new AST
.CompoundLiteralExp(loc
, t
, ci
);
910 e
= cparsePostfixOperators(e
);
915 e
= new AST
.TypeExp(loc
, t
);
917 e
= new AST
.DotIdExp(loc
, e
, Id
.__sizeof
);
921 e
= cparseUnaryExp();
922 e
= new AST
.DotIdExp(loc
, e
, Id
.__sizeof
);
929 check(TOK
.leftParenthesis
);
930 auto t
= cparseTypeName();
931 check(TOK
.rightParenthesis
);
932 e
= new AST
.TypeExp(loc
, t
);
933 e
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
938 e
= cparsePostfixExp(e
);
949 * ( type-name ) cast-expression
951 private AST
.Expression
cparseCastExp()
953 if (token
.value
== TOK
.leftParenthesis
)
957 if (isCastExpression(pt
))
959 // Expression may be either a cast or a compound literal, which
960 // requires checking whether the next token is leftCurly
961 const loc
= token
.loc
;
963 auto t
= cparseTypeName();
964 check(TOK
.rightParenthesis
);
967 if (token
.value
== TOK
.leftCurly
)
969 // C11 6.5.2.5 ( type-name ) { initializer-list }
970 auto ci
= cparseInitializer();
971 auto ce
= new AST
.CompoundLiteralExp(loc
, t
, ci
);
972 return cparsePostfixOperators(ce
);
974 else if (t
.isTypeIdentifier() &&
975 token
.value
== TOK
.leftParenthesis
&&
976 !isCastExpression(pt
))
978 /* this might actually be a function
979 * call that looks like `(a)(b)` or even `(a)(b,c)`
981 auto ie
= new AST
.IdentifierExp(loc
, t
.isTypeIdentifier().ident
);
982 ie
.parens
= true; // disambiguate it from being a declaration
983 return new AST
.CallExp(loc
, ie
, cparseArguments());
987 // ( type-name ) cast-expression
988 auto ce
= cparseCastExp();
989 return new AST
.CastExp(loc
, ce
, t
);
993 return cparseUnaryExp();
998 * multiplicative-expression
1000 * multiplicative-expression * cast-expression
1001 * multiplicative-expression / cast-expression
1002 * multiplicative-expression % cast-expression
1004 private AST
.Expression
cparseMulExp()
1006 const loc
= token
.loc
;
1007 auto e
= cparseCastExp();
1011 switch (token
.value
)
1015 auto e2
= cparseCastExp();
1016 e
= new AST
.MulExp(loc
, e
, e2
);
1021 auto e2
= cparseCastExp();
1022 e
= new AST
.DivExp(loc
, e
, e2
);
1027 auto e2
= cparseCastExp();
1028 e
= new AST
.ModExp(loc
, e
, e2
);
1041 * additive-expression
1042 * multiplicative-expression
1043 * additive-expression + multiplicative-expression
1044 * additive-expression - multiplicative-expression
1046 private AST
.Expression
cparseAddExp()
1048 const loc
= token
.loc
;
1049 auto e
= cparseMulExp();
1053 switch (token
.value
)
1057 auto e2
= cparseMulExp();
1058 e
= new AST
.AddExp(loc
, e
, e2
);
1063 auto e2
= cparseMulExp();
1064 e
= new AST
.MinExp(loc
, e
, e2
);
1078 * additive-expression
1079 * shift-expression << additive-expression
1080 * shift-expression >> additive-expression
1082 private AST
.Expression
cparseShiftExp()
1084 const loc
= token
.loc
;
1085 auto e
= cparseAddExp();
1089 switch (token
.value
)
1093 auto e2
= cparseAddExp();
1094 e
= new AST
.ShlExp(loc
, e
, e2
);
1097 case TOK
.rightShift
:
1099 auto e2
= cparseAddExp();
1100 e
= new AST
.ShrExp(loc
, e
, e2
);
1113 * relational-expression
1115 * relational-expression < shift-expression
1116 * relational-expression > shift-expression
1117 * relational-expression <= shift-expression
1118 * relational-expression >= shift-expression
1120 private AST
.Expression
cparseRelationalExp()
1122 const loc
= token
.loc
;
1124 auto e
= cparseShiftExp();
1126 EXP op
= EXP
.reserved
;
1127 switch (token
.value
)
1129 case TOK
.lessThan
: op
= EXP
.lessThan
; goto Lcmp
;
1130 case TOK
.lessOrEqual
: op
= EXP
.lessOrEqual
; goto Lcmp
;
1131 case TOK
.greaterThan
: op
= EXP
.greaterThan
; goto Lcmp
;
1132 case TOK
.greaterOrEqual
: op
= EXP
.greaterOrEqual
; goto Lcmp
;
1135 auto e2
= cparseShiftExp();
1136 e
= new AST
.CmpExp(op
, loc
, e
, e2
);
1147 * equality-expression
1148 * relational-expression
1149 * equality-expression == relational-expression
1150 * equality-expression != relational-expression
1152 private AST
.Expression
cparseEqualityExp()
1154 const loc
= token
.loc
;
1156 auto e
= cparseRelationalExp();
1158 EXP op
= EXP
.reserved
;
1159 switch (token
.value
)
1161 case TOK
.equal
: op
= EXP
.equal
; goto Lequal
;
1162 case TOK
.notEqual
: op
= EXP
.notEqual
; goto Lequal
;
1165 auto e2
= cparseRelationalExp();
1166 e
= new AST
.EqualExp(op
, loc
, e
, e2
);
1178 * equality-expression
1179 * AND-expression & equality-expression
1181 private AST
.Expression
cparseAndExp()
1183 Loc loc
= token
.loc
;
1184 auto e
= cparseEqualityExp();
1185 while (token
.value
== TOK
.and)
1188 auto e2
= cparseEqualityExp();
1189 e
= new AST
.AndExp(loc
, e
, e2
);
1197 * exclusive-OR-expression
1199 * exclusive-OR-expression ^ AND-expression
1201 private AST
.Expression
cparseXorExp()
1203 const loc
= token
.loc
;
1205 auto e
= cparseAndExp();
1206 while (token
.value
== TOK
.xor)
1209 auto e2
= cparseAndExp();
1210 e
= new AST
.XorExp(loc
, e
, e2
);
1217 * inclusive-OR-expression
1218 * exclusive-OR-expression
1219 * inclusive-OR-expression | exclusive-OR-expression
1221 private AST
.Expression
cparseOrExp()
1223 const loc
= token
.loc
;
1225 auto e
= cparseXorExp();
1226 while (token
.value
== TOK
.or)
1229 auto e2
= cparseXorExp();
1230 e
= new AST
.OrExp(loc
, e
, e2
);
1237 * logical-AND-expression
1238 * inclusive-OR-expression
1239 * logical-AND-expression && inclusive-OR-expression
1241 private AST
.Expression
cparseAndAndExp()
1243 const loc
= token
.loc
;
1245 auto e
= cparseOrExp();
1246 while (token
.value
== TOK
.andAnd
)
1249 auto e2
= cparseOrExp();
1250 e
= new AST
.LogicalExp(loc
, EXP
.andAnd
, e
, e2
);
1257 * logical-OR-expression
1258 * logical-AND-expression
1259 * logical-OR-expression || logical-AND-expression
1261 private AST
.Expression
cparseOrOrExp()
1263 const loc
= token
.loc
;
1265 auto e
= cparseAndAndExp();
1266 while (token
.value
== TOK
.orOr
)
1269 auto e2
= cparseAndAndExp();
1270 e
= new AST
.LogicalExp(loc
, EXP
.orOr
, e
, e2
);
1277 * conditional-expression:
1278 * logical-OR-expression
1279 * logical-OR-expression ? expression : conditional-expression
1281 private AST
.Expression
cparseCondExp()
1283 const loc
= token
.loc
;
1285 auto e
= cparseOrOrExp();
1286 if (token
.value
== TOK
.question
)
1289 auto e1
= cparseExpression();
1291 auto e2
= cparseCondExp();
1292 e
= new AST
.CondExp(loc
, e
, e1
, e2
);
1299 * assignment-expression:
1300 * conditional-expression
1301 * unary-expression assignment-operator assignment-expression
1303 * assignment-operator:
1304 * = *= /= %= += -= <<= >>= &= ^= |=
1306 AST
.Expression
cparseAssignExp()
1309 e
= cparseCondExp(); // constrain it to being unary-expression in semantic pass
1313 const loc
= token
.loc
;
1314 switch (token
.value
)
1318 auto e2
= cparseAssignExp();
1319 e
= new AST
.AssignExp(loc
, e
, e2
);
1324 auto e2
= cparseAssignExp();
1325 e
= new AST
.AddAssignExp(loc
, e
, e2
);
1330 auto e2
= cparseAssignExp();
1331 e
= new AST
.MinAssignExp(loc
, e
, e2
);
1336 auto e2
= cparseAssignExp();
1337 e
= new AST
.MulAssignExp(loc
, e
, e2
);
1342 auto e2
= cparseAssignExp();
1343 e
= new AST
.DivAssignExp(loc
, e
, e2
);
1348 auto e2
= cparseAssignExp();
1349 e
= new AST
.ModAssignExp(loc
, e
, e2
);
1354 auto e2
= cparseAssignExp();
1355 e
= new AST
.AndAssignExp(loc
, e
, e2
);
1360 auto e2
= cparseAssignExp();
1361 e
= new AST
.OrAssignExp(loc
, e
, e2
);
1366 auto e2
= cparseAssignExp();
1367 e
= new AST
.XorAssignExp(loc
, e
, e2
);
1370 case TOK
.leftShiftAssign
:
1372 auto e2
= cparseAssignExp();
1373 e
= new AST
.ShlAssignExp(loc
, e
, e2
);
1376 case TOK
.rightShiftAssign
:
1378 auto e2
= cparseAssignExp();
1379 e
= new AST
.ShrAssignExp(loc
, e
, e2
);
1389 /***********************
1391 * _Generic ( assignment-expression, generic-assoc-list )
1393 * generic-assoc-list:
1394 * generic-association
1395 * generic-assoc-list generic-association
1397 * generic-association:
1398 * type-name : assignment-expression
1399 * default : assignment-expression
1401 private AST
.Expression
cparseGenericSelection()
1403 const loc
= token
.loc
;
1405 check(TOK
.leftParenthesis
);
1406 auto cntlExp
= cparseAssignExp();
1408 auto types
= new AST
.Types();
1409 auto exps
= new AST
.Expressions();
1414 if (token
.value
== TOK
.default_
)
1418 error("only one `default` allowed in generic-assoc-list");
1423 t
= cparseTypeName();
1427 auto e
= cparseAssignExp();
1429 if (token
.value
== TOK
.rightParenthesis || token
.value
== TOK
.endOfFile
)
1433 check(TOK
.rightParenthesis
);
1434 return new AST
.GenericExp(loc
, cntlExp
, types
, exps
);
1437 /***********************
1438 * C11 6.6 Constant expressions
1439 * constant-expression:
1440 * conditional-expression
1442 private AST
.Expression
cparseConstantExp()
1444 return cparseAssignExp();
1448 /********************************************************************************/
1449 /********************************* Declaration Parser ***************************/
1452 /*************************************
1455 * declaration-specifiers init-declarator-list (opt) ;
1456 * static_assert-declaration
1458 * init-declarator-list:
1460 * init-declarator-list , init-declarator
1464 * declarator = initializer
1467 * level = declaration context
1469 void cparseDeclaration(LVL level
)
1471 //printf("cparseDeclaration(level = %d)\n", level);
1472 if (token
.value
== TOK
._Static_assert
)
1474 auto s
= cparseStaticAssert();
1479 auto symbolsSave
= symbols
;
1480 Specifier specifier
;
1481 specifier
.packalign
= this.packalign
;
1482 auto tspec
= cparseDeclarationSpecifiers(level
, specifier
);
1484 /* If a declarator does not follow, it is unnamed
1486 if (token
.value
== TOK
.semicolon
&& tspec
)
1489 auto tt
= tspec
.isTypeTag();
1491 !tt
.id
&& (tt
.tok
== TOK
.struct_ || tt
.tok
== TOK
.union_
))
1492 return; // legal but meaningless empty declaration, ignore it
1494 /* `struct tag;` and `struct tag { ... };`
1495 * always result in a declaration in the current scope
1497 auto stag
= (tt
.tok
== TOK
.struct_
) ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false) :
1498 (tt
.tok
== TOK
.union_
) ?
new AST
.UnionDeclaration(tt
.loc
, tt
.id
) :
1499 new AST
.EnumDeclaration(tt
.loc
, tt
.id
, AST
.Type
.tint32
);
1500 stag
.members
= tt
.members
;
1502 symbols
= new AST
.Dsymbols();
1503 auto stags
= applySpecifier(stag
, specifier
);
1504 symbols
.push(stags
);
1506 if (tt
.tok
== TOK
.enum_
)
1509 error(tt
.loc
, "`enum %s` has no members", stag
.toChars());
1514 if (tspec
&& specifier
.mod
& MOD
.xconst
)
1516 tspec
= toConst(tspec
);
1517 specifier
.mod
= MOD
.xnone
; // 'used' it
1524 AST
.Expression asmname
;
1525 auto dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
1530 break; // error recovery
1535 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1536 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1538 switch (token
.value
)
1544 case TOK
.__attribute__
:
1545 /* This is a data definition, there cannot now be a
1546 * function definition.
1549 if (token
.value
== TOK
.asm_
)
1550 asmname
= cparseSimpleAsmExpr();
1551 if (token
.value
== TOK
.__attribute__
)
1553 cparseGnuAttributes(specifier
);
1554 if (token
.value
== TOK
.leftCurly
)
1556 error("attributes should be specified before the function definition");
1571 if (specifier
.alignExps
&& dt.isTypeFunction())
1572 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1573 if (specifier
.alignExps
&& specifier
.scw
== SCW
.xregister
)
1574 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1576 /* C11 6.9.1 Function Definitions
1577 * function-definition:
1578 * declaration-specifiers declarator declaration-list (opt) compound-statement
1582 * declaration-list declaration
1585 if (first
&& // first declarator
1587 dt.isTypeFunction() && // function type not inherited from a typedef
1588 isDeclarationList(t
) && // optional declaration-list
1589 level
== LVL
.global
&& // function definitions only at global scope
1590 t
.value
== TOK
.leftCurly
) // start of compound-statement
1592 auto s
= cparseFunctionDefinition(id
, dt.isTypeFunction(), specifier
);
1593 symbols
= symbolsSave
;
1597 AST
.Dsymbol s
= null;
1598 symbols
= symbolsSave
;
1600 symbols
= new AST
.Dsymbols
; // lazilly create it
1602 if (level
!= LVL
.global
&& !tspec
&& !specifier
.scw
&& !specifier
.mod
)
1603 error("declaration-specifier-seq required");
1604 else if (specifier
.scw
== SCW
.xtypedef
)
1606 if (token
.value
== TOK
.assign
)
1607 error("no initializer for typedef declaration");
1608 if (specifier
.alignExps
)
1609 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1611 bool isalias
= true;
1612 if (auto ts
= dt.isTypeStruct())
1614 if (ts
.sym
.isAnonymous())
1616 // This is a typedef for an anonymous struct-or-union.
1617 // Directly set the ident for the struct-or-union.
1622 else if (auto te
= dt.isTypeEnum())
1624 if (te
.sym
.isAnonymous())
1626 // This is a typedef for an anonymous enum.
1632 s
= new AST
.AliasDeclaration(token
.loc
, id
, dt);
1636 if (level
== LVL
.prototype
)
1637 break; // declared later as Parameter, not VarDeclaration
1639 if (dt.ty
== AST
.Tvoid
)
1640 error("`void` has no value");
1642 AST
.Initializer initializer
;
1643 bool hasInitializer
;
1644 if (token
.value
== TOK
.assign
)
1647 hasInitializer
= true;
1648 initializer
= cparseInitializer();
1650 // declare the symbol
1652 if (dt.isTypeFunction())
1655 error("no initializer for function declaration");
1656 if (specifier
.scw
& SCW
.x_Thread_local
)
1657 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1658 auto fd
= new AST
.FuncDeclaration(token
.loc
, Loc
.initial
, id
, specifiersToSTC(level
, specifier
), dt, specifier
.noreturn
);
1663 // Give non-extern variables an implicit void initializer
1664 // if one has not been explicitly set.
1665 if (!hasInitializer
&& !(specifier
.scw
& SCW
.xextern
))
1666 initializer
= new AST
.VoidInitializer(token
.loc
);
1667 s
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(level
, specifier
));
1672 s
= applySpecifier(s
, specifier
);
1673 if (level
== LVL
.local
)
1675 // Wrap the declaration in `extern (C) { declaration }`
1676 // Necessary for function pointers, but harmless to apply to all.
1677 auto decls
= new AST
.Dsymbols(1);
1679 s
= new AST
.LinkDeclaration(s
.loc
, linkage
, decls
);
1681 // Saw `asm("name")` in the function, type, or variable definition.
1682 // This maps directly to `pragma(mangle, "name")`
1685 auto args
= new AST
.Expressions(1);
1686 (*args
)[0] = asmname
;
1687 auto decls
= new AST
.Dsymbols(1);
1689 s
= new AST
.PragmaDeclaration(asmname
.loc
, Id
.mangle
, args
, decls
);
1695 switch (token
.value
)
1697 case TOK
.identifier
:
1700 error("missing comma or semicolon after declaration of `%s`, found `%s` instead", s
.toChars(), token
.toChars());
1711 symbolsSave
= symbols
;
1716 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token
.toChars());
1718 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
1726 /***************************************
1727 * C11 Function Definitions
1728 * function-definition
1729 * declaration-specifiers declarator declaration-list (opt) compound-statement
1733 * declaration-list declaration
1735 * It's already been parsed up to the declaration-list (opt).
1736 * Pick it up from there.
1738 * id = function identifier
1739 * ft = function type
1740 * specifier = function specifiers
1742 * Dsymbol for the function
1744 AST
.Dsymbol
cparseFunctionDefinition(Identifier id
, AST
.TypeFunction ft
, ref Specifier specifier
)
1746 if (token
.value
!= TOK
.leftCurly
) // if not start of a compound-statement
1748 // Do declaration-list
1751 cparseDeclaration(LVL
.parameter
);
1752 } while (token
.value
!= TOK
.leftCurly
);
1754 /* Since there were declarations, the parameter-list must have been
1755 * an identifier-list.
1757 auto pl
= ft
.parameterList
;
1758 pl
.hasIdentifierList
= true; // semantic needs to know to adjust parameter types
1759 if (pl
.varargs
!= AST
.VarArg
.none
&& pl
.length
)
1760 error("function identifier-list cannot end with `...`");
1761 ft
.parameterList
.varargs
= AST
.VarArg
.variadic
; // but C11 allows extra arguments
1762 auto plLength
= pl
.length
;
1763 if (symbols
.length
!= plLength
)
1764 error("%d identifiers does not match %d declarations", cast(int)plLength
, cast(int)symbols
.length
);
1766 /* Transfer the types and storage classes from symbols[] to pl[]
1768 foreach (i
; 0 .. plLength
)
1770 auto p
= pl
[i
]; // yes, quadratic
1772 // Convert typedef-identifier to identifier
1775 if (auto t
= p
.type
.isTypeIdentifier())
1782 if (p
.type ||
!(p
.storageClass
& STC
.parameter
))
1783 error("storage class and type are not allowed in identifier-list");
1784 foreach (s
; (*symbols
)[]) // yes, quadratic
1786 auto d
= s
.isDeclaration();
1787 if (d
&& p
.ident
== d
.ident
&& d
.type
)
1790 p
.storageClass
= d
.storage_class
;
1791 d
.type
= null; // don't reuse
1797 error("no declaration for identifier `%s`", p
.ident
.toChars());
1798 p
.type
= AST
.Type
.terror
;
1803 addFuncName
= false; // gets set to true if somebody references __func__ in this function
1804 const locFunc
= token
.loc
;
1806 auto body = cparseStatement(ParseStatementFlags
.curly
); // don't start a new scope; continue with parameter scope
1807 auto fd
= new AST
.FuncDeclaration(locFunc
, prevloc
, id
, specifiersToSTC(LVL
.global
, specifier
), ft
, specifier
.noreturn
);
1811 auto s
= createFuncName(locFunc
, id
);
1812 body = new AST
.CompoundStatement(locFunc
, s
, body);
1816 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
1821 /***************************************
1822 * C11 Initialization
1824 * assignment-expression
1825 * { initializer-list }
1826 * { initializer-list , }
1829 * designation (opt) initializer
1830 * initializer-list , designation (opt) initializer
1837 * designator-list designator
1840 * [ constant-expression ]
1845 AST
.Initializer
cparseInitializer()
1847 if (token
.value
!= TOK
.leftCurly
)
1849 auto ae
= cparseAssignExp(); // assignment-expression
1850 return new AST
.ExpInitializer(token
.loc
, ae
);
1853 const loc
= token
.loc
;
1855 /* Collect one or more `designation (opt) initializer`
1856 * into ci.initializerList, but lazily create ci
1858 AST
.CInitializer ci
;
1861 /* There can be 0 or more designators preceding an initializer.
1862 * Collect them in desigInit
1864 AST
.DesigInit desigInit
;
1867 if (token
.value
== TOK
.leftBracket
) // [ constant-expression ]
1870 auto e
= cparseConstantExp();
1871 check(TOK
.rightBracket
);
1872 if (!desigInit
.designatorList
)
1873 desigInit
.designatorList
= new AST
.Designators
;
1874 desigInit
.designatorList
.push(AST
.Designator(e
));
1876 else if (token
.value
== TOK
.dot
) // . identifier
1879 if (token
.value
!= TOK
.identifier
)
1881 error("identifier expected following `.` designator");
1884 if (!desigInit
.designatorList
)
1885 desigInit
.designatorList
= new AST
.Designators
;
1886 desigInit
.designatorList
.push(AST
.Designator(token
.ident
));
1891 if (desigInit
.designatorList
)
1897 desigInit
.initializer
= cparseInitializer();
1899 ci
= new AST
.CInitializer(loc
);
1900 ci
.initializerList
.push(desigInit
);
1901 if (token
.value
== TOK
.comma
)
1904 if (token
.value
!= TOK
.rightCurly
)
1909 check(TOK
.rightCurly
);
1910 //printf("ci: %s\n", ci.toChars());
1914 /*************************************
1916 * declaration-specifier:
1917 * storage-class-specifier declaration-specifiers (opt)
1918 * type-specifier declaration-specifiers (opt)
1919 * type-qualifier declaration-specifiers (opt)
1920 * function-specifier declaration-specifiers (opt)
1921 * alignment-specifier declaration-specifiers (opt)
1923 * level = declaration context
1924 * specifier = specifiers in and out
1926 * resulting type, null if not specified
1928 private AST
.Type
cparseDeclarationSpecifiers(LVL level
, ref Specifier specifier
)
1947 ximaginary
= 0x8000,
1954 //printf("parseDeclarationSpecifiers()\n");
1957 SCW scw
= specifier
.scw
& SCW
.xtypedef
;
1965 //printf("token %s\n", token.toChars());
1969 switch (token
.value
)
1971 // Storage class specifiers
1972 case TOK
.static_
: scwx
= SCW
.xstatic
; break;
1973 case TOK
.extern_
: scwx
= SCW
.xextern
; break;
1974 case TOK
.auto_
: scwx
= SCW
.xauto
; break;
1975 case TOK
.register
: scwx
= SCW
.xregister
; break;
1976 case TOK
.typedef_
: scwx
= SCW
.xtypedef
; break;
1977 case TOK
.inline
: scwx
= SCW
.xinline
; break;
1978 case TOK
._Noreturn
: scwx
= SCW
.x_Noreturn
; break;
1979 case TOK
._Thread_local
: scwx
= SCW
.x_Thread_local
; break;
1982 case TOK
.const_
: modx
= MOD
.xconst
; break;
1983 case TOK
.volatile: modx
= MOD
.xvolatile
; break;
1984 case TOK
.restrict
: modx
= MOD
.xrestrict
; break;
1987 case TOK
.char_
: tkwx
= TKW
.xchar
; break;
1988 case TOK
.signed
: tkwx
= TKW
.xsigned
; break;
1989 case TOK
.unsigned
: tkwx
= TKW
.xunsigned
; break;
1990 case TOK
.int16
: tkwx
= TKW
.xshort
; break;
1991 case TOK
.int32
: tkwx
= TKW
.xint
; break;
1992 case TOK
.int64
: tkwx
= TKW
.xlong
; break;
1993 case TOK
.float32
: tkwx
= TKW
.xfloat
; break;
1994 case TOK
.float64
: tkwx
= TKW
.xdouble
; break;
1995 case TOK
.void_
: tkwx
= TKW
.xvoid
; break;
1996 case TOK
._Bool
: tkwx
= TKW
.xbool
; break;
1997 case TOK
._Imaginary
: tkwx
= TKW
.ximaginary
; break;
1998 case TOK
._Complex
: tkwx
= TKW
.xcomplex
; break;
2000 case TOK
.identifier
:
2008 const structOrUnion
= token
.value
;
2009 const sloc
= token
.loc
;
2013 * struct-or-union-specifier:
2014 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2015 * struct-or-union gnu-attribute (opt) identifier
2017 if (token
.value
== TOK
.__attribute__
)
2018 cparseGnuAttributes(specifier
);
2020 t
= cparseStruct(sloc
, structOrUnion
, symbols
);
2026 t
= cparseEnum(symbols
);
2033 // type-specifier if followed by `( type-name )`
2034 auto tk
= peek(&token
);
2035 if (tk
.value
== TOK
.leftParenthesis
)
2038 if (isTypeName(tk
) && tk
.value
== TOK
.rightParenthesis
)
2041 t
= cparseTypeName();
2042 // TODO - implement the "atomic" part of t
2043 tkwx
= TKW
.x_Atomic
;
2047 // C11 6.7.3 type-qualifier if not
2048 modx
= MOD
.x_Atomic
;
2055 * _Alignas ( type-name )
2056 * _Alignas ( constant-expression )
2059 if (level
& (LVL
.parameter | LVL
.prototype
))
2060 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2063 check(TOK
.leftParenthesis
);
2066 if (isTypeName(tk
)) // _Alignas ( type-name )
2068 auto talign
= cparseTypeName();
2069 /* Convert type to expression: `talign.alignof`
2071 auto e
= new AST
.TypeExp(loc
, talign
);
2072 exp
= new AST
.DotIdExp(loc
, e
, Id
.__xalignof
);
2074 else // _Alignas ( constant-expression )
2076 exp
= cparseConstantExp();
2079 if (!specifier
.alignExps
)
2080 specifier
.alignExps
= new AST
.Expressions(0);
2081 specifier
.alignExps
.push(exp
);
2083 check(TOK
.rightParenthesis
);
2087 case TOK
.__attribute__
:
2090 * declaration-specifiers:
2091 * gnu-attributes declaration-specifiers (opt)
2093 cparseGnuAttributes(specifier
);
2103 if (tkw
& TKW
.xlong
&& tkwx
& TKW
.xlong
)
2108 if (tkw
&& tkwx
& TKW
.xident
)
2110 // 2nd identifier can't be a typedef
2111 break Lwhile
; // leave parser on the identifier for the following declarator
2113 else if (tkwx
& TKW
.xident
)
2115 // 1st identifier, save it for TypeIdentifier
2118 if (tkw
& TKW
.xident
&& tkwx ||
// typedef-name followed by type-specifier
2119 tkw
& tkwx
) // duplicate type-specifiers
2121 error("illegal combination of type specifiers");
2125 if (!(tkwx
& TKW
.xtag
)) // if parser already advanced
2140 error("duplicate storage class");
2142 const scw2
= scw
& (SCW
.xstatic | SCW
.xextern | SCW
.xauto | SCW
.xregister | SCW
.xtypedef
);
2143 if (scw2
& (scw2
- 1) ||
2144 scw
& (SCW
.xauto | SCW
.xregister
) && scw
& (SCW
.xinline | SCW
.x_Noreturn
))
2146 error("conflicting storage class");
2149 if (level
& (LVL
.parameter | LVL
.prototype
) &&
2150 scw
& ~SCW
.xregister
)
2152 error("only `register` storage class allowed for function parameters");
2155 if (level
== LVL
.global
&&
2156 scw
& (SCW
.xauto | SCW
.xregister
))
2158 error("`auto` and `register` storage class not allowed for global");
2166 specifier
.scw
= scw
;
2167 specifier
.mod
= mod
;
2169 // Convert TKW bits to type t
2172 case TKW
.xnone
: t
= null; break;
2174 case TKW
.xchar
: t
= AST
.Type
.tchar
; break;
2175 case TKW
.xsigned | TKW
.xchar
: t
= AST
.Type
.tint8
; break;
2176 case TKW
.xunsigned | TKW
.xchar
: t
= AST
.Type
.tuns8
; break;
2179 case TKW
.xsigned | TKW
.xshort
:
2180 case TKW
.xsigned | TKW
.xshort | TKW
.xint
:
2181 case TKW
.xshort | TKW
.xint
: t
= AST
.Type
.tint16
; break;
2183 case TKW
.xunsigned | TKW
.xshort | TKW
.xint
:
2184 case TKW
.xunsigned | TKW
.xshort
: t
= AST
.Type
.tuns16
; break;
2188 case TKW
.xsigned | TKW
.xint
: t
= AST
.Type
.tint32
; break;
2191 case TKW
.xunsigned | TKW
.xint
: t
= AST
.Type
.tuns32
; break;
2194 case TKW
.xsigned | TKW
.xlong
:
2195 case TKW
.xsigned | TKW
.xlong | TKW
.xint
:
2196 case TKW
.xlong | TKW
.xint
: t
= longsize
== 4 ? AST
.Type
.tint32
: AST
.Type
.tint64
; break;
2198 case TKW
.xunsigned | TKW
.xlong | TKW
.xint
:
2199 case TKW
.xunsigned | TKW
.xlong
: t
= longsize
== 4 ? AST
.Type
.tuns32
: AST
.Type
.tuns64
; break;
2202 case TKW
.xsigned | TKW
.xllong
:
2203 case TKW
.xsigned | TKW
.xllong | TKW
.xint
:
2204 case TKW
.xllong | TKW
.xint
: t
= AST
.Type
.tint64
; break;
2206 case TKW
.xunsigned | TKW
.xllong | TKW
.xint
:
2207 case TKW
.xunsigned | TKW
.xllong
: t
= AST
.Type
.tuns64
; break;
2209 case TKW
.xvoid
: t
= AST
.Type
.tvoid
; break;
2210 case TKW
.xbool
: t
= AST
.Type
.tbool
; break;
2212 case TKW
.xfloat
: t
= AST
.Type
.tfloat32
; break;
2213 case TKW
.xdouble
: t
= AST
.Type
.tfloat64
; break;
2214 case TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.realfloat
); break;
2216 case TKW
.ximaginary | TKW
.xfloat
: t
= AST
.Type
.timaginary32
; break;
2217 case TKW
.ximaginary | TKW
.xdouble
: t
= AST
.Type
.timaginary64
; break;
2218 case TKW
.ximaginary | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.imaginary
); break;
2220 case TKW
.xcomplex | TKW
.xfloat
: t
= AST
.Type
.tcomplex32
; break;
2221 case TKW
.xcomplex | TKW
.xdouble
: t
= AST
.Type
.tcomplex64
; break;
2222 case TKW
.xcomplex | TKW
.xlong | TKW
.xdouble
: t
= realType(RTFlags
.complex
); break;
2224 case TKW
.xident
: t
= new AST
.TypeIdentifier(loc
, previd
);
2228 break; // t is already set
2231 error("illegal type combination");
2232 t
= AST
.Type
.terror
;
2239 /********************************
2241 * Parse a declarator (including function definitions).
2243 * pointer (opt) direct-declarator
2245 * direct-declarator :
2248 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2249 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2250 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2251 * direct-declarator [ type-qualifier-list (opt) * ]
2252 * direct-declarator ( parameter-type-list )
2253 * direct-declarator ( identifier-list (opt) )
2256 * * type-qualifier-list (opt)
2257 * * type-qualifier-list (opt) pointer
2259 * type-qualifier-list :
2261 * type-qualifier-list type-qualifier
2263 * parameter-type-list :
2265 * parameter-list , ...
2268 * parameter-declaration
2269 * parameter-list , parameter-declaration
2271 * parameter-declaration :
2272 * declaration-specifiers declarator
2273 * declaration-specifiers abstract-declarator (opt)
2277 * identifier-list , identifier
2280 * declarator = declarator kind
2281 * t = base type to start with
2282 * pident = set to Identifier if there is one, null if not
2283 * specifier = specifiers in and out
2285 * type declared. If a TypeFunction is returned, this.symbols is the
2286 * symbol table for the parameter-type-list, which will contain any
2287 * declared struct, union or enum tags.
2289 private AST
.Type
cparseDeclarator(DTR declarator
, AST
.Type t
,
2290 out Identifier pident
, ref Specifier specifier
)
2292 //printf("cparseDeclarator(%d)\n", declarator);
2293 AST
.Types constTypes
; // all the Types that will need `const` applied to them
2294 constTypes
.setDim(0);
2296 AST
.Type
parseDecl(AST
.Type t
)
2301 switch (token
.value
)
2303 case TOK
.identifier
: // identifier
2304 //printf("identifier %s\n", token.ident.toChars());
2305 if (declarator
== DTR
.xabstract
)
2306 error("identifier not allowed in abstract-declarator");
2307 pident
= token
.ident
;
2312 case TOK
.leftParenthesis
: // ( declarator )
2318 check(TOK
.rightParenthesis
);
2321 case TOK
.mul: // pointer
2322 t
= new AST
.TypePointer(t
);
2324 // add post fixes const/volatile/restrict/_Atomic
2325 const mod
= cparseTypeQualifierList();
2326 if (mod
& MOD
.xconst
)
2328 if (token
.value
== TOK
.__attribute__
)
2329 cparseGnuAttributes(specifier
);
2333 if (declarator
== DTR
.xdirect
)
2335 error("identifier or `(` expected"); // )
2344 // parse DeclaratorSuffixes
2347 /* Insert tx -> t into
2350 * ts -> ... -> tx -> t
2352 static void insertTx(ref AST
.Type ts
, AST
.Type tx
, AST
.Type t
)
2355 for (pt
= &ts
; *pt
!= t
; pt
= &(cast(AST
.TypeNext
)*pt
).next
)
2361 switch (token
.value
)
2363 case TOK
.leftBracket
:
2365 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2369 auto mod
= cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2373 if (token
.value
== TOK
.static_
)
2375 isStatic
= true; // `static`
2377 if (!mod
) // type qualifiers after `static`
2378 mod
= cparseTypeQualifierList();
2380 else if (token
.value
== TOK
.mul)
2382 if (peekNext() == TOK
.rightBracket
)
2384 isVLA
= true; // `*`
2389 if (isStatic || token
.value
!= TOK
.rightBracket
)
2391 //printf("It's a static array\n");
2392 AST
.Expression e
= cparseAssignExp(); // [ expression ]
2393 ta
= new AST
.TypeSArray(t
, e
);
2397 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2399 ta
= new AST
.TypeSArray(t
);
2401 check(TOK
.rightBracket
);
2403 // Issue errors for unsupported types.
2404 if (isVLA
) // C11 6.7.6.2
2406 error("variable length arrays are not supported");
2408 if (isStatic
) // C11 6.7.6.3
2410 error("static array parameters are not supported");
2412 if (declarator
!= DTR
.xparameter
)
2414 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2417 error("variable length array used outside of function prototype");
2418 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2419 * in a declaration of a function parameter with an array type.
2421 if (isStatic || mod
)
2422 error("static or type qualifier used outside of function prototype");
2424 if (ts
.isTypeSArray() || ts
.isTypeDArray())
2426 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2427 * in the outermost array type derivation.
2429 if (isStatic || mod
)
2430 error("static or type qualifier used in non-outermost array type derivation");
2431 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2434 if (ta
.isTypeSArray() && ta
.isTypeSArray().isIncomplete() && !isVLA
)
2435 error("array type has incomplete element type `%s`", ta
.toChars());
2438 // Apply type qualifiers to the constructed type.
2439 if (mod
& MOD
.xconst
) // ignore the other bits
2441 insertTx(ts
, ta
, t
); // ts -> ... -> ta -> t
2445 case TOK
.leftParenthesis
:
2447 // New symbol table for parameter-list
2448 auto symbolsSave
= this.symbols
;
2449 this.symbols
= null;
2451 auto parameterList
= cparseParameterList();
2452 AST
.Type tf
= new AST
.TypeFunction(parameterList
, t
, linkage
, 0);
2453 // tf = tf.addSTC(storageClass); // TODO
2454 insertTx(ts
, tf
, t
); // ts -> ... -> tf -> t
2457 this.symbols
= symbolsSave
;
2471 /* Because const is transitive, cannot assemble types from
2472 * fragments. Instead, types to be annotated with const are put
2473 * in constTypes[], and a bottom up scan of t is done to apply
2476 if (constTypes
.length
)
2478 AST
.Type
constApply(AST
.Type t
)
2482 auto tn
= cast(AST
.TypeNext
)t
; // t.nextOf() should return a ref instead of this
2483 tn
.next
= constApply(tn
.next
);
2485 foreach (tc
; constTypes
[])
2498 //printf("result: %s\n", t.toChars());
2502 /******************************
2510 MOD
cparseTypeQualifierList()
2515 switch (token
.value
)
2517 case TOK
.const_
: mod |
= MOD
.xconst
; break;
2518 case TOK
.volatile: mod |
= MOD
.xvolatile
; break;
2519 case TOK
.restrict
: mod |
= MOD
.xrestrict
; break;
2520 case TOK
._Atomic
: mod |
= MOD
.x_Atomic
; break;
2529 /***********************************
2532 AST
.Type
cparseTypeName()
2534 Specifier specifier
;
2535 specifier
.packalign
.setDefault();
2536 auto tspec
= cparseSpecifierQualifierList(LVL
.global
, specifier
);
2538 return cparseDeclarator(DTR
.xabstract
, tspec
, id
, specifier
);
2541 /***********************************
2543 * specifier-qualifier-list:
2544 * type-specifier specifier-qualifier-list (opt)
2545 * type-qualifier specifier-qualifier-list (opt)
2547 * level = declaration context
2548 * specifier = specifiers in and out
2550 * resulting type, null if not specified
2552 AST
.Type
cparseSpecifierQualifierList(LVL level
, ref Specifier specifier
)
2554 auto t
= cparseDeclarationSpecifiers(level
, specifier
);
2556 error("storage class not allowed in specifier-qualified-list");
2560 /***********************************
2562 * ( parameter-type-list )
2563 * ( identifier-list (opt) )
2565 AST
.ParameterList
cparseParameterList()
2567 auto parameters
= new AST
.Parameters();
2568 AST
.VarArg varargs
= AST
.VarArg
.none
;
2569 StorageClass varargsStc
;
2571 check(TOK
.leftParenthesis
);
2572 if (token
.value
== TOK
.void_
&& peekNext() == TOK
.rightParenthesis
) // func(void)
2576 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
2579 if (token
.value
== TOK
.rightParenthesis
) // func()
2582 return AST
.ParameterList(parameters
, AST
.VarArg
.variadic
, varargsStc
);
2585 /* The check for identifier-list comes later,
2586 * when doing the trailing declaration-list (opt)
2590 if (token
.value
== TOK
.rightParenthesis
)
2592 if (token
.value
== TOK
.dotDotDot
)
2594 if (parameters
.length
== 0) // func(...)
2595 error("named parameter required before `...`");
2596 varargs
= AST
.VarArg
.variadic
; // C-style variadics
2598 check(TOK
.rightParenthesis
);
2599 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
2602 Specifier specifier
;
2603 specifier
.packalign
.setDefault();
2604 auto tspec
= cparseDeclarationSpecifiers(LVL
.prototype
, specifier
);
2605 if (tspec
&& specifier
.mod
& MOD
.xconst
)
2607 tspec
= toConst(tspec
);
2608 specifier
.mod
= MOD
.xnone
; // 'used' it
2612 auto t
= cparseDeclarator(DTR
.xparameter
, tspec
, id
, specifier
);
2613 if (specifier
.mod
& MOD
.xconst
)
2615 auto param
= new AST
.Parameter(STC
.parameter
, t
, id
, null, null);
2616 parameters
.push(param
);
2617 if (token
.value
== TOK
.rightParenthesis
)
2622 return AST
.ParameterList(parameters
, varargs
, varargsStc
);
2625 /***********************************
2627 * _Static_assert ( constant-expression , string-literal ) ;
2629 private AST
.StaticAssert
cparseStaticAssert()
2631 const loc
= token
.loc
;
2633 //printf("cparseStaticAssert()\n");
2635 check(TOK
.leftParenthesis
);
2636 auto exp
= cparseConstantExp();
2638 if (token
.value
!= TOK
.string_
)
2639 error("string literal expected");
2640 auto msg
= cparsePrimaryExp();
2641 check(TOK
.rightParenthesis
);
2642 check(TOK
.semicolon
);
2643 return new AST
.StaticAssert(loc
, exp
, msg
);
2646 /*************************
2647 * Collect argument list.
2648 * Parser is on opening parenthesis.
2652 private AST
.Expressions
* cparseArguments()
2655 auto arguments
= new AST
.Expressions();
2656 while (token
.value
!= TOK
.rightParenthesis
&& token
.value
!= TOK
.endOfFile
)
2658 auto arg
= cparseAssignExp();
2659 arguments
.push(arg
);
2660 if (token
.value
!= TOK
.comma
)
2663 nextToken(); // consume comma
2666 check(TOK
.rightParenthesis
);
2671 /*************************
2673 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
2675 * __declspec ( extended-decl-modifier-seq )
2677 * extended-decl-modifier-seq:
2678 * extended-decl-modifier (opt)
2679 * extended-decl-modifier extended-decl-modifier-seq
2681 * extended-decl-modifier:
2685 private void cparseDeclspec()
2687 /* Check for dllexport, dllimport
2690 bool dllimport
; // TODO implement
2691 bool dllexport
; // TODO implement
2692 nextToken(); // move past __declspec
2693 check(TOK
.leftParenthesis
);
2696 if (token
.value
== TOK
.rightParenthesis
)
2701 else if (token
.value
== TOK
.endOfFile
)
2703 else if (token
.value
== TOK
.identifier
)
2705 if (token
.ident
== Id
.dllimport
)
2710 else if (token
.ident
== Id
.dllexport
)
2718 if (token
.value
== TOK
.leftParenthesis
)
2724 error("extended-decl-modifier expected");
2730 /*************************
2732 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
2734 * asm ( asm-string-literal )
2736 * asm-string-literal:
2739 private AST
.Expression
cparseSimpleAsmExpr()
2741 nextToken(); // move past asm
2742 check(TOK
.leftParenthesis
);
2743 if (token
.value
!= TOK
.string_
)
2744 error("string literal expected");
2745 auto label
= cparsePrimaryExp();
2746 check(TOK
.rightParenthesis
);
2750 /*************************
2751 * __attribute__ parser
2752 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
2754 * gnu-attributes gnu-attribute-specifier
2756 * gnu-attribute-specifier:
2757 * __attribute__ (( gnu-attribute-list ))
2759 * gnu-attribute-list:
2760 * gnu-attribute (opt)
2761 * gnu-attribute-list , gnu-attribute
2764 * specifier = filled in with the attribute(s)
2766 private void cparseGnuAttributes(ref Specifier specifier
)
2768 while (token
.value
== TOK
.__attribute__
)
2770 nextToken(); // move past __attribute__
2771 check(TOK
.leftParenthesis
);
2772 check(TOK
.leftParenthesis
);
2774 if (token
.value
!= TOK
.rightParenthesis
)
2778 cparseGnuAttribute(specifier
);
2779 if (token
.value
!= TOK
.comma
)
2785 check(TOK
.rightParenthesis
);
2786 check(TOK
.rightParenthesis
);
2790 /*************************
2791 * Parse a single GNU attribute
2793 * gnu-attribute-name
2794 * gnu-attribute-name ( identifier )
2795 * gnu-attribute-name ( identifier , expression-list )
2796 * gnu-attribute-name ( expression-list (opt) )
2798 * gnu-attribute-name:
2803 * constant-expression
2804 * expression-list , constant-expression
2807 * specifier = filled in with the attribute(s)
2809 private void cparseGnuAttribute(ref Specifier specifier
)
2811 /* Check for dllimport, dllexport, vector_size(bytes)
2814 bool dllimport
; // TODO implement
2815 bool dllexport
; // TODO implement
2817 if (!isGnuAttributeName())
2820 if (token
.value
== TOK
.identifier
)
2822 if (token
.ident
== Id
.dllimport
)
2827 else if (token
.ident
== Id
.dllexport
)
2832 else if (token
.ident
== Id
.noreturn
)
2834 specifier
.noreturn
= true;
2837 else if (token
.ident
== Id
.vector_size
)
2840 check(TOK
.leftParenthesis
);
2841 cparseConstantExp(); // TODO implement
2842 check(TOK
.rightParenthesis
);
2847 if (token
.value
== TOK
.leftParenthesis
)
2854 if (token
.value
== TOK
.leftParenthesis
)
2859 /*************************
2860 * See if match for GNU attribute name, which may be any identifier,
2861 * storage-class-specifier, type-specifier, or type-qualifier.
2863 * true if a valid GNU attribute name
2865 private bool isGnuAttributeName()
2867 switch (token
.value
)
2869 case TOK
.identifier
:
2885 case TOK
._Thread_local
:
2900 /***************************
2901 * Like skipParens(), but consume the tokens.
2903 private void cparseParens()
2905 check(TOK
.leftParenthesis
);
2910 switch (token
.value
)
2912 case TOK
.leftParenthesis
:
2916 case TOK
.rightParenthesis
:
2920 error("extra right parenthesis");
2931 error("end of file found before right parenthesis");
2942 /******************************************************************************/
2943 /***************************** Struct & Enum Parser ***************************/
2946 /*************************************
2949 * enum identifier (opt) { enumerator-list }
2950 * enum identifier (opt) { enumerator-list , }
2955 * enumerator-list , enumerator
2958 * enumeration-constant
2959 * enumeration-constant = constant-expression
2961 * enumeration-constant:
2965 * symbols = symbols to add enum declaration to
2969 private AST
.Type
cparseEnum(ref AST
.Dsymbols
* symbols
)
2971 const loc
= token
.loc
;
2976 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
2977 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
2978 * enum gnu-attributes (opt) identifier
2980 Specifier specifier
;
2981 specifier
.packalign
.setDefault();
2982 if (token
.value
== TOK
.__attribute__
)
2983 cparseGnuAttributes(specifier
);
2986 if (token
.value
== TOK
.identifier
)
2992 AST
.Dsymbols
* members
;
2993 if (token
.value
== TOK
.leftCurly
)
2996 members
= new AST
.Dsymbols();
2998 if (token
.value
== TOK
.rightCurly
) // C11 6.7.2.2-1
3001 error("no members for `enum %s`", tag
.toChars());
3003 error("no members for anonymous enum");
3006 while (token
.value
== TOK
.identifier
)
3008 auto ident
= token
.ident
; // enumeration-constant
3010 auto mloc
= token
.loc
;
3012 if (token
.value
== TOK
.__attribute__
)
3014 /* gnu-attributes can appear here, but just scan and ignore them
3015 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3017 Specifier specifierx
;
3018 specifierx
.packalign
.setDefault();
3019 cparseGnuAttributes(specifierx
);
3022 AST
.Expression value
;
3023 if (token
.value
== TOK
.assign
)
3026 value
= cparseConstantExp();
3027 // TODO C11 6.7.2.2-2 value must fit into an int
3030 if (token
.value
== TOK
.__attribute__
)
3032 /* gnu-attributes can appear here, but just scan and ignore them
3033 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3035 Specifier specifierx
;
3036 specifierx
.packalign
.setDefault();
3037 cparseGnuAttributes(specifierx
);
3040 auto em
= new AST
.EnumMember(mloc
, ident
, value
, null, 0, null, null);
3043 if (token
.value
== TOK
.comma
)
3050 check(TOK
.rightCurly
);
3053 * Parse the postfix gnu-attributes (opt)
3055 if (token
.value
== TOK
.__attribute__
)
3056 cparseGnuAttributes(specifier
);
3059 error("missing `identifier` after `enum`");
3061 /* Need semantic information to determine if this is a declaration,
3062 * redeclaration, or reference to existing declaration.
3063 * Defer to the semantic() pass with a TypeTag.
3065 return new AST
.TypeTag(loc
, TOK
.enum_
, tag
, members
);
3068 /*************************************
3070 * Parse struct and union specifiers.
3071 * Parser is advanced to the tag identifier or brace.
3072 * struct-or-union-specifier:
3073 * struct-or-union identifier (opt) { struct-declaration-list }
3074 * struct-or-union identifier
3080 * struct-declaration-list:
3081 * struct-declaration
3082 * struct-declaration-list struct-declaration
3085 * loc = location of `struct` or `union`
3086 * structOrUnion = TOK.struct_ or TOK.union_
3087 * symbols = symbols to add struct-or-union declaration to
3089 * type of the struct
3091 private AST
.Type
cparseStruct(Loc loc
, TOK structOrUnion
, ref AST
.Dsymbols
* symbols
)
3095 if (token
.value
== TOK
.identifier
)
3101 AST
.Dsymbols
* members
;
3102 if (token
.value
== TOK
.leftCurly
)
3105 auto symbolsSave
= symbols
;
3106 symbols
= new AST
.Dsymbols();
3107 while (token
.value
!= TOK
.rightCurly
)
3109 cparseStructDeclaration();
3111 if (token
.value
== TOK
.endOfFile
)
3114 members
= symbols
; // `members` will be non-null even with 0 members
3115 symbols
= symbolsSave
;
3116 check(TOK
.rightCurly
);
3118 if ((*members
).length
== 0) // C11 6.7.2.1-8
3120 /* allow empty structs as an extension
3121 * struct-declarator-list:
3122 * struct-declarator (opt)
3127 error("missing tag `identifier` after `%s`", Token
.toChars(structOrUnion
));
3129 /* Need semantic information to determine if this is a declaration,
3130 * redeclaration, or reference to existing declaration.
3131 * Defer to the semantic() pass with a TypeTag.
3133 return new AST
.TypeTag(loc
, structOrUnion
, tag
, members
);
3136 /*************************************
3138 * Parse a struct declaration member.
3139 * struct-declaration:
3140 * specifier-qualifier-list struct-declarator-list (opt) ;
3141 * static_assert-declaration
3143 * struct-declarator-list:
3145 * struct-declarator-list , struct-declarator
3147 * struct-declarator:
3149 * declarator (opt) : constant-expression
3151 void cparseStructDeclaration()
3153 //printf("cparseStructDeclaration()\n");
3154 if (token
.value
== TOK
._Static_assert
)
3156 auto s
= cparseStaticAssert();
3161 auto symbolsSave
= symbols
;
3162 Specifier specifier
;
3163 specifier
.packalign
= this.packalign
;
3164 auto tspec
= cparseSpecifierQualifierList(LVL
.member
, specifier
);
3165 if (tspec
&& specifier
.mod
& MOD
.xconst
)
3167 tspec
= toConst(tspec
);
3168 specifier
.mod
= MOD
.xnone
; // 'used' it
3171 /* If a declarator does not follow, it is unnamed
3173 if (token
.value
== TOK
.semicolon
&& tspec
)
3176 auto tt
= tspec
.isTypeTag();
3178 return; // legal but meaningless empty declaration
3180 /* If anonymous struct declaration
3181 * struct { ... members ... };
3184 if (!tt
.id
&& tt
.members
)
3186 /* members of anonymous struct are considered members of
3187 * the containing struct
3189 auto ad
= new AST
.AnonDeclaration(tt
.loc
, tt
.tok
== TOK
.union_
, tt
.members
);
3191 symbols
= new AST
.Dsymbols();
3192 auto s
= applySpecifier(ad
, specifier
);
3196 if (!tt
.id
&& !tt
.members
)
3197 return; // already gave error in cparseStruct()
3199 /* `struct tag;` and `struct tag { ... };`
3200 * always result in a declaration in the current scope
3202 // TODO: merge in specifier
3203 auto stag
= (tt
.tok
== TOK
.struct_
)
3204 ?
new AST
.StructDeclaration(tt
.loc
, tt
.id
, false)
3205 : new AST
.UnionDeclaration(tt
.loc
, tt
.id
);
3206 stag
.members
= tt
.members
;
3208 symbols
= new AST
.Dsymbols();
3209 auto s
= applySpecifier(stag
, specifier
);
3218 if (token
.value
== TOK
.colon
)
3220 // C11 6.7.2.1-12 unnamed bit-field
3221 id
= Identifier
.generateAnonymousId("BitField");
3226 dt = cparseDeclarator(DTR
.xdirect
, tspec
, id
, specifier
);
3231 break; // error recovery
3235 AST
.Expression width
;
3236 if (token
.value
== TOK
.colon
)
3238 // C11 6.7.2.1-10 bit-field
3240 width
= cparseConstantExp();
3244 * struct-declarator:
3245 * declarator gnu-attributes (opt)
3246 * declarator (opt) : constant-expression gnu-attributes (opt)
3248 if (token
.value
== TOK
.__attribute__
)
3249 cparseGnuAttributes(specifier
);
3251 AST
.Dsymbol s
= null;
3252 symbols
= symbolsSave
;
3254 symbols
= new AST
.Dsymbols
; // lazilly create it
3256 if (!tspec
&& !specifier
.scw
&& !specifier
.mod
)
3257 error("specifier-qualifier-list required");
3260 if (specifier
.alignExps
)
3261 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
3262 s
= new AST
.BitFieldDeclaration(width
.loc
, dt, id
, width
);
3266 if (dt.ty
== AST
.Tvoid
)
3267 error("`void` has no value");
3269 // declare the symbol
3270 // Give member variables an implicit void initializer
3271 auto initializer
= new AST
.VoidInitializer(token
.loc
);
3272 s
= new AST
.VarDeclaration(token
.loc
, dt, id
, initializer
, specifiersToSTC(LVL
.member
, specifier
));
3273 s
= applySpecifier(s
, specifier
);
3278 switch (token
.value
)
3280 case TOK
.identifier
:
3281 error("missing comma");
3293 error("`;` or `,` expected");
3294 while (token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
3303 /******************************************************************************/
3304 /********************************* Lookahead Parser ***************************/
3307 /************************************
3308 * Determine if the scanner is sitting on the start of a declaration.
3310 * t = current token of the scanner
3311 * needId = flag with additional requirements for a declaration
3312 * endtok = ending token
3313 * pt = will be set ending token (if not null)
3315 * true at start of a declaration
3317 private bool isCDeclaration(ref Token
* pt
)
3320 //printf("isCDeclaration() %s\n", t.toChars());
3321 if (!isDeclarationSpecifiers(t
))
3326 if (t
.value
== TOK
.semicolon
)
3332 if (!isCDeclarator(t
, DTR
.xdirect
))
3334 if (t
.value
== TOK
.asm_
)
3337 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
3340 if (t
.value
== TOK
.__attribute__
)
3343 if (t
.value
!= TOK
.leftParenthesis ||
!skipParens(t
, &t
))
3346 if (t
.value
== TOK
.assign
)
3349 if (!isInitializer(t
))
3369 /********************************
3370 * See if match for initializer.
3372 * pt = starting token, updated to one past end of initializer if true
3374 * true if initializer
3376 private bool isInitializer(ref Token
* pt
)
3378 //printf("isInitializer()\n");
3381 if (t
.value
== TOK
.leftCurly
)
3389 // skip over assignment-expression, ending before comma or semiColon or EOF
3390 if (!isAssignmentExpression(t
))
3396 /********************************
3398 * postfix-expression ( argument-expression-list(opt) )
3400 * pt = starting token, updated to one past end of initializer if true
3402 * true if function call
3404 private bool isFunctionCall(ref Token
* pt
)
3406 //printf("isFunctionCall()\n");
3409 if (!isPrimaryExpression(t
))
3411 if (t
.value
!= TOK
.leftParenthesis
)
3416 if (!isAssignmentExpression(t
))
3418 if (t
.value
== TOK
.comma
)
3423 if (t
.value
== TOK
.rightParenthesis
)
3430 if (t
.value
!= TOK
.semicolon
)
3436 /********************************
3437 * See if match for assignment-expression.
3439 * pt = starting token, updated to one past end of assignment-expression if true
3441 * true if assignment-expression
3443 private bool isAssignmentExpression(ref Token
* pt
)
3446 //printf("isAssignmentExpression() %s\n", t.toChars());
3448 /* This doesn't actually check for grammar matching an
3449 * assignment-expression. It just matches ( ) [ ] looking for
3450 * an ending token that would terminate one.
3459 case TOK
.rightParenthesis
:
3460 case TOK
.rightBracket
:
3466 case TOK
.leftParenthesis
:
3467 if (!skipParens(t
, &t
))
3470 https://issues.dlang.org/show_bug.cgi?id=22267
3471 Fix issue 22267: If the parser encounters the following
3472 `identifier variableName = (expression);`
3473 the initializer is not identified as such since the parentheses
3474 cause the parser to keep walking indefinitely
3475 (whereas `(1) + 1` would not be affected.).
3480 case TOK
.leftBracket
:
3481 if (!skipBrackets(t
))
3491 any
= true; // assume token was part of an a-e
3500 /********************************
3501 * See if match for constant-expression.
3503 * pt = starting token, updated to one past end of constant-expression if true
3505 * true if constant-expression
3507 private bool isConstantExpression(ref Token
* pt
)
3509 return isAssignmentExpression(pt
);
3512 /********************************
3513 * See if match for declaration-specifiers.
3514 * No errors are diagnosed.
3516 * pt = starting token, updated to one past end of declaration-specifiers if true
3518 * true if declaration-specifiers
3520 private bool isDeclarationSpecifiers(ref Token
* pt
)
3522 //printf("isDeclarationSpecifiers()\n");
3543 //case TOK._Imaginary:
3550 case TOK
.identifier
: // typedef-name
3564 if (t
.value
== TOK
.identifier
)
3567 if (t
.value
== TOK
.leftCurly
)
3573 else if (t
.value
== TOK
.leftCurly
)
3583 // storage-class-specifiers
3587 case TOK
._Thread_local
:
3591 // function-specifiers
3603 case TOK
._Alignas
: // alignment-specifier
3604 case TOK
.__declspec
: // decl-specifier
3605 case TOK
.__attribute__
: // attribute-specifier
3607 if (!skipParens(t
, &t
))
3612 // either atomic-type-specifier or type_qualifier
3613 case TOK
._Atomic
: // TODO _Atomic ( type-name )
3615 if (t
.value
== TOK
.leftParenthesis
) // maybe atomic-type-specifier
3619 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
3620 { // it's a type-qualifier
3621 t
= tsave
; // back up parser
3625 t
= peek(t
); // move past right parenthesis of atomic-type-specifier
3644 /**************************************
3645 * See if declaration-list is present.
3647 * true if declaration-list is present, even an empty one
3649 bool isDeclarationList(ref Token
* pt
)
3654 if (t
.value
== TOK
.leftCurly
)
3659 if (!isCDeclaration(t
))
3664 /*******************************************
3667 * pt = enters on left brace, set to token past right bracket on true
3669 * true if successful
3671 private bool skipBraces(ref Token
* pt
)
3674 if (t
.value
!= TOK
.leftCurly
)
3688 case TOK
.rightCurly
:
3711 /*******************************************
3714 * pt = enters on left bracket, set to token past right bracket on true
3716 * true if successful
3718 private bool skipBrackets(ref Token
* pt
)
3721 if (t
.value
!= TOK
.leftBracket
)
3730 case TOK
.leftBracket
:
3735 case TOK
.rightBracket
:
3758 /*********************************
3759 * Check to see if tokens starting with *pt form a declarator.
3761 * pt = pointer to starting token, updated to point past declarator if true is returned
3762 * declarator = declarator kind
3766 private bool isCDeclarator(ref Token
* pt
, DTR declarator
)
3771 if (t
.value
== TOK
.mul) // pointer
3774 if (!isTypeQualifierList(t
))
3781 if (t
.value
== TOK
.identifier
)
3783 if (declarator
== DTR
.xabstract
)
3787 else if (t
.value
== TOK
.leftParenthesis
)
3790 if (!isCDeclarator(t
, declarator
))
3792 if (t
.value
!= TOK
.rightParenthesis
)
3796 else if (declarator
== DTR
.xdirect
)
3803 if (t
.value
== TOK
.leftBracket
)
3805 if (!skipBrackets(t
))
3808 else if (t
.value
== TOK
.leftParenthesis
)
3810 if (!skipParens(t
, &t
))
3820 /***************************
3821 * Is this the start of a type-qualifier-list?
3824 * pt = first token; updated with past end of type-qualifier-list if true
3826 * true if start of type-qualifier-list
3828 private bool isTypeQualifierList(ref Token
* pt
)
3851 /***************************
3852 * Is this the start of a type-name?
3854 * pt = first token; updated with past end of type-name if true
3856 * true if start of type-name
3858 private bool isTypeName(ref Token
* pt
)
3861 //printf("isTypeName() %s\n", t.toChars());
3862 if (!isSpecifierQualifierList(t
))
3864 if (!isCDeclarator(t
, DTR
.xabstract
))
3866 if (t
.value
!= TOK
.rightParenthesis
)
3872 /***************************
3873 * Is this the start of a specifier-qualifier-list?
3875 * pt = first token; updated with past end of specifier-qualifier-list if true
3877 * true if start of specifier-qualifier-list
3879 private bool isSpecifierQualifierList(ref Token
* pt
)
3903 //case TOK._Imaginary: // ? missing in Spec
3907 case TOK
.identifier
: // will not know until semantic if typedef
3911 // struct-or-union-specifier
3917 if (t
.value
== TOK
.identifier
)
3920 if (t
.value
== TOK
.leftCurly
)
3926 else if (t
.value
== TOK
.leftCurly
)
3935 // atomic-type-specifier
3938 if (t
.value
!= TOK
.leftParenthesis ||
3952 /************************************
3953 * Looking at the leading left parenthesis, and determine if it is
3954 * either of the following:
3955 * ( type-name ) cast-expression
3956 * ( type-name ) { initializer-list }
3960 * pt = starting token, updated to one past end of constant-expression if true
3961 * afterParenType = true if already seen ( type-name )
3963 * true if matches ( type-name ) ...
3965 private bool isCastExpression(ref Token
* pt
, bool afterParenType
= false)
3970 case TOK
.leftParenthesis
:
3971 auto tk
= peek(t
); // move past left parenthesis
3972 if (!isTypeName(tk
) || tk
.value
!= TOK
.rightParenthesis
)
3975 goto default; // could be ( type-name ) ( unary-expression )
3978 tk
= peek(tk
); // move past right parenthesis
3980 if (tk
.value
== TOK
.leftCurly
)
3982 // ( type-name ) { initializer-list }
3983 if (!isInitializer(tk
))
3989 if (tk
.value
== TOK
.leftParenthesis
&& peek(tk
).value
== TOK
.rightParenthesis
)
3990 return false; // (type-name)() is not a cast (it might be a function call)
3992 if (!isCastExpression(tk
, true))
3994 if (afterParenType
) // could be ( type-name ) ( unary-expression )
3995 goto default; // where unary-expression also matched type-name
3998 // ( type-name ) cast-expression
4003 if (!afterParenType ||
!isUnaryExpression(t
, afterParenType
))
4005 // if we've already seen ( type-name ), then this is a cast
4012 /********************************
4013 * See if match for unary-expression.
4015 * pt = starting token, updated to one past end of constant-expression if true
4016 * afterParenType = true if already seen ( type-name ) of a cast-expression
4018 * true if unary-expression
4020 private bool isUnaryExpression(ref Token
* pt
, bool afterParenType
= false)
4026 case TOK
.minusMinus
:
4028 if (!isUnaryExpression(t
, afterParenType
))
4039 if (!isCastExpression(t
, afterParenType
))
4045 if (t
.value
== TOK
.leftParenthesis
)
4050 if (tk
.value
!= TOK
.rightParenthesis
)
4056 if (!isUnaryExpression(t
, afterParenType
))
4062 if (t
.value
!= TOK
.leftParenthesis
)
4065 if (!isTypeName(t
) || t
.value
!= TOK
.rightParenthesis
)
4070 // Compound literals are handled by cast and sizeof expressions,
4071 // so be content with just seeing a primary expression.
4072 if (!isPrimaryExpression(t
))
4080 /********************************
4081 * See if match for primary-expression.
4083 * pt = starting token, updated to one past end of constant-expression if true
4085 * true if primary-expression
4087 private bool isPrimaryExpression(ref Token
* pt
)
4092 case TOK
.identifier
:
4093 case TOK
.int32Literal
:
4094 case TOK
.uns32Literal
:
4095 case TOK
.int64Literal
:
4096 case TOK
.uns64Literal
:
4097 case TOK
.float32Literal
:
4098 case TOK
.float64Literal
:
4099 case TOK
.float80Literal
:
4100 case TOK
.imaginary32Literal
:
4101 case TOK
.imaginary64Literal
:
4102 case TOK
.imaginary80Literal
:
4107 case TOK
.leftParenthesis
:
4109 if (!skipParens(t
, &t
))
4115 if (!skipParens(t
, &t
))
4127 /******************************************************************************/
4128 /********************************* More ***************************************/
4132 * Declaration context
4136 global
= 1, /// global
4137 parameter
= 2, /// function parameter (declarations for function identifier-list)
4138 prototype
= 4, /// function prototype
4139 local
= 8, /// local
4140 member
= 0x10, /// struct member
4143 /// Types of declarator to parse
4146 xdirect
= 1, /// C11 6.7.6 direct-declarator
4147 xabstract
= 2, /// C11 6.7.7 abstract-declarator
4148 xparameter
= 3, /// parameter declarator may be either direct or abstract
4151 /// C11 6.7.1 Storage-class specifiers
4161 // C11 6.7.4 Function specifiers
4166 /// C11 6.7.3 Type qualifiers
4176 /**********************************
4177 * Aggregate for all the various specifiers
4181 bool noreturn
; /// noreturn attribute
4182 SCW scw
; /// storage-class specifiers
4183 MOD mod
; /// type qualifiers
4184 AST
.Expressions
* alignExps
; /// alignment
4185 structalign_t packalign
; /// #pragma pack alignment value
4188 /***********************
4189 * Convert from C specifiers to D storage class
4191 * level = declaration context
4192 * specifier = specifiers, context, etc.
4194 * corresponding D storage class
4196 StorageClass
specifiersToSTC(LVL level
, const ref Specifier specifier
)
4199 if (specifier
.scw
& SCW
.x_Thread_local
)
4201 if (level
== LVL
.global
)
4203 if (specifier
.scw
& SCW
.xextern
)
4204 stc = AST
.STC
.extern_
;
4206 else if (level
== LVL
.local
)
4208 if (specifier
.scw
& SCW
.xextern
)
4209 stc = AST
.STC
.extern_
;
4210 else if (specifier
.scw
& SCW
.xstatic
)
4211 stc = AST
.STC
.static_
;
4213 else if (level
== LVL
.member
)
4215 if (specifier
.scw
& SCW
.xextern
)
4216 stc = AST
.STC
.extern_
;
4217 else if (specifier
.scw
& SCW
.xstatic
)
4218 stc = AST
.STC
.static_
;
4223 if (level
== LVL
.global
)
4225 if (specifier
.scw
& SCW
.xextern
)
4226 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
4227 else if (specifier
.scw
& SCW
.xstatic
)
4228 stc = AST
.STC
.gshared | AST
.STC
.static_
;
4230 stc = AST
.STC
.gshared
;
4232 else if (level
== LVL
.local
)
4234 if (specifier
.scw
& SCW
.xextern
)
4235 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
4236 else if (specifier
.scw
& SCW
.xstatic
)
4237 stc = AST
.STC
.gshared
;
4239 else if (level
== LVL
.member
)
4241 if (specifier
.scw
& SCW
.xextern
)
4242 stc = AST
.STC
.extern_ | AST
.STC
.gshared
;
4243 else if (specifier
.scw
& SCW
.xstatic
)
4244 stc = AST
.STC
.gshared
;
4250 /***********************
4251 * Return suitable D float type for C `long double`
4253 * flags = kind of float to return (real, imaginary, complex).
4255 * corresponding D type
4257 private AST
.Type
realType(RTFlags flags
)
4259 if (long_doublesize
== AST
.Type
.tfloat80
.size())
4261 // On GDC and LDC, D `real` types map to C `long double`, so never
4262 // return a double type when real.sizeof == double.sizeof.
4263 final switch (flags
)
4265 case RTFlags
.realfloat
: return AST
.Type
.tfloat80
;
4266 case RTFlags
.imaginary
: return AST
.Type
.timaginary80
;
4267 case RTFlags
.complex
: return AST
.Type
.tcomplex80
;
4272 final switch (flags
)
4274 case RTFlags
.realfloat
: return long_doublesize
== 8 ? AST
.Type
.tfloat64
: AST
.Type
.tfloat80
;
4275 case RTFlags
.imaginary
: return long_doublesize
== 8 ? AST
.Type
.timaginary64
: AST
.Type
.timaginary80
;
4276 case RTFlags
.complex
: return long_doublesize
== 8 ? AST
.Type
.tcomplex64
: AST
.Type
.tcomplex80
;
4282 * Flags for realType
4284 private enum RTFlags
4291 /********************
4292 * C11 6.4.2.2 Create declaration to predefine __func__
4293 * `static const char __func__[] = " function-name ";`
4295 * loc = location for this declaration
4296 * id = identifier of function
4298 * statement representing the declaration of __func__
4300 private AST
.Statement
createFuncName(Loc loc
, Identifier id
)
4302 const fn
= id
.toString(); // function-name
4303 auto efn
= new AST
.StringExp(loc
, fn
, fn
.length
, 1, 'c');
4304 auto ifn
= new AST
.ExpInitializer(loc
, efn
);
4305 auto lenfn
= new AST
.IntegerExp(loc
, fn
.length
+ 1, AST
.Type
.tuns32
); // +1 for terminating 0
4306 auto tfn
= new AST
.TypeSArray(AST
.Type
.tchar
, lenfn
);
4307 efn
.type
= tfn
.immutableOf();
4309 auto sfn
= new AST
.VarDeclaration(loc
, tfn
, Id
.__func__
, ifn
, STC
.gshared | STC
.immutable_
);
4310 auto e
= new AST
.DeclarationExp(loc
, sfn
);
4311 return new AST
.ExpStatement(loc
, e
);
4314 /************************
4315 * After encountering an error, scan forward until a right brace or ; is found
4316 * or the end of the file.
4320 while (token
.value
!= TOK
.rightCurly
&& token
.value
!= TOK
.semicolon
&& token
.value
!= TOK
.endOfFile
)
4324 /**************************
4325 * Apply `const` to a type.
4327 * t = type to add const to
4331 private AST
.Type
toConst(AST
.Type t
)
4333 // `const` is always applied to the return type, not the
4334 // type function itself.
4335 if (auto tf
= t
.isTypeFunction())
4336 tf
.next
= tf
.next
.addSTC(STC
.const_
);
4338 t
= t
.addSTC(STC
.const_
);
4342 /***************************
4343 * Apply specifier to a Dsymbol.
4346 * specifier = specifiers to apply
4348 * Dsymbol with specifiers applied
4350 private AST
.Dsymbol
applySpecifier(AST
.Dsymbol s
, ref Specifier specifier
)
4352 //printf("applySpecifier() %s\n", s.toChars());
4353 if (specifier
.alignExps
)
4355 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
4356 // Wrap declaration in an AlignDeclaration
4357 auto decls
= new AST
.Dsymbols(1);
4359 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.alignExps
, decls
);
4361 else if (!specifier
.packalign
.isDefault())
4363 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
4364 // Wrap #pragma pack in an AlignDeclaration
4365 auto decls
= new AST
.Dsymbols(1);
4367 s
= new AST
.AlignDeclaration(s
.loc
, specifier
.packalign
, decls
);
4372 /***********************************
4373 * Add global target-dependent builtin declarations.
4375 private void addBuiltinDeclarations()
4377 void genBuiltinFunc(Identifier id
, AST
.VarArg va
)
4379 auto tva_list
= new AST
.TypeIdentifier(Loc
.initial
, Id
.builtin_va_list
);
4380 auto parameters
= new AST
.Parameters();
4381 parameters
.push(new AST
.Parameter(STC
.parameter | STC
.ref_
, tva_list
, null, null, null));
4382 auto pl
= AST
.ParameterList(parameters
, va
, 0);
4383 auto tf
= new AST
.TypeFunction(pl
, AST
.Type
.tvoid
, LINK
.c
, 0);
4384 auto s
= new AST
.FuncDeclaration(Loc
.initial
, Loc
.initial
, id
, AST
.STC
.static_
, tf
, false);
4388 /* void __builtin_va_start(__builtin_va_list, ...);
4389 * The second argument is supposed to be of any type, so fake it with the ...
4391 genBuiltinFunc(Id
.builtin_va_start
, AST
.VarArg
.variadic
);
4393 /* void __builtin_va_end(__builtin_va_list);
4395 genBuiltinFunc(Id
.builtin_va_end
, AST
.VarArg
.none
);
4397 /* struct __va_list_tag
4399 * uint, uint, void*, void*
4402 auto s
= new AST
.StructDeclaration(Loc
.initial
, Id
.va_list_tag
, false);