| /** |
| * Takes a token stream from the lexer, and parses it into an abstract syntax tree. |
| * |
| * Specification: C11 |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d, _cparse.d) |
| * Documentation: https://dlang.org/phobos/dmd_cparse.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d |
| */ |
| |
| module dmd.cparse; |
| |
| import core.stdc.stdio; |
| import core.stdc.string; |
| import dmd.astenums; |
| import dmd.globals; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.lexer; |
| import dmd.parse; |
| import dmd.errors; |
| import dmd.root.array; |
| import dmd.root.filename; |
| import dmd.common.outbuffer; |
| import dmd.root.rmem; |
| import dmd.root.rootobject; |
| import dmd.root.string; |
| import dmd.tokens; |
| |
| /*********************************************************** |
| */ |
| final class CParser(AST) : Parser!AST |
| { |
| AST.Dsymbols* symbols; // symbols declared in current scope |
| |
| bool addFuncName; /// add declaration of __func__ to function symbol table |
| bool importBuiltins; /// seen use of C compiler builtins, so import __builtins; |
| |
| private |
| { |
| structalign_t packalign; // current state of #pragma pack alignment |
| |
| // #pragma pack stack |
| Array!Identifier* records; // identifers (or null) |
| Array!structalign_t* packs; // parallel alignment values |
| } |
| |
| /* C cannot be parsed without determining if an identifier is a type or a variable. |
| * For expressions like `(T)-3`, is it a cast or a minus expression? |
| * It also occurs with `typedef int (F)(); F fun;` |
| * but to build the AST we need to distinguish `fun` being a function as opposed to a variable. |
| * To fix, build a symbol table for the typedefs. |
| * Symbol table of typedefs indexed by Identifier cast to void*. |
| * 1. if an identifier is a typedef, then it will return a non-null Type |
| * 2. if an identifier is not a typedef, then it will return null |
| */ |
| Array!(void*) typedefTab; /// Array of AST.Type[Identifier], typedef's indexed by Identifier |
| |
| /* This is passed in as a list of #define lines, as generated by the C preprocessor with the |
| * appropriate switch to emit them. We append to it any #define's and #undef's encountered in the source |
| * file, as cpp with the -dD embeds them in the preprocessed output file. |
| * Once the file is parsed, then the #define's are converted to D symbols and appended to the array |
| * of Dsymbols returned by parseModule(). |
| */ |
| OutBuffer* defines; |
| |
| extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment, |
| const ref TARGET target, OutBuffer* defines) |
| { |
| super(_module, input, doDocComment); |
| |
| //printf("CParser.this()\n"); |
| mod = _module; |
| linkage = LINK.c; |
| Ccompile = true; |
| this.packalign.setDefault(); |
| this.defines = defines; |
| |
| // Configure sizes for C `long`, `long double`, `wchar_t`, ... |
| this.boolsize = target.boolsize; |
| this.shortsize = target.shortsize; |
| this.intsize = target.intsize; |
| this.longsize = target.longsize; |
| this.long_longsize = target.long_longsize; |
| this.long_doublesize = target.long_doublesize; |
| this.wchar_tsize = target.wchar_tsize; |
| |
| // C `char` is always unsigned in ImportC |
| } |
| |
| /******************************************** |
| * Parse translation unit. |
| * C11 6.9 |
| * translation-unit: |
| * external-declaration |
| * translation-unit external-declaration |
| * |
| * external-declaration: |
| * function-definition |
| * declaration |
| * Returns: |
| * array of Dsymbols that were declared |
| */ |
| override AST.Dsymbols* parseModule() |
| { |
| //printf("cparseTranslationUnit()\n"); |
| symbols = new AST.Dsymbols(); |
| typedefTab.push(null); // C11 6.2.1-3 symbol table for "file scope" |
| while (1) |
| { |
| if (token.value == TOK.endOfFile) |
| { |
| addDefines(); // convert #define's to Dsymbols |
| |
| // wrap the symbols in `extern (C) { symbols }` |
| auto wrap = new AST.Dsymbols(); |
| auto ld = new AST.LinkDeclaration(token.loc, LINK.c, symbols); |
| wrap.push(ld); |
| |
| if (importBuiltins) |
| { |
| /* Seen references to C builtin functions. |
| * Import their definitions |
| */ |
| auto s = new AST.Import(Loc.initial, null, Id.builtins, null, false); |
| wrap.push(s); |
| } |
| |
| // end of file scope |
| typedefTab.pop(); |
| assert(typedefTab.length == 0); |
| |
| return wrap; |
| } |
| |
| cparseDeclaration(LVL.global); |
| } |
| } |
| |
| /******************************************************************************/ |
| /********************************* Statement Parser ***************************/ |
| //{ |
| |
| /********************** |
| * C11 6.8 |
| * statement: |
| * labeled-statement |
| * compound-statement |
| * expression-statement |
| * selection-statement |
| * iteration-statement |
| * jump-statement |
| * |
| * Params: |
| * flags = PSxxxx |
| * endPtr = store location of closing brace |
| * pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement |
| * Returns: |
| * parsed statement |
| */ |
| AST.Statement cparseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null) |
| { |
| AST.Statement s; |
| const loc = token.loc; |
| |
| //printf("cparseStatement()\n"); |
| |
| const typedefTabLengthSave = typedefTab.length; |
| auto symbolsSave = symbols; |
| if (flags & ParseStatementFlags.scope_) |
| { |
| typedefTab.push(null); // introduce new block scope |
| } |
| |
| if (!(flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))) |
| { |
| symbols = new AST.Dsymbols(); |
| } |
| |
| switch (token.value) |
| { |
| case TOK.identifier: |
| /* A leading identifier can be a declaration, label, or expression. |
| * A quick check of the next token can disambiguate most cases. |
| */ |
| switch (peekNext()) |
| { |
| case TOK.colon: |
| { |
| // It's a label |
| auto ident = token.ident; |
| nextToken(); // advance to `:` |
| nextToken(); // advance past `:` |
| if (token.value == TOK.rightCurly) |
| s = null; |
| else if (token.value == TOK.leftCurly) |
| s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_); |
| else |
| s = cparseStatement(ParseStatementFlags.semiOk); |
| s = new AST.LabelStatement(loc, ident, s); |
| break; |
| } |
| |
| case TOK.dot: |
| case TOK.arrow: |
| case TOK.plusPlus: |
| case TOK.minusMinus: |
| case TOK.leftBracket: |
| case TOK.question: |
| case TOK.assign: |
| case TOK.addAssign: |
| case TOK.minAssign: |
| case TOK.mulAssign: |
| case TOK.divAssign: |
| case TOK.modAssign: |
| case TOK.andAssign: |
| case TOK.orAssign: |
| case TOK.xorAssign: |
| case TOK.leftShiftAssign: |
| case TOK.rightShiftAssign: |
| goto Lexp; |
| |
| case TOK.leftParenthesis: |
| if (auto pt = lookupTypedef(token.ident)) |
| { |
| if (*pt) |
| goto Ldeclaration; |
| } |
| goto Lexp; // function call |
| |
| default: |
| { |
| /* If tokens look like a declaration, assume it is one |
| */ |
| auto tk = &token; |
| if (isCDeclaration(tk)) |
| goto Ldeclaration; |
| goto Lexp; |
| } |
| } |
| break; |
| |
| case TOK.charLiteral: |
| case TOK.int32Literal: |
| case TOK.uns32Literal: |
| case TOK.int64Literal: |
| case TOK.uns64Literal: |
| case TOK.int128Literal: |
| case TOK.uns128Literal: |
| case TOK.float32Literal: |
| case TOK.float64Literal: |
| case TOK.float80Literal: |
| case TOK.imaginary32Literal: |
| case TOK.imaginary64Literal: |
| case TOK.imaginary80Literal: |
| case TOK.leftParenthesis: |
| case TOK.and: |
| case TOK.mul: |
| case TOK.min: |
| case TOK.add: |
| case TOK.tilde: |
| case TOK.not: |
| case TOK.plusPlus: |
| case TOK.minusMinus: |
| case TOK.sizeof_: |
| case TOK._Generic: |
| Lexp: |
| auto exp = cparseExpression(); |
| if (token.value == TOK.identifier && exp.op == EXP.identifier) |
| { |
| error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars()); |
| nextToken(); |
| } |
| else |
| check(TOK.semicolon, "statement"); |
| s = new AST.ExpStatement(loc, exp); |
| break; |
| |
| // type-specifiers |
| case TOK.void_: |
| case TOK.char_: |
| case TOK.int16: |
| case TOK.int32: |
| case TOK.int64: |
| case TOK.float32: |
| case TOK.float64: |
| case TOK.signed: |
| case TOK.unsigned: |
| case TOK._Bool: |
| //case TOK._Imaginary: |
| case TOK._Complex: |
| case TOK.struct_: |
| case TOK.union_: |
| case TOK.enum_: |
| case TOK.typeof_: |
| |
| // storage-class-specifiers |
| case TOK.typedef_: |
| case TOK.extern_: |
| case TOK.static_: |
| case TOK._Thread_local: |
| case TOK.auto_: |
| case TOK.register: |
| |
| // function-specifiers |
| case TOK.inline: |
| case TOK._Noreturn: |
| |
| // type-qualifiers |
| case TOK.const_: |
| case TOK.volatile: |
| case TOK.restrict: |
| case TOK.__stdcall: |
| |
| // alignment-specifier |
| case TOK._Alignas: |
| |
| // atomic-type-specifier or type_qualifier |
| case TOK._Atomic: |
| |
| Ldeclaration: |
| { |
| cparseDeclaration(LVL.local); |
| if (symbols.length > 1) |
| { |
| auto as = new AST.Statements(); |
| as.reserve(symbols.length); |
| foreach (d; (*symbols)[]) |
| { |
| s = new AST.ExpStatement(loc, d); |
| as.push(s); |
| } |
| s = new AST.CompoundDeclarationStatement(loc, as); |
| symbols.setDim(0); |
| } |
| else if (symbols.length == 1) |
| { |
| auto d = (*symbols)[0]; |
| s = new AST.ExpStatement(loc, d); |
| symbols.setDim(0); |
| } |
| else |
| s = new AST.ExpStatement(loc, cast(AST.Expression)null); |
| if (flags & ParseStatementFlags.scope_) |
| s = new AST.ScopeStatement(loc, s, token.loc); |
| break; |
| } |
| |
| case TOK._Static_assert: // _Static_assert ( constant-expression, string-literal ) ; |
| s = new AST.StaticAssertStatement(cparseStaticAssert()); |
| break; |
| |
| case TOK.leftCurly: |
| { |
| /* C11 6.8.2 |
| * compound-statement: |
| * { block-item-list (opt) } |
| * |
| * block-item-list: |
| * block-item |
| * block-item-list block-item |
| * |
| * block-item: |
| * declaration |
| * statement |
| */ |
| nextToken(); |
| auto statements = new AST.Statements(); |
| while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) |
| { |
| statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); |
| } |
| if (endPtr) |
| *endPtr = token.ptr; |
| endloc = token.loc; |
| if (pEndloc) |
| { |
| *pEndloc = token.loc; |
| pEndloc = null; // don't set it again |
| } |
| s = new AST.CompoundStatement(loc, statements); |
| if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope)) |
| s = new AST.ScopeStatement(loc, s, token.loc); |
| check(TOK.rightCurly, "compound statement"); |
| break; |
| } |
| |
| case TOK.while_: |
| { |
| nextToken(); |
| check(TOK.leftParenthesis); |
| auto condition = cparseExpression(); |
| check(TOK.rightParenthesis); |
| Loc endloc; |
| auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc); |
| s = new AST.WhileStatement(loc, condition, _body, endloc, null); |
| break; |
| } |
| |
| case TOK.semicolon: |
| /* C11 6.8.3 null statement |
| */ |
| nextToken(); |
| s = new AST.ExpStatement(loc, cast(AST.Expression)null); |
| break; |
| |
| case TOK.do_: |
| { |
| nextToken(); |
| auto _body = cparseStatement(ParseStatementFlags.scope_); |
| check(TOK.while_); |
| check(TOK.leftParenthesis); |
| auto condition = cparseExpression(); |
| check(TOK.rightParenthesis); |
| check(TOK.semicolon, "terminating `;` required after do-while statement"); |
| s = new AST.DoStatement(loc, _body, condition, token.loc); |
| break; |
| } |
| |
| case TOK.for_: |
| { |
| AST.Statement _init; |
| AST.Expression condition; |
| AST.Expression increment; |
| |
| nextToken(); |
| check(TOK.leftParenthesis); |
| if (token.value == TOK.semicolon) |
| { |
| _init = null; |
| nextToken(); |
| } |
| else |
| { |
| _init = cparseStatement(0); |
| } |
| if (token.value == TOK.semicolon) |
| { |
| condition = null; |
| nextToken(); |
| } |
| else |
| { |
| condition = cparseExpression(); |
| check(TOK.semicolon, "`for` condition"); |
| } |
| if (token.value == TOK.rightParenthesis) |
| { |
| increment = null; |
| nextToken(); |
| } |
| else |
| { |
| increment = cparseExpression(); |
| check(TOK.rightParenthesis); |
| } |
| Loc endloc; |
| auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc); |
| s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc); |
| break; |
| } |
| |
| case TOK.if_: |
| { |
| nextToken(); |
| check(TOK.leftParenthesis); |
| auto condition = cparseExpression(); |
| check(TOK.rightParenthesis); |
| auto ifbody = cparseStatement(ParseStatementFlags.scope_); |
| AST.Statement elsebody; |
| if (token.value == TOK.else_) |
| { |
| nextToken(); |
| elsebody = cparseStatement(ParseStatementFlags.scope_); |
| } |
| else |
| elsebody = null; |
| if (condition && ifbody) |
| s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc); |
| else |
| s = null; // don't propagate parsing errors |
| break; |
| } |
| |
| case TOK.else_: |
| error("found `else` without a corresponding `if` statement"); |
| goto Lerror; |
| |
| case TOK.switch_: |
| { |
| nextToken(); |
| check(TOK.leftParenthesis); |
| auto condition = cparseExpression(); |
| check(TOK.rightParenthesis); |
| auto _body = cparseStatement(ParseStatementFlags.scope_); |
| s = new AST.SwitchStatement(loc, condition, _body, false); |
| break; |
| } |
| |
| case TOK.case_: |
| { |
| |
| nextToken(); |
| auto exp = cparseAssignExp(); |
| check(TOK.colon); |
| |
| if (flags & ParseStatementFlags.curlyScope) |
| { |
| auto statements = new AST.Statements(); |
| while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) |
| { |
| auto cur = cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); |
| statements.push(cur); |
| |
| // https://issues.dlang.org/show_bug.cgi?id=21739 |
| // Stop at the last break s.t. the following non-case statements are |
| // not merged into the current case. This can happen for |
| // case 1: ... break; |
| // debug { case 2: ... } |
| if (cur && cur.isBreakStatement()) |
| break; |
| } |
| s = new AST.CompoundStatement(loc, statements); |
| } |
| else |
| { |
| s = cparseStatement(ParseStatementFlags.semi); |
| } |
| s = new AST.ScopeStatement(loc, s, token.loc); |
| s = new AST.CaseStatement(loc, exp, s); |
| break; |
| } |
| |
| case TOK.default_: |
| { |
| nextToken(); |
| check(TOK.colon); |
| |
| if (flags & ParseStatementFlags.curlyScope) |
| { |
| auto statements = new AST.Statements(); |
| while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) |
| { |
| statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); |
| } |
| s = new AST.CompoundStatement(loc, statements); |
| } |
| else |
| s = cparseStatement(ParseStatementFlags.semi); |
| s = new AST.ScopeStatement(loc, s, token.loc); |
| s = new AST.DefaultStatement(loc, s); |
| break; |
| } |
| |
| case TOK.return_: |
| { |
| /* return ; |
| * return expression ; |
| */ |
| nextToken(); |
| auto exp = token.value == TOK.semicolon ? null : cparseExpression(); |
| check(TOK.semicolon, "`return` statement"); |
| s = new AST.ReturnStatement(loc, exp); |
| break; |
| } |
| |
| case TOK.break_: |
| nextToken(); |
| check(TOK.semicolon, "`break` statement"); |
| s = new AST.BreakStatement(loc, null); |
| break; |
| |
| case TOK.continue_: |
| nextToken(); |
| check(TOK.semicolon, "`continue` statement"); |
| s = new AST.ContinueStatement(loc, null); |
| break; |
| |
| case TOK.goto_: |
| { |
| Identifier ident; |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected following `goto`"); |
| ident = null; |
| } |
| else |
| { |
| ident = token.ident; |
| nextToken(); |
| } |
| s = new AST.GotoStatement(loc, ident); |
| check(TOK.semicolon, "`goto` statement"); |
| break; |
| } |
| |
| case TOK.asm_: |
| s = parseAsm(); |
| break; |
| |
| default: |
| error("found `%s` instead of statement", token.toChars()); |
| goto Lerror; |
| |
| Lerror: |
| panic(); |
| if (token.value == TOK.semicolon) |
| nextToken(); |
| s = null; |
| break; |
| } |
| if (pEndloc) |
| *pEndloc = prevloc; |
| symbols = symbolsSave; |
| typedefTab.setDim(typedefTabLengthSave); |
| return s; |
| } |
| |
| //} |
| /*******************************************************************************/ |
| /********************************* Expression Parser ***************************/ |
| //{ |
| |
| /************** |
| * C11 6.5.17 |
| * expression: |
| * assignment-expression |
| * expression , assignment-expression |
| */ |
| AST.Expression cparseExpression() |
| { |
| auto loc = token.loc; |
| |
| //printf("cparseExpression() loc = %d\n", loc.linnum); |
| auto e = cparseAssignExp(); |
| while (token.value == TOK.comma) |
| { |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.CommaExp(loc, e, e2, false); |
| loc = token.loc; |
| } |
| return e; |
| } |
| |
| |
| /********************* |
| * C11 6.5.1 |
| * primary-expression: |
| * identifier |
| * constant |
| * string-literal |
| * ( expression ) |
| * generic-selection |
| * __builtin_va_arg(assign_expression, type) |
| */ |
| AST.Expression cparsePrimaryExp() |
| { |
| AST.Expression e; |
| const loc = token.loc; |
| |
| //printf("parsePrimaryExp(): loc = %d\n", loc.linnum); |
| switch (token.value) |
| { |
| case TOK.identifier: |
| const id = token.ident.toString(); |
| if (id.length > 2 && id[0] == '_' && id[1] == '_') // leading double underscore |
| { |
| if (token.ident is Id.__func__) |
| { |
| addFuncName = true; // implicitly declare __func__ |
| } |
| else if (token.ident is Id.builtin_va_arg) |
| { |
| e = cparseBuiltin_va_arg(); |
| break; |
| } |
| else |
| importBuiltins = true; // probably one of those compiler extensions |
| } |
| e = new AST.IdentifierExp(loc, token.ident); |
| nextToken(); |
| break; |
| |
| case TOK.charLiteral: |
| case TOK.int32Literal: |
| e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32); |
| nextToken(); |
| break; |
| |
| case TOK.uns32Literal: |
| e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32); |
| nextToken(); |
| break; |
| |
| case TOK.int64Literal: |
| e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64); |
| nextToken(); |
| break; |
| |
| case TOK.uns64Literal: |
| e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64); |
| nextToken(); |
| break; |
| |
| case TOK.float32Literal: |
| e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32); |
| nextToken(); |
| break; |
| |
| case TOK.float64Literal: |
| e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64); |
| nextToken(); |
| break; |
| |
| case TOK.float80Literal: |
| e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80); |
| nextToken(); |
| break; |
| |
| case TOK.imaginary32Literal: |
| e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32); |
| nextToken(); |
| break; |
| |
| case TOK.imaginary64Literal: |
| e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64); |
| nextToken(); |
| break; |
| |
| case TOK.imaginary80Literal: |
| e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80); |
| nextToken(); |
| break; |
| |
| case TOK.string_: |
| { |
| // cat adjacent strings |
| auto s = token.ustring; |
| auto len = token.len; |
| auto postfix = token.postfix; |
| while (1) |
| { |
| nextToken(); |
| if (token.value == TOK.string_) |
| { |
| if (token.postfix) |
| { |
| if (token.postfix != postfix) |
| error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix); |
| postfix = token.postfix; |
| } |
| |
| const len1 = len; |
| const len2 = token.len; |
| len = len1 + len2; |
| auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof); |
| memcpy(s2, s, len1 * char.sizeof); |
| memcpy(s2 + len1, token.ustring, len2 * char.sizeof); |
| s = s2; |
| } |
| else |
| break; |
| } |
| e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix); |
| break; |
| } |
| |
| case TOK.leftParenthesis: |
| nextToken(); |
| e = cparseExpression(); |
| check(TOK.rightParenthesis); |
| break; |
| |
| case TOK._Generic: |
| e = cparseGenericSelection(); |
| break; |
| |
| default: |
| error("expression expected, not `%s`", token.toChars()); |
| // Anything for e, as long as it's not NULL |
| e = new AST.IntegerExp(loc, 0, AST.Type.tint32); |
| nextToken(); |
| break; |
| } |
| return e; |
| } |
| |
| /********************************* |
| * C11 6.5.2 |
| * postfix-expression: |
| * primary-expression |
| * postfix-expression [ expression ] |
| * postfix-expression ( argument-expression-list (opt) ) |
| * postfix-expression . identifier |
| * postfix-expression -> identifier |
| * postfix-expression ++ |
| * postfix-expression -- |
| * ( type-name ) { initializer-list } |
| * ( type-name ) { initializer-list , } |
| * |
| * argument-expression-list: |
| * assignment-expression |
| * argument-expression-list , assignment-expression |
| */ |
| private AST.Expression cparsePostfixExp(AST.Expression e) |
| { |
| e = cparsePrimaryExp(); |
| return cparsePostfixOperators(e); |
| } |
| |
| /******************************** |
| * C11 6.5.2 |
| * Parse a series of operators for a postfix expression after already parsing |
| * a primary-expression or compound literal expression. |
| * Params: |
| * e = parsed primary or compound literal expression |
| * Returns: |
| * parsed postfix expression |
| */ |
| private AST.Expression cparsePostfixOperators(AST.Expression e) |
| { |
| while (1) |
| { |
| const loc = token.loc; |
| switch (token.value) |
| { |
| case TOK.dot: |
| nextToken(); |
| if (token.value == TOK.identifier) |
| { |
| Identifier id = token.ident; |
| e = new AST.DotIdExp(loc, e, id); |
| break; |
| } |
| error("identifier expected following `.`, not `%s`", token.toChars()); |
| break; |
| |
| case TOK.arrow: |
| nextToken(); |
| if (token.value == TOK.identifier) |
| { |
| Identifier id = token.ident; |
| auto die = new AST.DotIdExp(loc, e, id); |
| die.arrow = true; |
| e = die; |
| break; |
| } |
| error("identifier expected following `->`, not `%s`", token.toChars()); |
| break; |
| |
| case TOK.plusPlus: |
| e = new AST.PostExp(EXP.plusPlus, loc, e); |
| break; |
| |
| case TOK.minusMinus: |
| e = new AST.PostExp(EXP.minusMinus, loc, e); |
| break; |
| |
| case TOK.leftParenthesis: |
| e = new AST.CallExp(loc, e, cparseArguments()); |
| continue; |
| |
| case TOK.leftBracket: |
| { |
| // array dereferences: |
| // array[index] |
| AST.Expression index; |
| auto arguments = new AST.Expressions(); |
| |
| inBrackets++; |
| nextToken(); |
| index = cparseAssignExp(); |
| arguments.push(index); |
| check(TOK.rightBracket); |
| inBrackets--; |
| e = new AST.ArrayExp(loc, e, arguments); |
| continue; |
| } |
| default: |
| return e; |
| } |
| nextToken(); |
| } |
| } |
| |
| /************************ |
| * C11 6.5.3 |
| * unary-expression: |
| * postfix-expression |
| * ++ unary-expression |
| * -- unary-expression |
| * unary-operator cast-expression |
| * sizeof unary-expression |
| * sizeof ( type-name ) |
| * _Alignof ( type-name ) |
| * |
| * unary-operator: |
| * & * + - ~ ! |
| */ |
| private AST.Expression cparseUnaryExp() |
| { |
| AST.Expression e; |
| const loc = token.loc; |
| |
| switch (token.value) |
| { |
| case TOK.plusPlus: |
| nextToken(); |
| // Parse `++` as an unary operator so that cast expressions only give |
| // an error for being non-lvalues. |
| e = cparseCastExp(); |
| e = new AST.PreExp(EXP.prePlusPlus, loc, e); |
| break; |
| |
| case TOK.minusMinus: |
| nextToken(); |
| // Parse `--` as an unary operator, same as prefix increment. |
| e = cparseCastExp(); |
| e = new AST.PreExp(EXP.preMinusMinus, loc, e); |
| break; |
| |
| case TOK.and: |
| nextToken(); |
| e = cparseCastExp(); |
| e = new AST.AddrExp(loc, e); |
| break; |
| |
| case TOK.mul: |
| nextToken(); |
| e = cparseCastExp(); |
| e = new AST.PtrExp(loc, e); |
| break; |
| |
| case TOK.min: |
| nextToken(); |
| e = cparseCastExp(); |
| e = new AST.NegExp(loc, e); |
| break; |
| |
| case TOK.add: |
| nextToken(); |
| e = cparseCastExp(); |
| e = new AST.UAddExp(loc, e); |
| break; |
| |
| case TOK.not: |
| nextToken(); |
| e = cparseCastExp(); |
| e = new AST.NotExp(loc, e); |
| break; |
| |
| case TOK.tilde: |
| nextToken(); |
| e = cparseCastExp(); |
| e = new AST.ComExp(loc, e); |
| break; |
| |
| case TOK.sizeof_: |
| { |
| nextToken(); |
| if (token.value == TOK.leftParenthesis) |
| { |
| auto tk = peek(&token); |
| if (isTypeName(tk)) |
| { |
| /* Expression may be either be requesting the sizeof a type-name |
| * or a compound literal, which requires checking whether |
| * the next token is leftCurly |
| */ |
| nextToken(); |
| auto t = cparseTypeName(); |
| check(TOK.rightParenthesis); |
| if (token.value == TOK.leftCurly) |
| { |
| // ( type-name ) { initializer-list } |
| auto ci = cparseInitializer(); |
| e = new AST.CompoundLiteralExp(loc, t, ci); |
| e = cparsePostfixOperators(e); |
| } |
| else |
| { |
| // ( type-name ) |
| e = new AST.TypeExp(loc, t); |
| } |
| } |
| else |
| { |
| // must be an expression |
| e = cparseUnaryExp(); |
| } |
| } |
| else |
| { |
| //C11 6.5.3 |
| e = cparseUnaryExp(); |
| } |
| |
| e = new AST.DotIdExp(loc, e, Id.__sizeof); |
| break; |
| } |
| |
| case TOK._Alignof: |
| { |
| nextToken(); |
| check(TOK.leftParenthesis); |
| auto t = cparseTypeName(); |
| check(TOK.rightParenthesis); |
| e = new AST.TypeExp(loc, t); |
| e = new AST.DotIdExp(loc, e, Id.__xalignof); |
| break; |
| } |
| |
| default: |
| e = cparsePostfixExp(e); |
| break; |
| } |
| assert(e); |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.4 |
| * cast-expression |
| * unary-expression |
| * ( type-name ) cast-expression |
| */ |
| private AST.Expression cparseCastExp() |
| { |
| if (token.value == TOK.leftParenthesis) |
| { |
| //printf("cparseCastExp()\n"); |
| auto tk = peek(&token); |
| bool iscast; |
| bool isexp; |
| if (tk.value == TOK.identifier) |
| { |
| iscast = isTypedef(tk.ident); |
| isexp = !iscast; |
| } |
| if (isexp) |
| { |
| // ( identifier ) is an expression |
| return cparseUnaryExp(); |
| } |
| |
| // If ( type-name ) |
| auto pt = &token; |
| |
| if (isCastExpression(pt)) |
| { |
| // Expression may be either a cast or a compound literal, which |
| // requires checking whether the next token is leftCurly |
| const loc = token.loc; |
| nextToken(); |
| auto t = cparseTypeName(); |
| check(TOK.rightParenthesis); |
| pt = &token; |
| |
| if (token.value == TOK.leftCurly) |
| { |
| // C11 6.5.2.5 ( type-name ) { initializer-list } |
| auto ci = cparseInitializer(); |
| auto ce = new AST.CompoundLiteralExp(loc, t, ci); |
| return cparsePostfixOperators(ce); |
| } |
| |
| if (iscast) |
| { |
| // ( type-name ) cast-expression |
| auto ce = cparseCastExp(); |
| return new AST.CastExp(loc, ce, t); |
| } |
| |
| if (t.isTypeIdentifier() && |
| isexp && |
| token.value == TOK.leftParenthesis && |
| !isCastExpression(pt)) |
| { |
| /* (t)(...)... might be a cast expression or a function call, |
| * with different grammars: a cast would be cparseCastExp(), |
| * a function call would be cparsePostfixExp(CallExp(cparseArguments())). |
| * We can't know until t is known. So, parse it as a function call |
| * and let semantic() rewrite the AST as a CastExp if it turns out |
| * to be a type. |
| */ |
| auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident); |
| ie.parens = true; // let semantic know it might be a CastExp |
| AST.Expression e = new AST.CallExp(loc, ie, cparseArguments()); |
| return cparsePostfixOperators(e); |
| } |
| |
| // ( type-name ) cast-expression |
| auto ce = cparseCastExp(); |
| return new AST.CastExp(loc, ce, t); |
| } |
| } |
| return cparseUnaryExp(); |
| } |
| |
| /************** |
| * C11 6.5.5 |
| * multiplicative-expression |
| * cast-expression |
| * multiplicative-expression * cast-expression |
| * multiplicative-expression / cast-expression |
| * multiplicative-expression % cast-expression |
| */ |
| private AST.Expression cparseMulExp() |
| { |
| const loc = token.loc; |
| auto e = cparseCastExp(); |
| |
| while (1) |
| { |
| switch (token.value) |
| { |
| case TOK.mul: |
| nextToken(); |
| auto e2 = cparseCastExp(); |
| e = new AST.MulExp(loc, e, e2); |
| continue; |
| |
| case TOK.div: |
| nextToken(); |
| auto e2 = cparseCastExp(); |
| e = new AST.DivExp(loc, e, e2); |
| continue; |
| |
| case TOK.mod: |
| nextToken(); |
| auto e2 = cparseCastExp(); |
| e = new AST.ModExp(loc, e, e2); |
| continue; |
| |
| default: |
| break; |
| } |
| break; |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.6 |
| * additive-expression |
| * multiplicative-expression |
| * additive-expression + multiplicative-expression |
| * additive-expression - multiplicative-expression |
| */ |
| private AST.Expression cparseAddExp() |
| { |
| const loc = token.loc; |
| auto e = cparseMulExp(); |
| |
| while (1) |
| { |
| switch (token.value) |
| { |
| case TOK.add: |
| nextToken(); |
| auto e2 = cparseMulExp(); |
| e = new AST.AddExp(loc, e, e2); |
| continue; |
| |
| case TOK.min: |
| nextToken(); |
| auto e2 = cparseMulExp(); |
| e = new AST.MinExp(loc, e, e2); |
| continue; |
| |
| default: |
| break; |
| } |
| break; |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.7 |
| * shift-expression |
| * additive-expression |
| * shift-expression << additive-expression |
| * shift-expression >> additive-expression |
| */ |
| private AST.Expression cparseShiftExp() |
| { |
| const loc = token.loc; |
| auto e = cparseAddExp(); |
| |
| while (1) |
| { |
| switch (token.value) |
| { |
| case TOK.leftShift: |
| nextToken(); |
| auto e2 = cparseAddExp(); |
| e = new AST.ShlExp(loc, e, e2); |
| continue; |
| |
| case TOK.rightShift: |
| nextToken(); |
| auto e2 = cparseAddExp(); |
| e = new AST.ShrExp(loc, e, e2); |
| continue; |
| |
| default: |
| break; |
| } |
| break; |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.8 |
| * relational-expression |
| * shift-expression |
| * relational-expression < shift-expression |
| * relational-expression > shift-expression |
| * relational-expression <= shift-expression |
| * relational-expression >= shift-expression |
| */ |
| private AST.Expression cparseRelationalExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseShiftExp(); |
| |
| EXP op = EXP.reserved; |
| switch (token.value) |
| { |
| case TOK.lessThan: op = EXP.lessThan; goto Lcmp; |
| case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp; |
| case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp; |
| case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp; |
| Lcmp: |
| nextToken(); |
| auto e2 = cparseShiftExp(); |
| e = new AST.CmpExp(op, loc, e, e2); |
| break; |
| |
| default: |
| break; |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.9 |
| * equality-expression |
| * relational-expression |
| * equality-expression == relational-expression |
| * equality-expression != relational-expression |
| */ |
| private AST.Expression cparseEqualityExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseRelationalExp(); |
| |
| EXP op = EXP.reserved; |
| switch (token.value) |
| { |
| case TOK.equal: op = EXP.equal; goto Lequal; |
| case TOK.notEqual: op = EXP.notEqual; goto Lequal; |
| Lequal: |
| nextToken(); |
| auto e2 = cparseRelationalExp(); |
| e = new AST.EqualExp(op, loc, e, e2); |
| break; |
| |
| default: |
| break; |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.10 |
| * AND-expression |
| * equality-expression |
| * AND-expression & equality-expression |
| */ |
| private AST.Expression cparseAndExp() |
| { |
| Loc loc = token.loc; |
| auto e = cparseEqualityExp(); |
| while (token.value == TOK.and) |
| { |
| nextToken(); |
| auto e2 = cparseEqualityExp(); |
| e = new AST.AndExp(loc, e, e2); |
| loc = token.loc; |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.11 |
| * exclusive-OR-expression |
| * AND-expression |
| * exclusive-OR-expression ^ AND-expression |
| */ |
| private AST.Expression cparseXorExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseAndExp(); |
| while (token.value == TOK.xor) |
| { |
| nextToken(); |
| auto e2 = cparseAndExp(); |
| e = new AST.XorExp(loc, e, e2); |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.12 |
| * inclusive-OR-expression |
| * exclusive-OR-expression |
| * inclusive-OR-expression | exclusive-OR-expression |
| */ |
| private AST.Expression cparseOrExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseXorExp(); |
| while (token.value == TOK.or) |
| { |
| nextToken(); |
| auto e2 = cparseXorExp(); |
| e = new AST.OrExp(loc, e, e2); |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.13 |
| * logical-AND-expression |
| * inclusive-OR-expression |
| * logical-AND-expression && inclusive-OR-expression |
| */ |
| private AST.Expression cparseAndAndExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseOrExp(); |
| while (token.value == TOK.andAnd) |
| { |
| nextToken(); |
| auto e2 = cparseOrExp(); |
| e = new AST.LogicalExp(loc, EXP.andAnd, e, e2); |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.14 |
| * logical-OR-expression |
| * logical-AND-expression |
| * logical-OR-expression || logical-AND-expression |
| */ |
| private AST.Expression cparseOrOrExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseAndAndExp(); |
| while (token.value == TOK.orOr) |
| { |
| nextToken(); |
| auto e2 = cparseAndAndExp(); |
| e = new AST.LogicalExp(loc, EXP.orOr, e, e2); |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.15 |
| * conditional-expression: |
| * logical-OR-expression |
| * logical-OR-expression ? expression : conditional-expression |
| */ |
| private AST.Expression cparseCondExp() |
| { |
| const loc = token.loc; |
| |
| auto e = cparseOrOrExp(); |
| if (token.value == TOK.question) |
| { |
| nextToken(); |
| auto e1 = cparseExpression(); |
| check(TOK.colon); |
| auto e2 = cparseCondExp(); |
| e = new AST.CondExp(loc, e, e1, e2); |
| } |
| return e; |
| } |
| |
| /************** |
| * C11 6.5.16 |
| * assignment-expression: |
| * conditional-expression |
| * unary-expression assignment-operator assignment-expression |
| * |
| * assignment-operator: |
| * = *= /= %= += -= <<= >>= &= ^= |= |
| */ |
| AST.Expression cparseAssignExp() |
| { |
| AST.Expression e; |
| e = cparseCondExp(); // constrain it to being unary-expression in semantic pass |
| if (e is null) |
| return e; |
| |
| const loc = token.loc; |
| switch (token.value) |
| { |
| case TOK.assign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.AssignExp(loc, e, e2); |
| break; |
| |
| case TOK.addAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.AddAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.minAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.MinAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.mulAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.MulAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.divAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.DivAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.modAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.ModAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.andAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.AndAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.orAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.OrAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.xorAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.XorAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.leftShiftAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.ShlAssignExp(loc, e, e2); |
| break; |
| |
| case TOK.rightShiftAssign: |
| nextToken(); |
| auto e2 = cparseAssignExp(); |
| e = new AST.ShrAssignExp(loc, e, e2); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return e; |
| } |
| |
| /*********************** |
| * C11 6.5.1.1 |
| * _Generic ( assignment-expression, generic-assoc-list ) |
| * |
| * generic-assoc-list: |
| * generic-association |
| * generic-assoc-list generic-association |
| * |
| * generic-association: |
| * type-name : assignment-expression |
| * default : assignment-expression |
| */ |
| private AST.Expression cparseGenericSelection() |
| { |
| const loc = token.loc; |
| nextToken(); |
| check(TOK.leftParenthesis); |
| auto cntlExp = cparseAssignExp(); |
| check(TOK.comma); |
| auto types = new AST.Types(); |
| auto exps = new AST.Expressions(); |
| bool sawDefault; |
| while (1) |
| { |
| AST.Type t; |
| if (token.value == TOK.default_) |
| { |
| nextToken(); |
| if (sawDefault) |
| error("only one `default` allowed in generic-assoc-list"); |
| sawDefault = true; |
| t = null; |
| } |
| else |
| t = cparseTypeName(); |
| types.push(t); |
| |
| check(TOK.colon); |
| auto e = cparseAssignExp(); |
| exps.push(e); |
| if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile) |
| break; |
| check(TOK.comma); |
| } |
| check(TOK.rightParenthesis); |
| return new AST.GenericExp(loc, cntlExp, types, exps); |
| } |
| |
| /*********************** |
| * C11 6.6 Constant expressions |
| * constant-expression: |
| * conditional-expression |
| */ |
| private AST.Expression cparseConstantExp() |
| { |
| return cparseAssignExp(); |
| } |
| |
| /***************************** |
| * gcc extension: |
| * type __builtin_va_arg(assign-expression, type) |
| * Rewrite as `va_arg` template from `core.stdc.stdarg`: |
| * va_arg!(type)(assign-expression); |
| * Lexer is on `__builtin_va_arg` |
| */ |
| private AST.Expression cparseBuiltin_va_arg() |
| { |
| importBuiltins = true; // need core.stdc.stdarg |
| |
| nextToken(); |
| check(TOK.leftParenthesis); |
| |
| auto arguments = new AST.Expressions(); |
| auto arg = cparseAssignExp(); |
| arguments.push(arg); |
| |
| check(TOK.comma); |
| |
| auto t = cparseTypeName(); |
| auto tiargs = new AST.Objects(); |
| tiargs.push(t); |
| |
| const loc = loc; |
| auto ti = new AST.TemplateInstance(loc, Id.va_arg, tiargs); |
| auto tie = new AST.ScopeExp(loc, ti); |
| |
| AST.Expression e = new AST.CallExp(loc, tie, arguments); |
| |
| check(TOK.rightParenthesis); |
| return e; |
| } |
| |
| //} |
| /********************************************************************************/ |
| /********************************* Declaration Parser ***************************/ |
| //{ |
| |
| /************************************* |
| * C11 6.7 |
| * declaration: |
| * declaration-specifiers init-declarator-list (opt) ; |
| * static_assert-declaration |
| * |
| * init-declarator-list: |
| * init-declarator |
| * init-declarator-list , init-declarator |
| * |
| * init-declarator: |
| * declarator |
| * declarator = initializer |
| * |
| * Params: |
| * level = declaration context |
| */ |
| void cparseDeclaration(LVL level) |
| { |
| //printf("cparseDeclaration(level = %d)\n", level); |
| if (token.value == TOK._Static_assert) |
| { |
| auto s = cparseStaticAssert(); |
| symbols.push(s); |
| return; |
| } |
| |
| if (token.value == TOK.__pragma) |
| { |
| uupragmaDirective(scanloc); |
| return; |
| } |
| |
| if (token.value == TOK._import) // import declaration extension |
| { |
| auto a = parseImport(); |
| if (a && a.length) |
| symbols.append(a); |
| return; |
| } |
| |
| const typedefTabLengthSave = typedefTab.length; |
| auto symbolsSave = symbols; |
| Specifier specifier; |
| specifier.packalign = this.packalign; |
| auto tspec = cparseDeclarationSpecifiers(level, specifier); |
| |
| /* If a declarator does not follow, it is unnamed |
| */ |
| if (token.value == TOK.semicolon) |
| { |
| if (!tspec) |
| { |
| nextToken(); |
| return; // accept empty declaration as an extension |
| } |
| |
| if (auto ti = tspec.isTypeIdentifier()) |
| { |
| // C11 6.7.2-2 |
| error("type-specifier missing for declaration of `%s`", ti.ident.toChars()); |
| nextToken(); |
| return; |
| } |
| |
| nextToken(); |
| auto tt = tspec.isTypeTag(); |
| if (!tt || |
| !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_)) |
| return; // legal but meaningless empty declaration, ignore it |
| |
| /* `struct tag;` and `struct tag { ... };` |
| * always result in a declaration in the current scope |
| */ |
| auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) : |
| (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) : |
| new AST.EnumDeclaration(tt.loc, tt.id, tt.base); |
| stag.members = tt.members; |
| if (!symbols) |
| symbols = new AST.Dsymbols(); |
| auto stags = applySpecifier(stag, specifier); |
| symbols.push(stags); |
| |
| if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it |
| { |
| if (!tt.members) |
| error(tt.loc, "`enum %s` has no members", stag.toChars()); |
| } |
| return; |
| } |
| |
| if (!tspec) |
| { |
| error("no type for declarator before `%s`", token.toChars()); |
| panic(); |
| nextToken(); |
| return; |
| } |
| |
| if (tspec && specifier.mod & MOD.xconst) |
| { |
| tspec = toConst(tspec); |
| specifier.mod &= ~MOD.xnone; // 'used' it |
| } |
| |
| void scanPastSemicolon() |
| { |
| while (token.value != TOK.semicolon && token.value != TOK.endOfFile) |
| nextToken(); |
| nextToken(); |
| } |
| |
| if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier()) |
| { |
| /* C11 6.7.2-2 |
| * Special check for `const b = 1;` because some compilers allow it |
| */ |
| error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars()); |
| return scanPastSemicolon(); |
| } |
| |
| bool first = true; |
| while (1) |
| { |
| Identifier id; |
| AST.StringExp asmName; |
| auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier); |
| if (!dt) |
| { |
| panic(); |
| nextToken(); |
| break; // error recovery |
| } |
| |
| /* GNU Extensions |
| * init-declarator: |
| * declarator simple-asm-expr (opt) gnu-attributes (opt) |
| * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer |
| */ |
| switch (token.value) |
| { |
| case TOK.assign: |
| case TOK.comma: |
| case TOK.semicolon: |
| case TOK.asm_: |
| case TOK.__attribute__: |
| if (token.value == TOK.asm_) |
| asmName = cparseSimpleAsmExpr(); |
| if (token.value == TOK.__attribute__) |
| { |
| cparseGnuAttributes(specifier); |
| if (token.value == TOK.leftCurly) |
| break; // function definition |
| } |
| /* This is a data definition, there cannot now be a |
| * function definition. |
| */ |
| first = false; |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (specifier.alignExps && dt.isTypeFunction()) |
| error("no alignment-specifier for function declaration"); // C11 6.7.5-2 |
| if (specifier.alignExps && specifier.scw == SCW.xregister) |
| error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2 |
| |
| /* C11 6.9.1 Function Definitions |
| * function-definition: |
| * declaration-specifiers declarator declaration-list (opt) compound-statement |
| * |
| * declaration-list: |
| * declaration |
| * declaration-list declaration |
| */ |
| auto t = &token; |
| if (first && // first declarator |
| id && |
| dt.isTypeFunction() && // function type not inherited from a typedef |
| isDeclarationList(t) && // optional declaration-list |
| level == LVL.global && // function definitions only at global scope |
| t.value == TOK.leftCurly) // start of compound-statement |
| { |
| auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier); |
| typedefTab.setDim(typedefTabLengthSave); |
| symbols = symbolsSave; |
| symbols.push(s); |
| return; |
| } |
| AST.Dsymbol s = null; |
| typedefTab.setDim(typedefTabLengthSave); |
| symbols = symbolsSave; |
| if (!symbols) |
| symbols = new AST.Dsymbols; // lazilly create it |
| |
| if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod) |
| error("declaration-specifier-seq required"); |
| else if (specifier.scw == SCW.xtypedef) |
| { |
| if (token.value == TOK.assign) |
| error("no initializer for typedef declaration"); |
| if (specifier.alignExps) |
| error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2 |
| |
| bool isalias = true; |
| if (auto ts = dt.isTypeStruct()) |
| { |
| if (ts.sym.isAnonymous()) |
| { |
| // This is a typedef for an anonymous struct-or-union. |
| // Directly set the ident for the struct-or-union. |
| ts.sym.ident = id; |
| isalias = false; |
| } |
| } |
| else if (auto te = dt.isTypeEnum()) |
| { |
| if (te.sym.isAnonymous()) |
| { |
| // This is a typedef for an anonymous enum. |
| te.sym.ident = id; |
| isalias = false; |
| } |
| } |
| else if (auto tt = dt.isTypeTag()) |
| { |
| if (tt.id || tt.tok == TOK.enum_) |
| { |
| /* `struct tag;` and `struct tag { ... };` |
| * always result in a declaration in the current scope |
| */ |
| auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) : |
| (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) : |
| new AST.EnumDeclaration(tt.loc, tt.id, tt.base); |
| stag.members = tt.members; |
| tt.members = null; |
| if (!symbols) |
| symbols = new AST.Dsymbols(); |
| symbols.push(stag); |
| if (tt.tok == TOK.enum_) |
| { |
| isalias = false; |
| s = new AST.AliasDeclaration(token.loc, id, stag); |
| } |
| } |
| } |
| if (isalias) |
| s = new AST.AliasDeclaration(token.loc, id, dt); |
| insertTypedefToTypedefTab(id, dt); // remember typedefs |
| } |
| else if (id) |
| { |
| if (level == LVL.prototype) |
| break; // declared later as Parameter, not VarDeclaration |
| |
| if (dt.ty == AST.Tvoid) |
| error("`void` has no value"); |
| |
| AST.Initializer initializer; |
| bool hasInitializer; |
| if (token.value == TOK.assign) |
| { |
| nextToken(); |
| hasInitializer = true; |
| initializer = cparseInitializer(); |
| } |
| // declare the symbol |
| assert(id); |
| |
| if (isFunctionTypedef(dt)) |
| { |
| if (hasInitializer) |
| error("no initializer for function declaration"); |
| if (specifier.scw & SCW.x_Thread_local) |
| error("functions cannot be `_Thread_local`"); // C11 6.7.1-4 |
| auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn); |
| s = fd; |
| } |
| else |
| { |
| // Give non-extern variables an implicit void initializer |
| // if one has not been explicitly set. |
| if (!hasInitializer && |
| !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global)) |
| initializer = new AST.VoidInitializer(token.loc); |
| s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); |
| } |
| if (level != LVL.global) |
| insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes |
| } |
| if (s !is null) |
| { |
| // Saw `asm("name")` in the function, type, or variable definition. |
| // This is equivalent to `pragma(mangle, "name")` in D |
| if (asmName) |
| { |
| /* |
| https://issues.dlang.org/show_bug.cgi?id=23012 |
| Ideally this would be translated to a pragma(mangle) |
| decl. This is not possible because ImportC symbols are |
| (currently) merged before semantic analysis is performed, |
| so the pragma(mangle) never effects any change on the declarations |
| it pertains too. |
| |
| Writing to mangleOverride directly avoids this, and is possible |
| because C only a StringExp is allowed unlike a full fat pragma(mangle) |
| which is more liberal. |
| */ |
| if (auto p = s.isDeclaration()) |
| { |
| auto str = asmName.peekString(); |
| p.mangleOverride = str; |
| // p.adFlags |= AST.VarDeclaration.nounderscore; |
| p.adFlags |= 4; // cannot get above line to compile on Ubuntu |
| } |
| } |
| s = applySpecifier(s, specifier); |
| if (level == LVL.local) |
| { |
| // Wrap the declaration in `extern (C) { declaration }` |
| // Necessary for function pointers, but harmless to apply to all. |
| auto decls = new AST.Dsymbols(1); |
| (*decls)[0] = s; |
| s = new AST.LinkDeclaration(s.loc, linkage, decls); |
| } |
| symbols.push(s); |
| } |
| first = false; |
| |
| switch (token.value) |
| { |
| case TOK.identifier: |
| if (s) |
| { |
| error("missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars()); |
| goto Lend; |
| } |
| goto default; |
| |
| case TOK.semicolon: |
| nextToken(); |
| return; |
| |
| case TOK.comma: |
| if (!symbolsSave) |
| symbolsSave = symbols; |
| nextToken(); |
| break; |
| |
| default: |
| error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars()); |
| Lend: |
| return scanPastSemicolon(); |
| } |
| } |
| } |
| |
| /*************************************** |
| * C11 Function Definitions |
| * function-definition |
| * declaration-specifiers declarator declaration-list (opt) compound-statement |
| * |
| * declaration-list: |
| * declaration |
| * declaration-list declaration |
| * |
| * It's already been parsed up to the declaration-list (opt). |
| * Pick it up from there. |
| * Params: |
| * id = function identifier |
| * ft = function type |
| * specifier = function specifiers |
| * Returns: |
| * Dsymbol for the function |
| */ |
| AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier) |
| { |
| /* Start function scope |
| */ |
| typedefTab.push(null); |
| |
| if (token.value != TOK.leftCurly) // if not start of a compound-statement |
| { |
| // Do declaration-list |
| do |
| { |
| cparseDeclaration(LVL.parameter); |
| } while (token.value != TOK.leftCurly); |
| |
| /* Since there were declarations, the parameter-list must have been |
| * an identifier-list. |
| */ |
| ft.parameterList.hasIdentifierList = true; // semantic needs to know to adjust parameter types |
| auto pl = ft.parameterList; |
| if (pl.varargs != AST.VarArg.none && pl.length) |
| error("function identifier-list cannot end with `...`"); |
| ft.parameterList.varargs = AST.VarArg.variadic; // but C11 allows extra arguments |
| importBuiltins = true; // will need __va_list_tag |
| auto plLength = pl.length; |
| if (symbols.length != plLength) |
| error("%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length); |
| |
| /* Transfer the types and storage classes from symbols[] to pl[] |
| */ |
| foreach (i; 0 .. plLength) |
| { |
| auto p = pl[i]; // yes, quadratic |
| |
| // Convert typedef-identifier to identifier |
| if (p.type) |
| { |
| if (auto t = p.type.isTypeIdentifier()) |
| { |
| p.ident = t.ident; |
| p.type = null; |
| } |
| } |
| |
| if (p.type || !(p.storageClass & STC.parameter)) |
| error("storage class and type are not allowed in identifier-list"); |
| foreach (s; (*symbols)[]) // yes, quadratic |
| { |
| auto d = s.isDeclaration(); |
| if (d && p.ident == d.ident && d.type) |
| { |
| p.type = d.type; |
| p.storageClass = d.storage_class; |
| d.type = null; // don't reuse |
| break; |
| } |
| } |
| if (!p.type) |
| { |
| error("no declaration for identifier `%s`", p.ident.toChars()); |
| p.type = AST.Type.terror; |
| } |
| } |
| } |
| |
| addFuncName = false; // gets set to true if somebody references __func__ in this function |
| const locFunc = token.loc; |
| |
| auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope |
| typedefTab.pop(); // end of function scope |
| |
| auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn); |
| |
| if (addFuncName) |
| { |
| auto s = createFuncName(locFunc, id); |
| body = new AST.CompoundStatement(locFunc, s, body); |
| } |
| fd.fbody = body; |
| |
| // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3() |
| |
| return fd; |
| } |
| |
| /*************************************** |
| * C11 Initialization |
| * initializer: |
| * assignment-expression |
| * { initializer-list } |
| * { initializer-list , } |
| * |
| * initializer-list: |
| * designation (opt) initializer |
| * initializer-list , designation (opt) initializer |
| * |
| * designation: |
| * designator-list = |
| * |
| * designator-list: |
| * designator |
| * designator-list designator |
| * |
| * designator: |
| * [ constant-expression ] |
| * . identifier |
| * Returns: |
| * initializer |
| */ |
| AST.Initializer cparseInitializer() |
| { |
| if (token.value != TOK.leftCurly) |
| { |
| auto ae = cparseAssignExp(); // assignment-expression |
| return new AST.ExpInitializer(token.loc, ae); |
| } |
| nextToken(); |
| const loc = token.loc; |
| |
| /* Collect one or more `designation (opt) initializer` |
| * into ci.initializerList, but lazily create ci |
| */ |
| AST.CInitializer ci; |
| while (1) |
| { |
| /* There can be 0 or more designators preceding an initializer. |
| * Collect them in desigInit |
| */ |
| AST.DesigInit desigInit; |
| while (1) |
| { |
| if (token.value == TOK.leftBracket) // [ constant-expression ] |
| { |
| nextToken(); |
| auto e = cparseConstantExp(); |
| check(TOK.rightBracket); |
| if (!desigInit.designatorList) |
| desigInit.designatorList = new AST.Designators; |
| desigInit.designatorList.push(AST.Designator(e)); |
| } |
| else if (token.value == TOK.dot) // . identifier |
| { |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected following `.` designator"); |
| break; |
| } |
| if (!desigInit.designatorList) |
| desigInit.designatorList = new AST.Designators; |
| desigInit.designatorList.push(AST.Designator(token.ident)); |
| nextToken(); |
| } |
| else |
| { |
| if (desigInit.designatorList) |
| check(TOK.assign); |
| break; |
| } |
| } |
| |
| desigInit.initializer = cparseInitializer(); |
| if (!ci) |
| ci = new AST.CInitializer(loc); |
| ci.initializerList.push(desigInit); |
| if (token.value == TOK.comma) |
| { |
| nextToken(); |
| if (token.value != TOK.rightCurly) |
| continue; |
| } |
| break; |
| } |
| check(TOK.rightCurly); |
| //printf("ci: %s\n", ci.toChars()); |
| return ci; |
| } |
| |
| /************************************* |
| * C11 6.7 |
| * declaration-specifier: |
| * storage-class-specifier declaration-specifiers (opt) |
| * type-specifier declaration-specifiers (opt) |
| * type-qualifier declaration-specifiers (opt) |
| * function-specifier declaration-specifiers (opt) |
| * alignment-specifier declaration-specifiers (opt) |
| * Params: |
| * level = declaration context |
| * specifier = specifiers in and out |
| * Returns: |
| * resulting type, null if not specified |
| */ |
| private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier) |
| { |
| enum TKW : uint |
| { |
| xnone = 0, |
| xchar = 1, |
| xsigned = 2, |
| xunsigned = 4, |
| xshort = 8, |
| xint = 0x10, |
| xlong = 0x20, |
| xllong = 0x40, |
| xfloat = 0x80, |
| xdouble = 0x100, |
| xldouble = 0x200, |
| xtag = 0x400, |
| xident = 0x800, |
| xvoid = 0x1000, |
| xbool = 0x4000, |
| ximaginary = 0x8000, |
| xcomplex = 0x10000, |
| x_Atomic = 0x20000, |
| } |
| |
| AST.Type t; |
| Loc loc; |
| //printf("parseDeclarationSpecifiers()\n"); |
| |
| TKW tkw; |
| SCW scw = specifier.scw & SCW.xtypedef; |
| MOD mod; |
| Identifier id; |
| Identifier previd; |
| |
| Lwhile: |
| while (1) |
| { |
| //printf("token %s\n", token.toChars()); |
| TKW tkwx; |
| SCW scwx; |
| MOD modx; |
| switch (token.value) |
| { |
| // Storage class specifiers |
| case TOK.static_: scwx = SCW.xstatic; break; |
| case TOK.extern_: scwx = SCW.xextern; break; |
| case TOK.auto_: scwx = SCW.xauto; break; |
| case TOK.register: scwx = SCW.xregister; break; |
| case TOK.typedef_: scwx = SCW.xtypedef; break; |
| case TOK.inline: scwx = SCW.xinline; break; |
| case TOK._Noreturn: scwx = SCW.x_Noreturn; break; |
| case TOK._Thread_local: scwx = SCW.x_Thread_local; break; |
| |
| // Type qualifiers |
| case TOK.const_: modx = MOD.xconst; break; |
| case TOK.volatile: modx = MOD.xvolatile; break; |
| case TOK.restrict: modx = MOD.xrestrict; break; |
| case TOK.__stdcall: modx = MOD.x__stdcall; break; |
| |
| // Type specifiers |
| case TOK.char_: tkwx = TKW.xchar; break; |
| case TOK.signed: tkwx = TKW.xsigned; break; |
| case TOK.unsigned: tkwx = TKW.xunsigned; break; |
| case TOK.int16: tkwx = TKW.xshort; break; |
| case TOK.int32: tkwx = TKW.xint; break; |
| case TOK.int64: tkwx = TKW.xlong; break; |
| case TOK.float32: tkwx = TKW.xfloat; break; |
| case TOK.float64: tkwx = TKW.xdouble; break; |
| case TOK.void_: tkwx = TKW.xvoid; break; |
| case TOK._Bool: tkwx = TKW.xbool; break; |
| case TOK._Imaginary: tkwx = TKW.ximaginary; break; |
| case TOK._Complex: tkwx = TKW.xcomplex; break; |
| |
| case TOK.identifier: |
| tkwx = TKW.xident; |
| id = token.ident; |
| break; |
| |
| case TOK.struct_: |
| case TOK.union_: |
| { |
| const structOrUnion = token.value; |
| const sloc = token.loc; |
| nextToken(); |
| |
| /* GNU Extensions |
| * struct-or-union-specifier: |
| * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt) |
| * struct-or-union gnu-attribute (opt) identifier |
| */ |
| if (token.value == TOK.__attribute__) |
| cparseGnuAttributes(specifier); |
| |
| t = cparseStruct(sloc, structOrUnion, symbols); |
| tkwx = TKW.xtag; |
| break; |
| } |
| |
| case TOK.enum_: |
| t = cparseEnum(symbols); |
| tkwx = TKW.xtag; |
| break; |
| |
| case TOK._Atomic: |
| { |
| // C11 6.7.2.4 |
| // type-specifier if followed by `( type-name )` |
| auto tk = peek(&token); |
| if (tk.value == TOK.leftParenthesis) |
| { |
| tk = peek(tk); |
| if (isTypeName(tk) && tk.value == TOK.rightParenthesis) |
| { |
| nextToken(); |
| t = cparseTypeName(); |
| // TODO - implement the "atomic" part of t |
| tkwx = TKW.x_Atomic; |
| break; |
| } |
| } |
| // C11 6.7.3 type-qualifier if not |
| modx = MOD.x_Atomic; |
| break; |
| } |
| |
| case TOK._Alignas: |
| { |
| /* C11 6.7.5 |
| * _Alignas ( type-name ) |
| * _Alignas ( constant-expression ) |
| */ |
| |
| if (level & (LVL.parameter | LVL.prototype)) |
| error("no alignment-specifier for parameters"); // C11 6.7.5-2 |
| |
| nextToken(); |
| check(TOK.leftParenthesis); |
| AST.Expression exp; |
| auto tk = &token; |
| if (isTypeName(tk)) // _Alignas ( type-name ) |
| { |
| auto talign = cparseTypeName(); |
| /* Convert type to expression: `talign.alignof` |
| */ |
| auto e = new AST.TypeExp(loc, talign); |
| exp = new AST.DotIdExp(loc, e, Id.__xalignof); |
| } |
| else // _Alignas ( constant-expression ) |
| { |
| exp = cparseConstantExp(); |
| } |
| |
| if (!specifier.alignExps) |
| specifier.alignExps = new AST.Expressions(0); |
| specifier.alignExps.push(exp); |
| |
| check(TOK.rightParenthesis); |
| break; |
| } |
| |
| case TOK.__attribute__: |
| { |
| /* GNU Extensions |
| * declaration-specifiers: |
| * gnu-attributes declaration-specifiers (opt) |
| */ |
| cparseGnuAttributes(specifier); |
| break; |
| } |
| |
| case TOK.__declspec: |
| { |
| /* Microsoft extension |
| */ |
| cparseDeclspec(specifier); |
| break; |
| } |
| |
| case TOK.typeof_: |
| { |
| nextToken(); |
| check(TOK.leftParenthesis); |
| |
| auto tk = &token; |
| AST.Expression e; |
| if (isTypeName(tk)) |
| e = new AST.TypeExp(loc, cparseTypeName()); |
| else |
| e = cparseExpression(); |
| t = new AST.TypeTypeof(loc, e); |
| |
| if(token.value == TOK.rightParenthesis) |
| nextToken(); |
| else |
| { |
| t = AST.Type.terror; |
| error("`typeof` operator expects an expression or type name in parentheses"); |
| |
| // skipParens et. al expect to be on the opening parenthesis |
| int parens; |
| loop: while(1) |
| { |
| switch(token.value) |
| { |
| case TOK.leftParenthesis: |
| parens++; |
| break; |
| case TOK.rightParenthesis: |
| parens--; |
| if(parens < 0) |
| goto case; |
| break; |
| case TOK.endOfFile: |
| break loop; |
| default: |
| } |
| nextToken(); |
| } |
| } |
| |
| tkwx = TKW.xtag; |
| break; |
| } |
| |
| default: |
| break Lwhile; |
| } |
| |
| if (tkwx) |
| { |
| if (tkw & TKW.xlong && tkwx & TKW.xlong) |
| { |
| tkw &= ~TKW.xlong; |
| tkwx = TKW.xllong; |
| } |
| if (tkw && tkwx & TKW.xident) |
| { |
| // 2nd identifier can't be a typedef |
| break Lwhile; // leave parser on the identifier for the following declarator |
| } |
| else if (tkwx & TKW.xident) |
| { |
| // 1st identifier, save it for TypeIdentifier |
| previd = id; |
| } |
| if (tkw & TKW.xident && tkwx || // typedef-name followed by type-specifier |
| tkw & tkwx) // duplicate type-specifiers |
| { |
| error("illegal combination of type specifiers"); |
| tkwx = TKW.init; |
| } |
| tkw |= tkwx; |
| if (!(tkwx & TKW.xtag)) // if parser already advanced |
| nextToken(); |
| continue; |
| } |
| |
| if (modx) |
| { |
| mod |= modx; |
| nextToken(); |
| continue; |
| } |
| |
| if (scwx) |
| { |
| if (scw & scwx) |
| error("duplicate storage class"); |
| scw |= scwx; |
| // C11 6.7.1-2 At most one storage-class may be given, except that |
| // _Thread_local may appear with static or extern. |
| const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef); |
| if (scw2 & (scw2 - 1) || |
| scw & (SCW.x_Thread_local) && scw & (SCW.xauto | SCW.xregister | SCW.xtypedef)) |
| { |
| error("multiple storage classes in declaration specifiers"); |
| scw &= ~scwx; |
| } |
| if (level == LVL.local && |
| scw & (SCW.x_Thread_local) && scw & (SCW.xinline | SCW.x_Noreturn)) |
| { |
| error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`"); |
| scw &= ~scwx; |
| } |
| if (level & (LVL.parameter | LVL.prototype) && |
| scw & ~SCW.xregister) |
| { |
| error("only `register` storage class allowed for function parameters"); |
| scw &= ~scwx; |
| } |
| if (level == LVL.global && |
| scw & (SCW.xauto | SCW.xregister)) |
| { |
| error("`auto` and `register` storage class not allowed for global"); |
| scw &= ~scwx; |
| } |
| nextToken(); |
| continue; |
| } |
| } |
| |
| specifier.scw = scw; |
| specifier.mod = mod; |
| |
| // Convert TKW bits to type t |
| switch (tkw) |
| { |
| case TKW.xnone: t = null; break; |
| |
| case TKW.xchar: t = AST.Type.tchar; break; |
| case TKW.xsigned | TKW.xchar: t = AST.Type.tint8; break; |
| case TKW.xunsigned | TKW.xchar: t = AST.Type.tuns8; break; |
| |
| case TKW.xshort: |
| case TKW.xsigned | TKW.xshort: |
| case TKW.xsigned | TKW.xshort | TKW.xint: |
| case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break; |
| |
| case TKW.xunsigned | TKW.xshort | TKW.xint: |
| case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break; |
| |
| case TKW.xint: |
| case TKW.xsigned: |
| case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break; |
| |
| case TKW.xunsigned: |
| case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break; |
| |
| case TKW.xlong: |
| case TKW.xsigned | TKW.xlong: |
| case TKW.xsigned | TKW.xlong | TKW.xint: |
| case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break; |
| |
| case TKW.xunsigned | TKW.xlong | TKW.xint: |
| case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break; |
| |
| case TKW.xllong: |
| case TKW.xsigned | TKW.xllong: |
| case TKW.xsigned | TKW.xllong | TKW.xint: |
| case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break; |
| |
| case TKW.xunsigned | TKW.xllong | TKW.xint: |
| case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break; |
| |
| case TKW.xvoid: t = AST.Type.tvoid; break; |
| case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break; |
| |
| case TKW.xfloat: t = AST.Type.tfloat32; break; |
| case TKW.xdouble: t = AST.Type.tfloat64; break; |
| case TKW.xlong | TKW.xdouble: t = realType(RTFlags.realfloat); break; |
| |
| case TKW.ximaginary | TKW.xfloat: t = AST.Type.timaginary32; break; |
| case TKW.ximaginary | TKW.xdouble: t = AST.Type.timaginary64; break; |
| case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break; |
| |
| case TKW.xcomplex | TKW.xfloat: t = AST.Type.tcomplex32; break; |
| case TKW.xcomplex | TKW.xdouble: t = AST.Type.tcomplex64; break; |
| case TKW.xcomplex | TKW.xlong | TKW.xdouble: t = realType(RTFlags.complex); break; |
| |
| case TKW.xident: |
| { |
| const idx = previd.toString(); |
| if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore |
| importBuiltins = true; // probably one of those compiler extensions |
| t = null; |
| |
| /* Punch through to what the typedef is, to support things like: |
| * typedef T* T; |
| */ |
| auto pt = lookupTypedef(previd); |
| if (pt && *pt) // if previd is a known typedef |
| t = *pt; |
| |
| if (!t) |
| t = new AST.TypeIdentifier(loc, previd); |
| break; |
| } |
| |
| case TKW.xtag: |
| break; // t is already set |
| |
| default: |
| error("illegal type combination"); |
| t = AST.Type.terror; |
| break; |
| } |
| |
| return t; |
| } |
| |
| /******************************** |
| * C11 6.7.6 |
| * Parse a declarator (including function definitions). |
| * declarator: |
| * pointer (opt) direct-declarator |
| * |
| * direct-declarator : |
| * identifier |
| * ( declarator ) |
| * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ] |
| * direct-declarator [ static type-qualifier-list (opt) assignment-expression ] |
| * direct-declarator [ type-qualifier-list static assignment-expression (opt) ] |
| * direct-declarator [ type-qualifier-list (opt) * ] |
| * direct-declarator ( parameter-type-list ) |
| * direct-declarator ( identifier-list (opt) ) |
| * |
| * pointer : |
| * * type-qualifier-list (opt) |
| * * type-qualifier-list (opt) pointer |
| * |
| * type-qualifier-list : |
| * type-qualifier |
| * type-qualifier-list type-qualifier |
| * |
| * parameter-type-list : |
| * parameter-list |
| * parameter-list , ... |
| * |
| * parameter-list : |
| * parameter-declaration |
| * parameter-list , parameter-declaration |
| * |
| * parameter-declaration : |
| * declaration-specifiers declarator |
| * declaration-specifiers abstract-declarator (opt) |
| * |
| * identifier-list : |
| * identifier |
| * identifier-list , identifier |
| * |
| * Params: |
| * declarator = declarator kind |
| * t = base type to start with |
| * pident = set to Identifier if there is one, null if not |
| * specifier = specifiers in and out |
| * Returns: |
| * type declared. If a TypeFunction is returned, this.symbols is the |
| * symbol table for the parameter-type-list, which will contain any |
| * declared struct, union or enum tags. |
| */ |
| private AST.Type cparseDeclarator(DTR declarator, AST.Type t, |
| out Identifier pident, ref Specifier specifier) |
| { |
| //printf("cparseDeclarator(%d, %p)\n", declarator, t); |
| AST.Types constTypes; // all the Types that will need `const` applied to them |
| |
| AST.Type parseDecl(AST.Type t) |
| { |
| AST.Type ts; |
| while (1) |
| { |
| switch (token.value) |
| { |
| case TOK.identifier: // identifier |
| //printf("identifier %s\n", token.ident.toChars()); |
| if (declarator == DTR.xabstract) |
| error("identifier not allowed in abstract-declarator"); |
| pident = token.ident; |
| ts = t; |
| nextToken(); |
| break; |
| |
| case TOK.leftParenthesis: // ( declarator ) |
| /* like: T (*fp)(); |
| * T ((*fp))(); |
| */ |
| nextToken(); |
| |
| if (token.value == TOK.__stdcall) // T (__stdcall*fp)(); |
| { |
| specifier.mod |= MOD.x__stdcall; |
| nextToken(); |
| } |
| |
| ts = parseDecl(t); |
| check(TOK.rightParenthesis); |
| break; |
| |
| case TOK.mul: // pointer |
| t = new AST.TypePointer(t); |
| nextToken(); |
| // add post fixes const/volatile/restrict/_Atomic |
| const mod = cparseTypeQualifierList(); |
| if (mod & MOD.xconst) |
| constTypes.push(t); |
| if (token.value == TOK.__attribute__) |
| cparseGnuAttributes(specifier); |
| continue; |
| |
| default: |
| if (declarator == DTR.xdirect) |
| { |
| if (!t || t.isTypeIdentifier()) |
| { |
| // const arr[1]; |
| error("no type-specifier for declarator"); |
| t = AST.Type.tint32; |
| } |
| else |
| error("identifier or `(` expected"); // ) |
| panic(); |
| } |
| ts = t; |
| break; |
| } |
| break; |
| } |
| |
| // parse DeclaratorSuffixes |
| while (1) |
| { |
| /* Insert tx -> t into |
| * ts -> ... -> t |
| * so that |
| * ts -> ... -> tx -> t |
| */ |
| static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t) |
| { |
| AST.Type* pt; |
| for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next) |
| { |
| } |
| *pt = tx; |
| } |
| |
| switch (token.value) |
| { |
| case TOK.leftBracket: |
| { |
| // post [] syntax, pick up any leading type qualifiers, `static` and `*` |
| AST.Type ta; |
| nextToken(); |
| |
| auto mod = cparseTypeQualifierList(); // const/volatile/restrict/_Atomic |
| |
| bool isStatic; |
| bool isVLA; |
| if (token.value == TOK.static_) |
| { |
| isStatic = true; // `static` |
| nextToken(); |
| if (!mod) // type qualifiers after `static` |
| mod = cparseTypeQualifierList(); |
| } |
| else if (token.value == TOK.mul) |
| { |
| if (peekNext() == TOK.rightBracket) |
| { |
| isVLA = true; // `*` |
| nextToken(); |
| } |
| } |
| |
| if (isStatic || token.value != TOK.rightBracket) |
| { |
| //printf("It's a static array\n"); |
| AST.Expression e = cparseAssignExp(); // [ expression ] |
| ta = new AST.TypeSArray(t, e); |
| } |
| else |
| { |
| /* C11 6.7.6.2-4 An [ ] array is an incomplete array type |
| */ |
| ta = new AST.TypeSArray(t); |
| } |
| check(TOK.rightBracket); |
| |
| // Issue errors for unsupported types. |
| if (isVLA) // C11 6.7.6.2 |
| { |
| error("variable length arrays are not supported"); |
| } |
| if (isStatic) // C11 6.7.6.3 |
| { |
| error("static array parameters are not supported"); |
| } |
| if (declarator != DTR.xparameter) |
| { |
| /* C11 6.7.6.2-4: '*' can only be used with function prototype scope. |
| */ |
| if (isVLA) |
| error("variable length array used outside of function prototype"); |
| /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear |
| * in a declaration of a function parameter with an array type. |
| */ |
| if (isStatic || mod) |
| error("static or type qualifier used outside of function prototype"); |
| } |
| if (ts.isTypeSArray() || ts.isTypeDArray()) |
| { |
| /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear |
| * in the outermost array type derivation. |
| */ |
| if (isStatic || mod) |
| error("static or type qualifier used in non-outermost array type derivation"); |
| /* C11 6.7.6.2-1: the element type shall not be an incomplete or |
| * function type. |
| */ |
| if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA) |
| error("array type has incomplete element type `%s`", ta.toChars()); |
| } |
| |
| // Apply type qualifiers to the constructed type. |
| if (mod & MOD.xconst) // ignore the other bits |
| ta = toConst(ta); |
| insertTx(ts, ta, t); // ts -> ... -> ta -> t |
| continue; |
| } |
| |
| case TOK.leftParenthesis: |
| { |
| // New symbol table for parameter-list |
| auto symbolsSave = this.symbols; |
| this.symbols = null; |
| |
| auto parameterList = cparseParameterList(); |
| const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; |
| AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, 0); |
| // tf = tf.addSTC(storageClass); // TODO |
| insertTx(ts, tf, t); // ts -> ... -> tf -> t |
| |
| if (ts != tf) |
| this.symbols = symbolsSave; |
| break; |
| } |
| |
| default: |
| break; |
| } |
| break; |
| } |
| return ts; |
| } |
| |
| t = parseDecl(t); |
| |
| /* Because const is transitive, cannot assemble types from |
| * fragments. Instead, types to be annotated with const are put |
| * in constTypes[], and a bottom up scan of t is done to apply |
| * const |
| */ |
| if (constTypes.length) |
| { |
| AST.Type constApply(AST.Type t) |
| { |
| if (t.nextOf()) |
| { |
| auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this |
| tn.next = constApply(tn.next); |
| } |
| foreach (tc; constTypes[]) |
| { |
| if (tc is t) |
| { |
| return toConst(t); |
| } |
| } |
| return t; |
| } |
| |
| if (declarator == DTR.xparameter && |
| t.isTypePointer()) |
| { |
| /* Because there are instances in .h files of "const pointer to mutable", |
| * skip applying transitive `const` |
| * https://issues.dlang.org/show_bug.cgi?id=22534 |
| */ |
| auto tn = cast(AST.TypeNext)t; |
| tn.next = constApply(tn.next); |
| } |
| else |
| t = constApply(t); |
| } |
| |
| //printf("result: %s\n", t.toChars()); |
| return t; |
| } |
| |
| /****************************** |
| * C11 6.7.3 |
| * type-qualifier: |
| * const |
| * restrict |
| * volatile |
| * _Atomic |
| * __stdcall |
| */ |
| MOD cparseTypeQualifierList() |
| { |
| MOD mod; |
| while (1) |
| { |
| switch (token.value) |
| { |
| case TOK.const_: mod |= MOD.xconst; break; |
| case TOK.volatile: mod |= MOD.xvolatile; break; |
| case TOK.restrict: mod |= MOD.xrestrict; break; |
| case TOK._Atomic: mod |= MOD.x_Atomic; break; |
| case TOK.__stdcall: mod |= MOD.x__stdcall; break; |
| |
| default: |
| return mod; |
| } |
| nextToken(); |
| } |
| } |
| |
| /*********************************** |
| * C11 6.7.7 |
| */ |
| AST.Type cparseTypeName() |
| { |
| Specifier specifier; |
| specifier.packalign.setDefault(); |
| auto tspec = cparseSpecifierQualifierList(LVL.global, specifier); |
| if (!tspec) |
| { |
| error("type-specifier is missing"); |
| tspec = AST.Type.tint32; |
| } |
| if (tspec && specifier.mod & MOD.xconst) |
| { |
| tspec = toConst(tspec); |
| specifier.mod = MOD.xnone; // 'used' it |
| } |
| Identifier id; |
| return cparseDeclarator(DTR.xabstract, tspec, id, specifier); |
| } |
| |
| /*********************************** |
| * C11 6.7.2.1 |
| * specifier-qualifier-list: |
| * type-specifier specifier-qualifier-list (opt) |
| * type-qualifier specifier-qualifier-list (opt) |
| * Params: |
| * level = declaration context |
| * specifier = specifiers in and out |
| * Returns: |
| * resulting type, null if not specified |
| */ |
| AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier) |
| { |
| auto t = cparseDeclarationSpecifiers(level, specifier); |
| if (specifier.scw) |
| error("storage class not allowed in specifier-qualified-list"); |
| return t; |
| } |
| |
| /*********************************** |
| * C11 6.7.6.3 |
| * ( parameter-type-list ) |
| * ( identifier-list (opt) ) |
| */ |
| AST.ParameterList cparseParameterList() |
| { |
| auto parameters = new AST.Parameters(); |
| AST.VarArg varargs = AST.VarArg.none; |
| StorageClass varargsStc; |
| |
| check(TOK.leftParenthesis); |
| if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void) |
| { |
| nextToken(); |
| nextToken(); |
| return AST.ParameterList(parameters, varargs, varargsStc); |
| } |
| |
| if (token.value == TOK.rightParenthesis) // func() |
| { |
| nextToken(); |
| importBuiltins = true; // will need __va_list_tag |
| return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc); |
| } |
| |
| /* Create function prototype scope |
| */ |
| typedefTab.push(null); |
| |
| AST.ParameterList finish() |
| { |
| typedefTab.pop(); |
| return AST.ParameterList(parameters, varargs, varargsStc); |
| } |
| |
| /* The check for identifier-list comes later, |
| * when doing the trailing declaration-list (opt) |
| */ |
| while (1) |
| { |
| if (token.value == TOK.rightParenthesis) |
| break; |
| if (token.value == TOK.dotDotDot) |
| { |
| if (parameters.length == 0) // func(...) |
| error("named parameter required before `...`"); |
| importBuiltins = true; // will need __va_list_tag |
| varargs = AST.VarArg.variadic; // C-style variadics |
| nextToken(); |
| check(TOK.rightParenthesis); |
| return finish(); |
| } |
| |
| Specifier specifier; |
| specifier.packalign.setDefault(); |
| auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier); |
| if (!tspec) |
| { |
| error("no type-specifier for parameter"); |
| tspec = AST.Type.tint32; |
| } |
| |
| if (specifier.mod & MOD.xconst) |
| { |
| if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) && |
| tspec.isTypeIdentifier()) |
| error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars()); |
| |
| tspec = toConst(tspec); |
| specifier.mod = MOD.xnone; // 'used' it |
| } |
| |
| Identifier id; |
| auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier); |
| if (token.value == TOK.__attribute__) |
| cparseGnuAttributes(specifier); |
| if (specifier.mod & MOD.xconst) |
| t = toConst(t); |
| auto param = new AST.Parameter(specifiersToSTC(LVL.parameter, specifier), |
| t, id, null, null); |
| parameters.push(param); |
| if (token.value == TOK.rightParenthesis) |
| break; |
| check(TOK.comma); |
| } |
| nextToken(); |
| return finish(); |
| } |
| |
| /*********************************** |
| * C11 6.7.10 |
| * _Static_assert ( constant-expression , string-literal ) ; |
| */ |
| private AST.StaticAssert cparseStaticAssert() |
| { |
| const loc = token.loc; |
| |
| //printf("cparseStaticAssert()\n"); |
| nextToken(); |
| check(TOK.leftParenthesis); |
| auto exp = cparseConstantExp(); |
| check(TOK.comma); |
| if (token.value != TOK.string_) |
| error("string literal expected"); |
| auto msg = cparsePrimaryExp(); |
| check(TOK.rightParenthesis); |
| check(TOK.semicolon); |
| return new AST.StaticAssert(loc, exp, msg); |
| } |
| |
| /************************* |
| * Collect argument list. |
| * Parser is on opening parenthesis. |
| * Returns: |
| * the arguments |
| */ |
| private AST.Expressions* cparseArguments() |
| { |
| nextToken(); |
| auto arguments = new AST.Expressions(); |
| while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile) |
| { |
| auto arg = cparseAssignExp(); |
| arguments.push(arg); |
| if (token.value != TOK.comma) |
| break; |
| |
| nextToken(); // consume comma |
| } |
| |
| check(TOK.rightParenthesis); |
| |
| return arguments; |
| } |
| |
| /************************* |
| * __declspec parser |
| * https://docs.microsoft.com/en-us/cpp/cpp/declspec |
| * decl-specifier: |
| * __declspec ( extended-decl-modifier-seq ) |
| * |
| * extended-decl-modifier-seq: |
| * extended-decl-modifier (opt) |
| * extended-decl-modifier extended-decl-modifier-seq |
| * |
| * extended-decl-modifier: |
| * dllimport |
| * dllexport |
| * noreturn |
| * Params: |
| * specifier = filled in with the attribute(s) |
| */ |
| private void cparseDeclspec(ref Specifier specifier) |
| { |
| //printf("cparseDeclspec()\n"); |
| /* Check for dllexport, dllimport |
| * Ignore the rest |
| */ |
| bool dllimport; // TODO implement |
| bool dllexport; // TODO implement |
| nextToken(); // move past __declspec |
| check(TOK.leftParenthesis); |
| while (1) |
| { |
| if (token.value == TOK.rightParenthesis) |
| { |
| nextToken(); |
| break; |
| } |
| else if (token.value == TOK.endOfFile) |
| break; |
| else if (token.value == TOK.identifier) |
| { |
| if (token.ident == Id.dllimport) |
| { |
| dllimport = true; |
| nextToken(); |
| } |
| else if (token.ident == Id.dllexport) |
| { |
| dllexport = true; |
| nextToken(); |
| } |
|