blob: ad1ad6791c56fd47d72d9c607f9dcf6d8550a817 [file] [log] [blame]
/**
* 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();
}