
D front-end changes: - Import dmd v2.104.1. - Deprecation phase ended for access to private method when overloaded with public method. D runtime changes: - Import druntime v2.104.1. - Linux input header translations were added to druntime. - Integration with the Valgrind `memcheck' tool has been added to the garbage collector. Phobos changes: - Import phobos v2.104.1. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd a88e1335f7. * dmd/VERSION: Bump version to v2.104.1. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime a88e1335f7. * src/MERGE: Merge upstream phobos 1921d29df. * config.h.in: Regenerate. * configure: Regenerate. * configure.ac (libphobos-checking): Add valgrind flag. (DRUNTIME_LIBRARIES_VALGRIND): Call. * libdruntime/Makefile.am (DRUNTIME_CSOURCES): Add etc/valgrind/valgrind_.c. (DRUNTIME_DSOURCES): Add etc/valgrind/valgrind.d. (DRUNTIME_DSOURCES_LINUX): Add core/sys/linux/input.d, core/sys/linux/input_event_codes.d, core/sys/linux/uinput.d. * libdruntime/Makefile.in: Regenerate. * m4/druntime/libraries.m4 (DRUNTIME_LIBRARIES_VALGRIND): Define.
5878 lines
190 KiB
D
5878 lines
190 KiB
D
/**
|
|
* Takes a token stream from the lexer, and parses it into an abstract syntax tree.
|
|
*
|
|
* Specification: C11
|
|
*
|
|
* Copyright: Copyright (C) 1999-2023 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 : memcpy;
|
|
|
|
import dmd.astenums;
|
|
import dmd.errorsink;
|
|
import dmd.id;
|
|
import dmd.identifier;
|
|
import dmd.lexer;
|
|
import dmd.location;
|
|
import dmd.parse;
|
|
import dmd.root.array;
|
|
import dmd.common.outbuffer;
|
|
import dmd.root.rmem;
|
|
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,
|
|
ErrorSink errorSink,
|
|
const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope
|
|
{
|
|
const bool doUnittests = false;
|
|
super(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
|
|
|
|
//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(0);
|
|
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:
|
|
case TOK._assert:
|
|
Lexp:
|
|
auto exp = cparseExpression();
|
|
if (token.value == TOK.identifier && exp.op == EXP.identifier)
|
|
{
|
|
error(token.loc, "found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
|
|
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.__int128:
|
|
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.__thread:
|
|
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:
|
|
|
|
case TOK.__attribute__:
|
|
|
|
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.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();
|
|
AST.Expression expHigh;
|
|
if (token.value == TOK.dotDotDot)
|
|
{
|
|
/* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
|
|
*/
|
|
nextToken();
|
|
expHigh = 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.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(0);
|
|
}
|
|
s = new AST.ScopeStatement(loc, s, token.loc);
|
|
if (expHigh)
|
|
s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
|
|
else
|
|
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.curlyScope));
|
|
}
|
|
s = new AST.CompoundStatement(loc, statements);
|
|
}
|
|
else
|
|
s = cparseStatement(0);
|
|
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_:
|
|
switch (peekNext())
|
|
{
|
|
case TOK.goto_:
|
|
case TOK.inline:
|
|
case TOK.volatile:
|
|
case TOK.leftParenthesis:
|
|
s = cparseGnuAsm();
|
|
break;
|
|
|
|
default:
|
|
// ImportC extensions: parse as a D asm block.
|
|
s = parseAsm();
|
|
break;
|
|
}
|
|
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(token.loc, "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();
|
|
if (token.value == TOK.leftCurly)
|
|
e = cparseStatementExpression(); // gcc extension
|
|
else
|
|
e = cparseExpression();
|
|
check(TOK.rightParenthesis);
|
|
break;
|
|
|
|
case TOK._Generic:
|
|
e = cparseGenericSelection();
|
|
break;
|
|
|
|
case TOK._assert: // __check(assign-exp) extension
|
|
nextToken();
|
|
check(TOK.leftParenthesis, "`__check`");
|
|
e = parseAssignExp();
|
|
check(TOK.rightParenthesis);
|
|
e = new AST.AssertExp(loc, e, null);
|
|
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;
|
|
}
|
|
|
|
/*****************************
|
|
* gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
|
|
* Represent as a function literal, then call the function literal.
|
|
* Parser is on opening curly brace.
|
|
*/
|
|
private AST.Expression cparseStatementExpression()
|
|
{
|
|
AST.ParameterList parameterList;
|
|
StorageClass stc = 0;
|
|
const loc = token.loc;
|
|
typedefTab.push(null);
|
|
auto fbody = cparseStatement(ParseStatementFlags.scope_);
|
|
typedefTab.pop(); // end of function scope
|
|
|
|
// Rewrite last ExpStatement (if there is one) as a ReturnStatement
|
|
auto ss = fbody.isScopeStatement();
|
|
auto cs = ss.statement.isCompoundStatement();
|
|
assert(cs);
|
|
if (const len = (*cs.statements).length)
|
|
{
|
|
auto s = (*cs.statements)[len - 1];
|
|
if (auto es = s.isExpStatement())
|
|
(*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
|
|
}
|
|
|
|
auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
|
|
auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
|
|
fd.fbody = fbody;
|
|
|
|
auto fe = new AST.FuncExp(loc, fd);
|
|
auto args = new AST.Expressions();
|
|
auto e = new AST.CallExp(loc, fe, args); // call the function literal
|
|
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);
|
|
|
|
AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
|
|
{
|
|
/* `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);
|
|
if (!tt.packalign.isUnknown())
|
|
{
|
|
// saw `struct __declspec(align(N)) Tag ...`
|
|
auto st = stag.isStructDeclaration();
|
|
st.alignment = tt.packalign;
|
|
}
|
|
stag.members = tt.members;
|
|
tt.members = null;
|
|
if (!symbols)
|
|
symbols = new AST.Dsymbols();
|
|
auto stags = applySpecifier(stag, specifier);
|
|
symbols.push(stags);
|
|
return stags;
|
|
}
|
|
|
|
/* 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
|
|
|
|
auto stags = declareTag(tt, specifier);
|
|
|
|
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", stags.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 = cparseGnuAsmLabel();
|
|
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_)
|
|
{
|
|
if (!tt.id && id)
|
|
tt.id = id;
|
|
Specifier spec;
|
|
auto stag = declareTag(tt, spec);
|
|
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 (auto tt = dt.isTypeTag())
|
|
{
|
|
if (tt.members && (tt.id || tt.tok == TOK.enum_))
|
|
{
|
|
Specifier spec;
|
|
declareTag(tt, spec);
|
|
}
|
|
}
|
|
|
|
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);
|
|
specifiersToFuncDeclaration(fd, specifier);
|
|
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);
|
|
auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
|
|
specifiersToVarDeclaration(vd, specifier);
|
|
s = vd;
|
|
}
|
|
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(token.loc, "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.KRvariadic; // but C11 allows extra arguments
|
|
auto plLength = pl.length;
|
|
if (symbols.length != plLength)
|
|
error(token.loc, "%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 ad = s.isAttribDeclaration();
|
|
if (ad)
|
|
s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration
|
|
|
|
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);
|
|
specifiersToFuncDeclaration(fd, specifier);
|
|
|
|
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,
|
|
xint128 = 0x40000,
|
|
}
|
|
|
|
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:
|
|
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.__int128: tkwx = TKW.xint128; 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();
|
|
|
|
Specifier tagSpecifier;
|
|
|
|
/* 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
|
|
*/
|
|
while (1)
|
|
{
|
|
if (token.value == TOK.__attribute__)
|
|
cparseGnuAttributes(tagSpecifier);
|
|
else if (token.value == TOK.__declspec)
|
|
cparseDeclspec(tagSpecifier);
|
|
else if (token.value == TOK.__pragma)
|
|
uupragmaDirective(sloc);
|
|
else
|
|
break;
|
|
}
|
|
t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, 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();
|
|
nextToken();
|
|
t = cparseTypeName();
|
|
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.local &&
|
|
scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
|
|
{
|
|
error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
|
|
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.xint128:
|
|
case TKW.xsigned | TKW.xint128: t = integerTypeForSize(16); break;
|
|
|
|
case TKW.xunsigned | TKW.xint128: t = unsignedTypeForSize(16); 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:
|
|
case TKW.x_Atomic: // no atomics for you
|
|
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
|
|
* tbase = 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 tbase,
|
|
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
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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;
|
|
StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
|
|
if (specifier._pure)
|
|
stc |= STC.pure_;
|
|
AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
|
|
// tf = tf.addSTC(storageClass); // TODO
|
|
insertTx(ts, tf, t); // ts -> ... -> tf -> t
|
|
|
|
if (ts != tf)
|
|
this.symbols = symbolsSave;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return ts;
|
|
}
|
|
|
|
auto t = parseDecl(tbase);
|
|
|
|
if (specifier.vector_size)
|
|
{
|
|
auto length = new AST.IntegerExp(token.loc, specifier.vector_size / tbase.size(), AST.Type.tuns32);
|
|
auto tsa = new AST.TypeSArray(tbase, length);
|
|
AST.Type tv = new AST.TypeVector(tsa);
|
|
specifier.vector_size = 0; // used it up
|
|
|
|
insertTx(t, tv, tbase); // replace tbase with tv
|
|
}
|
|
|
|
/* 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();
|
|
return AST.ParameterList(parameters, AST.VarArg.KRvariadic, 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 || token.value == TOK.endOfFile)
|
|
break;
|
|
check(TOK.comma);
|
|
}
|
|
check(TOK.rightParenthesis);
|
|
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:
|
|
* align(number)
|
|
* deprecated(depMsg)
|
|
* dllimport
|
|
* dllexport
|
|
* naked
|
|
* noinline
|
|
* noreturn
|
|
* nothrow
|
|
* thread
|
|
* Params:
|
|
* specifier = filled in with the attribute(s)
|
|
*/
|
|
private void cparseDeclspec(ref Specifier specifier)
|
|
{
|
|
//printf("cparseDeclspec()\n");
|
|
/* Check for dllexport, dllimport
|
|
* Ignore the rest
|
|
*/
|
|
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)
|
|
{
|
|
specifier.dllimport = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.dllexport)
|
|
{
|
|
specifier.dllexport = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.naked)
|
|
{
|
|
specifier.naked = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.noinline)
|
|
{
|
|
specifier.scw |= SCW.xnoinline;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.noreturn)
|
|
{
|
|
specifier.noreturn = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id._nothrow)
|
|
{
|
|
specifier._nothrow = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.thread)
|
|
{
|
|
specifier.scw |= SCW.x_Thread_local;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id._align)
|
|
{
|
|
// Microsoft spec is very imprecise as to how this actually works
|
|
nextToken();
|
|
check(TOK.leftParenthesis);
|
|
if (token.value == TOK.int32Literal)
|
|
{
|
|
const n = token.unsvalue;
|
|
if (n < 1 || n & (n - 1) || 8192 < n)
|
|
error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
|
|
specifier.packalign.set(cast(uint)n);
|
|
specifier.packalign.setPack(true);
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
error("alignment value expected, not `%s`", token.toChars());
|
|
nextToken();
|
|
}
|
|
|
|
check(TOK.rightParenthesis);
|
|
}
|
|
else if (token.ident == Id._deprecated)
|
|
{
|
|
specifier._deprecated = true;
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis) // optional deprecation message
|
|
{
|
|
nextToken();
|
|
specifier.depMsg = cparseExpression();
|
|
check(TOK.rightParenthesis);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis)
|
|
cparseParens();
|
|
}
|
|
}
|
|
else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
|
|
nextToken();
|
|
else
|
|
{
|
|
error("extended-decl-modifier expected");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************
|
|
* Parser for asm label. It appears after the declarator, and has apparently
|
|
* nothing to do with inline assembler.
|
|
* https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
|
|
* simple-asm-expr:
|
|
* asm ( asm-string-literal )
|
|
*
|
|
* asm-string-literal:
|
|
* string-literal
|
|
*/
|
|
private AST.StringExp cparseGnuAsmLabel()
|
|
{
|
|
nextToken(); // move past asm
|
|
check(TOK.leftParenthesis);
|
|
if (token.value != TOK.string_)
|
|
error("string literal expected for Asm Label, not `%s`", token.toChars());
|
|
auto label = cparsePrimaryExp();
|
|
check(TOK.rightParenthesis);
|
|
return cast(AST.StringExp) label;
|
|
}
|
|
|
|
/********************
|
|
* Parse C inline assembler statement in Gnu format.
|
|
* https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
|
|
* asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
|
|
* Current token is on the `asm`.
|
|
* Returns:
|
|
* inline assembler expression as a Statement
|
|
*/
|
|
private AST.Statement cparseGnuAsm()
|
|
{
|
|
// Defer parsing of AsmStatements until semantic processing.
|
|
const loc = token.loc;
|
|
|
|
nextToken();
|
|
|
|
// Consume all asm-qualifiers. As a future optimization, we could record
|
|
// the `inline` and `volatile` storage classes against the statement.
|
|
while (token.value == TOK.goto_ ||
|
|
token.value == TOK.inline ||
|
|
token.value == TOK.volatile)
|
|
nextToken();
|
|
|
|
check(TOK.leftParenthesis);
|
|
if (token.value != TOK.string_)
|
|
error("string literal expected for Assembler Template, not `%s`", token.toChars());
|
|
Token* toklist = null;
|
|
Token** ptoklist = &toklist;
|
|
//Identifier label = null;
|
|
auto statements = new AST.Statements();
|
|
|
|
int parens;
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOK.leftParenthesis:
|
|
++parens;
|
|
goto default;
|
|
|
|
case TOK.rightParenthesis:
|
|
--parens;
|
|
if (parens >= 0)
|
|
goto default;
|
|
break;
|
|
|
|
case TOK.semicolon:
|
|
error("matching `)` expected, not `;`");
|
|
break;
|
|
|
|
case TOK.endOfFile:
|
|
/* ( */
|
|
error("matching `)` expected, not end of file");
|
|
break;
|
|
|
|
case TOK.colonColon: // treat as two separate : tokens for iasmgcc
|
|
*ptoklist = allocateToken();
|
|
memcpy(*ptoklist, &token, Token.sizeof);
|
|
(*ptoklist).value = TOK.colon;
|
|
ptoklist = &(*ptoklist).next;
|
|
|
|
*ptoklist = allocateToken();
|
|
memcpy(*ptoklist, &token, Token.sizeof);
|
|
(*ptoklist).value = TOK.colon;
|
|
ptoklist = &(*ptoklist).next;
|
|
|
|
*ptoklist = null;
|
|
nextToken();
|
|
continue;
|
|
|
|
default:
|
|
*ptoklist = allocateToken();
|
|
memcpy(*ptoklist, &token, Token.sizeof);
|
|
ptoklist = &(*ptoklist).next;
|
|
*ptoklist = null;
|
|
nextToken();
|
|
continue;
|
|
}
|
|
if (toklist)
|
|
{
|
|
// Create AsmStatement from list of tokens we've saved
|
|
AST.Statement s = new AST.AsmStatement(token.loc, toklist);
|
|
statements.push(s);
|
|
}
|
|
break;
|
|
}
|
|
nextToken();
|
|
auto s = new AST.CompoundAsmStatement(loc, statements, 0);
|
|
return s;
|
|
}
|
|
|
|
/*************************
|
|
* __attribute__ parser
|
|
* https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
|
|
* gnu-attributes:
|
|
* gnu-attributes gnu-attribute-specifier
|
|
*
|
|
* gnu-attribute-specifier:
|
|
* __attribute__ (( gnu-attribute-list ))
|
|
*
|
|
* gnu-attribute-list:
|
|
* gnu-attribute (opt)
|
|
* gnu-attribute-list , gnu-attribute
|
|
*
|
|
* Params:
|
|
* specifier = filled in with the attribute(s)
|
|
*/
|
|
private void cparseGnuAttributes(ref Specifier specifier)
|
|
{
|
|
while (token.value == TOK.__attribute__)
|
|
{
|
|
nextToken(); // move past __attribute__
|
|
check(TOK.leftParenthesis);
|
|
check(TOK.leftParenthesis);
|
|
|
|
if (token.value != TOK.rightParenthesis)
|
|
{
|
|
while (1)
|
|
{
|
|
cparseGnuAttribute(specifier);
|
|
if (token.value != TOK.comma)
|
|
break;
|
|
nextToken();
|
|
}
|
|
}
|
|
|
|
check(TOK.rightParenthesis);
|
|
check(TOK.rightParenthesis);
|
|
}
|
|
}
|
|
|
|
/*************************
|
|
* Parse a single GNU attribute
|
|
* gnu-attribute:
|
|
* gnu-attribute-name
|
|
* gnu-attribute-name ( identifier )
|
|
* gnu-attribute-name ( identifier , expression-list )
|
|
* gnu-attribute-name ( expression-list (opt) )
|
|
*
|
|
* gnu-attribute-name:
|
|
* keyword
|
|
* identifier
|
|
*
|
|
* expression-list:
|
|
* constant-expression
|
|
* expression-list , constant-expression
|
|
*
|
|
* Params:
|
|
* specifier = filled in with the attribute(s)
|
|
*/
|
|
private void cparseGnuAttribute(ref Specifier specifier)
|
|
{
|
|
/* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
|
|
* Ignore the rest
|
|
*/
|
|
if (!isGnuAttributeName())
|
|
return;
|
|
|
|
if (token.value == TOK.identifier)
|
|
{
|
|
if (token.ident == Id.aligned)
|
|
{
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis)
|
|
{
|
|
nextToken();
|
|
if (token.value == TOK.int32Literal)
|
|
{
|
|
const n = token.unsvalue;
|
|
if (n < 1 || n & (n - 1) || ushort.max < n)
|
|
error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
|
|
specifier.packalign.set(cast(uint)n);
|
|
specifier.packalign.setPack(true);
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
error("alignment value expected, not `%s`", token.toChars());
|
|
nextToken();
|
|
}
|
|
|
|
check(TOK.rightParenthesis);
|
|
}
|
|
/* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
|
|
* type on the target machine. It's the opposite of __attribute__((packed))
|
|
*/
|
|
}
|
|
else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
|
|
{
|
|
specifier.scw |= SCW.xinline;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id._deprecated)
|
|
{
|
|
specifier._deprecated = true;
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis) // optional deprecation message
|
|
{
|
|
nextToken();
|
|
specifier.depMsg = cparseExpression();
|
|
check(TOK.rightParenthesis);
|
|
}
|
|
}
|
|
else if (token.ident == Id.dllimport)
|
|
{
|
|
specifier.dllimport = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.dllexport)
|
|
{
|
|
specifier.dllexport = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.naked)
|
|
{
|
|
specifier.naked = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.noinline)
|
|
{
|
|
specifier.scw |= SCW.xnoinline;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.noreturn)
|
|
{
|
|
specifier.noreturn = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id._nothrow)
|
|
{
|
|
specifier._nothrow = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id._pure)
|
|
{
|
|
specifier._pure = true;
|
|
nextToken();
|
|
}
|
|
else if (token.ident == Id.vector_size)
|
|
{
|
|
nextToken();
|
|
check(TOK.leftParenthesis);
|
|
if (token.value == TOK.int32Literal)
|
|
{
|
|
const n = token.unsvalue;
|
|
if (n < 1 || n & (n - 1) || ushort.max < n)
|
|
error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
|
|
specifier.vector_size = cast(uint) n;
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
error("value for vector_size expected, not `%s`", token.toChars());
|
|
nextToken();
|
|
}
|
|
check(TOK.rightParenthesis);
|
|
}
|
|
else
|
|
{
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis)
|
|
cparseParens();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis)
|
|
cparseParens();
|
|
}
|
|
}
|
|
|
|
/*************************
|
|
* See if match for GNU attribute name, which may be any identifier,
|
|
* storage-class-specifier, type-specifier, or type-qualifier.
|
|
* Returns:
|
|
* true if a valid GNU attribute name
|
|
*/
|
|
private bool isGnuAttributeName()
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOK.identifier:
|
|
case TOK.static_:
|
|
case TOK.unsigned:
|
|
case TOK.int64:
|
|
case TOK.const_:
|
|
case TOK.extern_:
|
|
case TOK.register:
|
|
case TOK.typedef_:
|
|
case TOK.int16:
|
|
case TOK.inline:
|
|
case TOK._Noreturn:
|
|
case TOK.volatile:
|
|
case TOK.signed:
|
|
case TOK.auto_:
|
|
case TOK.restrict:
|
|
case TOK._Complex:
|
|
case TOK._Thread_local:
|
|
case TOK.int32:
|
|
case TOK.__int128:
|
|
case TOK.char_:
|
|
case TOK.float32:
|
|
case TOK.float64:
|
|
case TOK.void_:
|
|
case TOK._Bool:
|
|
case TOK._Atomic:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/***************************
|
|
* Like skipParens(), but consume the tokens.
|
|
*/
|
|
private void cparseParens()
|
|
{
|
|
check(TOK.leftParenthesis);
|
|
int parens = 1;
|
|
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOK.leftParenthesis:
|
|
++parens;
|
|
break;
|
|
|
|
case TOK.rightParenthesis:
|
|
--parens;
|
|
if (parens < 0)
|
|
{
|
|
error("extra right parenthesis");
|
|
return;
|
|
}
|
|
if (parens == 0)
|
|
{
|
|
nextToken();
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case TOK.endOfFile:
|
|
error("end of file found before right parenthesis");
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
nextToken();
|
|
}
|
|
}
|
|
|
|
//}
|
|
/******************************************************************************/
|
|
/***************************** Struct & Enum Parser ***************************/
|
|
//{
|
|
|
|
/*************************************
|
|
* C11 6.7.2.2
|
|
* enum-specifier:
|
|
* enum identifier (opt) { enumerator-list }
|
|
* enum identifier (opt) { enumerator-list , }
|
|
* enum identifier
|
|
*
|
|
* enumerator-list:
|
|
* enumerator
|
|
* enumerator-list , enumerator
|
|
*
|
|
* enumerator:
|
|
* enumeration-constant
|
|
* enumeration-constant = constant-expression
|
|
*
|
|
* enumeration-constant:
|
|
* identifier
|
|
*
|
|
* Params:
|
|
* symbols = symbols to add enum declaration to
|
|
* Returns:
|
|
* type of the enum
|
|
*/
|
|
private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
|
|
{
|
|
const loc = token.loc;
|
|
nextToken();
|
|
|
|
/* GNU Extensions
|
|
* enum-specifier:
|
|
* enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
|
|
* enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
|
|
* enum gnu-attributes (opt) identifier
|
|
*/
|
|
Specifier specifier;
|
|
specifier.packalign.setDefault();
|
|
if (token.value == TOK.__attribute__)
|
|
cparseGnuAttributes(specifier);
|
|
|
|
Identifier tag;
|
|
if (token.value == TOK.identifier)
|
|
{
|
|
tag = token.ident;
|
|
nextToken();
|
|
}
|
|
|
|
/* clang extension: add optional base type after the identifier
|
|
* https://en.cppreference.com/w/cpp/language/enum
|
|
* enum Identifier : Type
|
|
*/
|
|
//AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
|
|
AST.Type base = null; // C23 says base type is determined by enum member values
|
|
if (token.value == TOK.colon)
|
|
{
|
|
nextToken();
|
|
base = cparseTypeName();
|
|
}
|
|
|
|
AST.Dsymbols* members;
|
|
if (token.value == TOK.leftCurly)
|
|
{
|
|
nextToken();
|
|
members = new AST.Dsymbols();
|
|
|
|
if (token.value == TOK.rightCurly) // C11 6.7.2.2-1
|
|
{
|
|
if (tag)
|
|
error("no members for `enum %s`", tag.toChars());
|
|
else
|
|
error("no members for anonymous enum");
|
|
}
|
|
|
|
while (token.value == TOK.identifier)
|
|
{
|
|
auto ident = token.ident; // enumeration-constant
|
|
nextToken();
|
|
auto mloc = token.loc;
|
|
|
|
if (token.value == TOK.__attribute__)
|
|
{
|
|
/* gnu-attributes can appear here, but just scan and ignore them
|
|
* https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
|
|
*/
|
|
Specifier specifierx;
|
|
specifierx.packalign.setDefault();
|
|
cparseGnuAttributes(specifierx);
|
|
}
|
|
|
|
AST.Expression value;
|
|
if (token.value == TOK.assign)
|
|
{
|
|
nextToken();
|
|
value = cparseConstantExp();
|
|
// TODO C11 6.7.2.2-2 value must fit into an int
|
|
}
|
|
|
|
if (token.value == TOK.__attribute__)
|
|
{
|
|
/* gnu-attributes can appear here, but just scan and ignore them
|
|
* https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
|
|
*/
|
|
Specifier specifierx;
|
|
specifierx.packalign.setDefault();
|
|
cparseGnuAttributes(specifierx);
|
|
}
|
|
|
|
auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
|
|
members.push(em);
|
|
|
|
if (token.value == TOK.comma)
|
|
{
|
|
nextToken();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
check(TOK.rightCurly);
|
|
|
|
/* GNU Extensions
|
|
* Parse the postfix gnu-attributes (opt)
|
|
*/
|
|
if (token.value == TOK.__attribute__)
|
|
cparseGnuAttributes(specifier);
|
|
}
|
|
else if (!tag)
|
|
error("missing `identifier` after `enum`");
|
|
|
|
/* Need semantic information to determine if this is a declaration,
|
|
* redeclaration, or reference to existing declaration.
|
|
* Defer to the semantic() pass with a TypeTag.
|
|
*/
|
|
return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
|
|
}
|
|
|
|
/*************************************
|
|
* C11 6.7.2.1
|
|
* Parse struct and union specifiers.
|
|
* Parser is advanced to the tag identifier or brace.
|
|
* struct-or-union-specifier:
|
|
* struct-or-union identifier (opt) { struct-declaration-list }
|
|
* struct-or-union identifier
|
|
*
|
|
* struct-or-union:
|
|
* struct
|
|
* union
|
|
*
|
|
* struct-declaration-list:
|
|
* struct-declaration
|
|
* struct-declaration-list struct-declaration
|
|
*
|
|
* Params:
|
|
* loc = location of `struct` or `union`
|
|
* structOrUnion = TOK.struct_ or TOK.union_
|
|
* packalign = alignment to use for struct members
|
|
* symbols = symbols to add struct-or-union declaration to
|
|
* Returns:
|
|
* type of the struct
|
|
*/
|
|
private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
|
|
{
|
|
Identifier tag;
|
|
|
|
if (token.value == TOK.identifier)
|
|
{
|
|
tag = token.ident;
|
|
nextToken();
|
|
}
|
|
|
|
AST.Dsymbols* members;
|
|
if (token.value == TOK.leftCurly)
|
|
{
|
|
nextToken();
|
|
members = new AST.Dsymbols(); // so `members` will be non-null even with 0 members
|
|
while (token.value != TOK.rightCurly)
|
|
{
|
|
cparseStructDeclaration(members);
|
|
|
|
if (token.value == TOK.endOfFile)
|
|
break;
|
|
}
|
|
check(TOK.rightCurly);
|
|
|
|
if ((*members).length == 0) // C11 6.7.2.1-8
|
|
{
|
|
/* allow empty structs as an extension
|
|
* struct-declarator-list:
|
|
* struct-declarator (opt)
|
|
*/
|
|
}
|
|
}
|
|
else if (!tag)
|
|
error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
|
|
|
|
// many ways and places to declare alignment
|
|
if (packalign.isUnknown() && !this.packalign.isUnknown())
|
|
packalign.set(this.packalign.get());
|
|
|
|
/* Need semantic information to determine if this is a declaration,
|
|
* redeclaration, or reference to existing declaration.
|
|
* Defer to the semantic() pass with a TypeTag.
|
|
*/
|
|
return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
|
|
}
|
|
|
|
/*************************************
|
|
* C11 6.7.2.1
|
|
* Parse a struct declaration member.
|
|
* struct-declaration:
|
|
* specifier-qualifier-list struct-declarator-list (opt) ;
|
|
* static_assert-declaration
|
|
*
|
|
* struct-declarator-list:
|
|
* struct-declarator
|
|
* struct-declarator-list , struct-declarator
|
|
*
|
|
* struct-declarator:
|
|
* declarator
|
|
* declarator (opt) : constant-expression
|
|
* Params:
|
|
* members = where to put the fields (members)
|
|
*/
|
|
void cparseStructDeclaration(AST.Dsymbols* members)
|
|
{
|
|
//printf("cparseStructDeclaration()\n");
|
|
if (token.value == TOK._Static_assert)
|
|
{
|
|
auto s = cparseStaticAssert();
|
|
members.push(s);
|
|
return;
|
|
}
|
|
|
|
Specifier specifier;
|
|
specifier.packalign = this.packalign;
|
|
auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
|
|
if (!tspec)
|
|
{
|
|
error("no type-specifier for struct member");
|
|
tspec = AST.Type.tint32;
|
|
}
|
|
if (specifier.mod & MOD.xconst)
|
|
{
|
|
tspec = toConst(tspec);
|
|
specifier.mod = MOD.xnone; // 'used' it
|
|
}
|
|
|
|
/* If a declarator does not follow, it is unnamed
|
|
*/
|
|
if (token.value == TOK.semicolon && tspec)
|
|
{
|
|
nextToken();
|
|
auto tt = tspec.isTypeTag();
|
|
if (!tt)
|
|
{
|
|
if (auto ti = tspec.isTypeIdentifier())
|
|
{
|
|
error("type-specifier omitted before declaration of `%s`", ti.ident.toChars());
|
|
}
|
|
return; // legal but meaningless empty declaration
|
|
}
|
|
|
|
/* If anonymous struct declaration
|
|
* struct { ... members ... };
|
|
* C11 6.7.2.1-13
|
|
*/
|
|
if (!tt.id && tt.members)
|
|
{
|
|
/* members of anonymous struct are considered members of
|
|
* the containing struct
|
|
*/
|
|
auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
|
|
auto s = applySpecifier(ad, specifier);
|
|
members.push(s);
|
|
return;
|
|
}
|
|
if (!tt.id && !tt.members)
|
|
return; // already gave error in cparseStruct()
|
|
|
|
/* `struct tag;` and `struct tag { ... };`
|
|
* always result in a declaration in the current scope
|
|
*/
|
|
// TODO: merge in specifier
|
|
auto stag = (tt.tok == TOK.struct_)
|
|
? new AST.StructDeclaration(tt.loc, tt.id, false)
|
|
: new AST.UnionDeclaration(tt.loc, tt.id);
|
|
stag.members = tt.members;
|
|
if (!symbols)
|
|
symbols = new AST.Dsymbols();
|
|
auto s = applySpecifier(stag, specifier);
|
|
symbols.push(s);
|
|
return;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
Identifier id;
|
|
AST.Type dt;
|
|
if (token.value == TOK.colon)
|
|
{
|
|
if (auto ti = tspec.isTypeIdentifier())
|
|
{
|
|
error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars());
|
|
tspec = AST.Type.tint32;
|
|
}
|
|
|
|
// C11 6.7.2.1-12 unnamed bit-field
|
|
id = Identifier.generateAnonymousId("BitField");
|
|
dt = tspec;
|
|
}
|
|
else
|
|
{
|
|
dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
|
|
if (!dt)
|
|
{
|
|
panic();
|
|
nextToken();
|
|
break; // error recovery
|
|
}
|
|
}
|
|
|
|
AST.Expression width;
|
|
if (token.value == TOK.colon)
|
|
{
|
|
// C11 6.7.2.1-10 bit-field
|
|
nextToken();
|
|
width = cparseConstantExp();
|
|
}
|
|
|
|
/* GNU Extensions
|
|
* struct-declarator:
|
|
* declarator gnu-attributes (opt)
|
|
* declarator (opt) : constant-expression gnu-attributes (opt)
|
|
*/
|
|
if (token.value == TOK.__attribute__)
|
|
cparseGnuAttributes(specifier);
|
|
|
|
if (!tspec && !specifier.scw && !specifier.mod)
|
|
error("specifier-qualifier-list required");
|
|
else if (width)
|
|
{
|
|
if (specifier.alignExps)
|
|
error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
|
|
auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
|
|
members.push(s);
|
|
}
|
|
else if (id)
|
|
{
|
|
if (dt.ty == AST.Tvoid)
|
|
error("`void` has no value");
|
|
|
|
// declare the symbol
|
|
// Give member variables an implicit void initializer
|
|
auto initializer = new AST.VoidInitializer(token.loc);
|
|
AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
|
|
s = applySpecifier(s, specifier);
|
|
members.push(s);
|
|
}
|
|
|
|
switch (token.value)
|
|
{
|
|
case TOK.identifier:
|
|
error("missing comma");
|
|
goto default;
|
|
|
|
case TOK.semicolon:
|
|
nextToken();
|
|
return;
|
|
|
|
case TOK.comma:
|
|
nextToken();
|
|
break;
|
|
|
|
default:
|
|
error("`;` or `,` expected");
|
|
while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
|
|
nextToken();
|
|
nextToken();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//}
|
|
/******************************************************************************/
|
|
/********************************* Lookahead Parser ***************************/
|
|
//{
|
|
|
|
/************************************
|
|
* Determine if the scanner is sitting on the start of a declaration.
|
|
* Params:
|
|
* t = current token of the scanner
|
|
* needId = flag with additional requirements for a declaration
|
|
* endtok = ending token
|
|
* pt = will be set ending token (if not null)
|
|
* Returns:
|
|
* true at start of a declaration
|
|
*/
|
|
private bool isCDeclaration(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
//printf("isCDeclaration() %s\n", t.toChars());
|
|
if (!isDeclarationSpecifiers(t))
|
|
return false;
|
|
|
|
while (1)
|
|
{
|
|
if (t.value == TOK.semicolon)
|
|
{
|
|
t = peek(t);
|
|
pt = t;
|
|
return true;
|
|
}
|
|
if (!isCDeclarator(t, DTR.xdirect))
|
|
return false;
|
|
if (t.value == TOK.asm_)
|
|
{
|
|
t = peek(t);
|
|
if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
|
|
return false;
|
|
}
|
|
if (t.value == TOK.__attribute__)
|
|
{
|
|
t = peek(t);
|
|
if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
|
|
return false;
|
|
}
|
|
if (t.value == TOK.assign)
|
|
{
|
|
t = peek(t);
|
|
if (!isInitializer(t))
|
|
return false;
|
|
}
|
|
switch (t.value)
|
|
{
|
|
case TOK.comma:
|
|
t = peek(t);
|
|
break;
|
|
|
|
case TOK.semicolon:
|
|
t = peek(t);
|
|
pt = t;
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************
|
|
* See if match for initializer.
|
|
* Params:
|
|
* pt = starting token, updated to one past end of initializer if true
|
|
* Returns:
|
|
* true if initializer
|
|
*/
|
|
private bool isInitializer(ref Token* pt)
|
|
{
|
|
//printf("isInitializer()\n");
|
|
auto t = pt;
|
|
|
|
if (t.value == TOK.leftCurly)
|
|
{
|
|
if (!skipBraces(t))
|
|
return false;
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
// skip over assignment-expression, ending before comma or semiColon or EOF
|
|
if (!isAssignmentExpression(t))
|
|
return false;
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
/********************************
|
|
* See if match for:
|
|
* postfix-expression ( argument-expression-list(opt) )
|
|
* Params:
|
|
* pt = starting token, updated to one past end of initializer if true
|
|
* Returns:
|
|
* true if function call
|
|
*/
|
|
private bool isFunctionCall(ref Token* pt)
|
|
{
|
|
//printf("isFunctionCall()\n");
|
|
auto t = pt;
|
|
|
|
if (!isPrimaryExpression(t))
|
|
return false;
|
|
if (t.value != TOK.leftParenthesis)
|
|
return false;
|
|
t = peek(t);
|
|
while (1)
|
|
{
|
|
if (!isAssignmentExpression(t))
|
|
return false;
|
|
if (t.value == TOK.comma)
|
|
{
|
|
t = peek(t);
|
|
continue;
|
|
}
|
|
if (t.value == TOK.rightParenthesis)
|
|
{
|
|
t = peek(t);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
if (t.value != TOK.semicolon)
|
|
return false;
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
/********************************
|
|
* See if match for assignment-expression.
|
|
* Params:
|
|
* pt = starting token, updated to one past end of assignment-expression if true
|
|
* Returns:
|
|
* true if assignment-expression
|
|
*/
|
|
private bool isAssignmentExpression(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
//printf("isAssignmentExpression() %s\n", t.toChars());
|
|
|
|
/* This doesn't actually check for grammar matching an
|
|
* assignment-expression. It just matches ( ) [ ] looking for
|
|
* an ending token that would terminate one.
|
|
*/
|
|
bool any;
|
|
while (1)
|
|
{
|
|
switch (t.value)
|
|
{
|
|
case TOK.comma:
|
|
case TOK.semicolon:
|
|
case TOK.rightParenthesis:
|
|
case TOK.rightBracket:
|
|
case TOK.endOfFile:
|
|
if (!any)
|
|
return false;
|
|
break;
|
|
|
|
case TOK.leftParenthesis:
|
|
if (!skipParens(t, &t))
|
|
return false;
|
|
/*
|
|
https://issues.dlang.org/show_bug.cgi?id=22267
|
|
If the parser encounters the following
|
|
`identifier variableName = (expression);`
|
|
the initializer is not identified as such since the parentheses
|
|
cause the parser to keep walking indefinitely
|
|
(whereas `(1) + 1` would not be affected.).
|
|
*/
|
|
any = true;
|
|
continue;
|
|
|
|
case TOK.leftBracket:
|
|
if (!skipBrackets(t))
|
|
return false;
|
|
continue;
|
|
|
|
case TOK.leftCurly:
|
|
if (!skipBraces(t))
|
|
return false;
|
|
continue;
|
|
|
|
default:
|
|
any = true; // assume token was part of an a-e
|
|
t = peek(t);
|
|
continue;
|
|
}
|
|
pt = t;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/********************************
|
|
* See if match for constant-expression.
|
|
* Params:
|
|
* pt = starting token, updated to one past end of constant-expression if true
|
|
* Returns:
|
|
* true if constant-expression
|
|
*/
|
|
private bool isConstantExpression(ref Token* pt)
|
|
{
|
|
return isAssignmentExpression(pt);
|
|
}
|
|
|
|
/********************************
|
|
* See if match for declaration-specifiers.
|
|
* No errors are diagnosed.
|
|
* Params:
|
|
* pt = starting token, updated to one past end of declaration-specifiers if true
|
|
* Returns:
|
|
* true if declaration-specifiers
|
|
*/
|
|
private bool isDeclarationSpecifiers(ref Token* pt)
|
|
{
|
|
//printf("isDeclarationSpecifiers()\n");
|
|
|
|
auto t = pt;
|
|
|
|
bool seenType;
|
|
bool any;
|
|
while (1)
|
|
{
|
|
switch (t.value)
|
|
{
|
|
// type-specifiers
|
|
case TOK.void_:
|
|
case TOK.char_:
|
|
case TOK.int16:
|
|
case TOK.int32:
|
|
case TOK.int64:
|
|
case TOK.__int128:
|
|
case TOK.float32:
|
|
case TOK.float64:
|
|
case TOK.signed:
|
|
case TOK.unsigned:
|
|
case TOK._Bool:
|
|
//case TOK._Imaginary:
|
|
case TOK._Complex:
|
|
t = peek(t);
|
|
seenType = true;
|
|
any = true;
|
|
continue;
|
|
|
|
case TOK.identifier: // typedef-name
|
|
if (!seenType)
|
|
{
|
|
t = peek(t);
|
|
seenType = true;
|
|
any = true;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case TOK.struct_:
|
|
case TOK.union_:
|
|
case TOK.enum_:
|
|
t = peek(t);
|
|
if (t.value == TOK.__attribute__ ||
|
|
t.value == TOK.__declspec)
|
|
{
|
|
t = peek(t);
|
|
if (!skipParens(t, &t))
|
|
return false;
|
|
}
|
|
if (t.value == TOK.identifier)
|
|
{
|
|
t = peek(t);
|
|
if (t.value == TOK.leftCurly)
|
|
{
|
|
if (!skipBraces(t))
|
|
return false;
|
|
}
|
|
}
|
|
else if (t.value == TOK.leftCurly)
|
|
{
|
|
if (!skipBraces(t))
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
any = true;
|
|
continue;
|
|
|
|
// storage-class-specifiers
|
|
case TOK.typedef_:
|
|
case TOK.extern_:
|
|
case TOK.static_:
|
|
case TOK.__thread:
|
|
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:
|
|
t = peek(t);
|
|
any = true;
|
|
continue;
|
|
|
|
case TOK._Alignas: // alignment-specifier
|
|
case TOK.__declspec: // decl-specifier
|
|
case TOK.__attribute__: // attribute-specifier
|
|
t = peek(t);
|
|
if (!skipParens(t, &t))
|
|
return false;
|
|
any = true;
|
|
continue;
|
|
|
|
// either atomic-type-specifier or type_qualifier
|
|
case TOK._Atomic: // TODO _Atomic ( type-name )
|
|
t = peek(t);
|
|
if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
|
|
{
|
|
auto tsave = t;
|
|
t = peek(t);
|
|
if (!isTypeName(t) || t.value != TOK.rightParenthesis)
|
|
{ // it's a type-qualifier
|
|
t = tsave; // back up parser
|
|
any = true;
|
|
continue;
|
|
}
|
|
t = peek(t); // move past right parenthesis of atomic-type-specifier
|
|
}
|
|
any = true;
|
|
continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (any)
|
|
{
|
|
pt = t;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**************************************
|
|
* See if declaration-list is present.
|
|
* Returns:
|
|
* true if declaration-list is present, even an empty one
|
|
*/
|
|
bool isDeclarationList(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
while (1)
|
|
{
|
|
if (t.value == TOK.leftCurly)
|
|
{
|
|
pt = t;
|
|
return true;
|
|
}
|
|
if (!isCDeclaration(t))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*******************************************
|
|
* Skip braces.
|
|
* Params:
|
|
* pt = enters on left brace, set to token past right bracket on true
|
|
* Returns:
|
|
* true if successful
|
|
*/
|
|
private bool skipBraces(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
if (t.value != TOK.leftCurly)
|
|
return false;
|
|
|
|
int braces = 0;
|
|
|
|
while (1)
|
|
{
|
|
switch (t.value)
|
|
{
|
|
case TOK.leftCurly:
|
|
++braces;
|
|
t = peek(t);
|
|
continue;
|
|
|
|
case TOK.rightCurly:
|
|
--braces;
|
|
if (braces == 0)
|
|
{
|
|
pt = peek(t);
|
|
return true;
|
|
}
|
|
if (braces < 0)
|
|
return false;
|
|
|
|
t = peek(t);
|
|
continue;
|
|
|
|
case TOK.endOfFile:
|
|
return false;
|
|
|
|
default:
|
|
t = peek(t);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************
|
|
* Skip brackets.
|
|
* Params:
|
|
* pt = enters on left bracket, set to token past right bracket on true
|
|
* Returns:
|
|
* true if successful
|
|
*/
|
|
private bool skipBrackets(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
if (t.value != TOK.leftBracket)
|
|
return false;
|
|
|
|
int brackets = 0;
|
|
|
|
while (1)
|
|
{
|
|
switch (t.value)
|
|
{
|
|
case TOK.leftBracket:
|
|
++brackets;
|
|
t = peek(t);
|
|
continue;
|
|
|
|
case TOK.rightBracket:
|
|
--brackets;
|
|
if (brackets == 0)
|
|
{
|
|
pt = peek(t);
|
|
return true;
|
|
}
|
|
if (brackets < 0)
|
|
return false;
|
|
|
|
t = peek(t);
|
|
continue;
|
|
|
|
case TOK.endOfFile:
|
|
return false;
|
|
|
|
default:
|
|
t = peek(t);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************
|
|
* Check to see if tokens starting with *pt form a declarator.
|
|
* Params:
|
|
* pt = pointer to starting token, updated to point past declarator if true is returned
|
|
* declarator = declarator kind
|
|
* Returns:
|
|
* true if it does
|
|
*/
|
|
private bool isCDeclarator(ref Token* pt, DTR declarator)
|
|
{
|
|
auto t = pt;
|
|
while (1)
|
|
{
|
|
if (t.value == TOK.mul) // pointer
|
|
{
|
|
t = peek(t);
|
|
if (!isTypeQualifierList(t))
|
|
return false;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (t.value == TOK.identifier)
|
|
{
|
|
if (declarator == DTR.xabstract)
|
|
return false;
|
|
t = peek(t);
|
|
}
|
|
else if (t.value == TOK.leftParenthesis)
|
|
{
|
|
t = peek(t);
|
|
if (!isCDeclarator(t, declarator))
|
|
return false;
|
|
if (t.value != TOK.rightParenthesis)
|
|
return false;
|
|
t = peek(t);
|
|
}
|
|
else if (declarator == DTR.xdirect)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (t.value == TOK.leftBracket)
|
|
{
|
|
if (!skipBrackets(t))
|
|
return false;
|
|
}
|
|
else if (t.value == TOK.leftParenthesis)
|
|
{
|
|
if (!skipParens(t, &t))
|
|
return false;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
/***************************
|
|
* Is this the start of a type-qualifier-list?
|
|
* (Can be empty.)
|
|
* Params:
|
|
* pt = first token; updated with past end of type-qualifier-list if true
|
|
* Returns:
|
|
* true if start of type-qualifier-list
|
|
*/
|
|
private bool isTypeQualifierList(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
while (1)
|
|
{
|
|
switch (t.value)
|
|
{
|
|
case TOK.const_:
|
|
case TOK.restrict:
|
|
case TOK.volatile:
|
|
case TOK._Atomic:
|
|
case TOK.__stdcall:
|
|
t = peek(t);
|
|
continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
/***************************
|
|
* Is this the start of a type-name?
|
|
* Params:
|
|
* pt = first token; updated with past end of type-name if true
|
|
* Returns:
|
|
* true if start of type-name
|
|
*/
|
|
private bool isTypeName(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
//printf("isTypeName() %s\n", t.toChars());
|
|
if (!isSpecifierQualifierList(t))
|
|
return false;
|
|
if (!isCDeclarator(t, DTR.xabstract))
|
|
return false;
|
|
if (t.value != TOK.rightParenthesis)
|
|
return false;
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
/***************************
|
|
* Is this the start of a specifier-qualifier-list?
|
|
* Params:
|
|
* pt = first token; updated with past end of specifier-qualifier-list if true
|
|
* Returns:
|
|
* true if start of specifier-qualifier-list
|
|
*/
|
|
private bool isSpecifierQualifierList(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
bool result;
|
|
while (1)
|
|
{
|
|
switch (t.value)
|
|
{
|
|
// Type Qualifiers
|
|
case TOK.const_:
|
|
case TOK.restrict:
|
|
case TOK.volatile:
|
|
case TOK.__stdcall:
|
|
|
|
// Type Specifiers
|
|
case TOK.char_:
|
|
case TOK.signed:
|
|
case TOK.unsigned:
|
|
case TOK.int16:
|
|
case TOK.int32:
|
|
case TOK.int64:
|
|
case TOK.__int128:
|
|
case TOK.float32:
|
|
case TOK.float64:
|
|
case TOK.void_:
|
|
case TOK._Bool:
|
|
//case TOK._Imaginary: // ? missing in Spec
|
|
case TOK._Complex:
|
|
t = peek(t);
|
|
break;
|
|
|
|
case TOK.identifier:
|
|
// Use typedef table to disambiguate
|
|
if (isTypedef(t.ident))
|
|
{
|
|
t = peek(t);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// struct-or-union-specifier
|
|
// enum-specifier
|
|
case TOK.struct_:
|
|
case TOK.union_:
|
|
case TOK.enum_:
|
|
t = peek(t);
|
|
if (t.value == TOK.identifier)
|
|
{
|
|
t = peek(t);
|
|
if (t.value == TOK.leftCurly)
|
|
{
|
|
if (!skipBraces(t))
|
|
return false;
|
|
}
|
|
}
|
|
else if (t.value == TOK.leftCurly)
|
|
{
|
|
if (!skipBraces(t))
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
break;
|
|
|
|
// atomic-type-specifier
|
|
case TOK._Atomic:
|
|
case TOK.typeof_:
|
|
case TOK.__attribute__:
|
|
t = peek(t);
|
|
if (t.value != TOK.leftParenthesis ||
|
|
!skipParens(t, &t))
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
if (result)
|
|
pt = t;
|
|
return result;
|
|
}
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
/************************************
|
|
* Looking at the leading left parenthesis, and determine if it is
|
|
* either of the following:
|
|
* ( type-name ) cast-expression
|
|
* ( type-name ) { initializer-list }
|
|
* as opposed to:
|
|
* ( expression )
|
|
* Params:
|
|
* pt = starting token, updated to one past end of constant-expression if true
|
|
* afterParenType = true if already seen `( type-name )`
|
|
* Returns:
|
|
* true if matches ( type-name ) ...
|
|
*/
|
|
private bool isCastExpression(ref Token* pt, bool afterParenType = false)
|
|
{
|
|
enum log = false;
|
|
if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
|
|
auto t = pt;
|
|
switch (t.value)
|
|
{
|
|
case TOK.leftParenthesis:
|
|
auto tk = peek(t); // move past left parenthesis
|
|
if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
|
|
{
|
|
if (afterParenType)
|
|
goto default; // could be ( type-name ) ( unary-expression )
|
|
return false;
|
|
}
|
|
tk = peek(tk); // move past right parenthesis
|
|
|
|
if (tk.value == TOK.leftCurly)
|
|
{
|
|
// ( type-name ) { initializer-list }
|
|
if (!isInitializer(tk))
|
|
{
|
|
return false;
|
|
}
|
|
t = tk;
|
|
break;
|
|
}
|
|
|
|
if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
|
|
{
|
|
return false; // (type-name)() is not a cast (it might be a function call)
|
|
}
|
|
|
|
if (!isCastExpression(tk, true))
|
|
{
|
|
if (afterParenType) // could be ( type-name ) ( unary-expression )
|
|
goto default; // where unary-expression also matched type-name
|
|
return true;
|
|
}
|
|
// ( type-name ) cast-expression
|
|
t = tk;
|
|
break;
|
|
|
|
default:
|
|
if (!afterParenType || !isUnaryExpression(t, afterParenType))
|
|
{
|
|
return false;
|
|
}
|
|
// if we've already seen ( type-name ), then this is a cast
|
|
break;
|
|
}
|
|
pt = t;
|
|
if (log) printf("isCastExpression true\n");
|
|
return true;
|
|
}
|
|
|
|
/********************************
|
|
* See if match for unary-expression.
|
|
* Params:
|
|
* pt = starting token, updated to one past end of constant-expression if true
|
|
* afterParenType = true if already seen ( type-name ) of a cast-expression
|
|
* Returns:
|
|
* true if unary-expression
|
|
*/
|
|
private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
|
|
{
|
|
auto t = pt;
|
|
switch (t.value)
|
|
{
|
|
case TOK.plusPlus:
|
|
case TOK.minusMinus:
|
|
t = peek(t);
|
|
if (!isUnaryExpression(t, afterParenType))
|
|
return false;
|
|
break;
|
|
|
|
case TOK.and:
|
|
case TOK.mul:
|
|
case TOK.min:
|
|
case TOK.add:
|
|
case TOK.not:
|
|
case TOK.tilde:
|
|
t = peek(t);
|
|
if (!isCastExpression(t, afterParenType))
|
|
return false;
|
|
break;
|
|
|
|
case TOK.sizeof_:
|
|
t = peek(t);
|
|
if (t.value == TOK.leftParenthesis)
|
|
{
|
|
auto tk = peek(t);
|
|
if (isTypeName(tk))
|
|
{
|
|
if (tk.value != TOK.rightParenthesis)
|
|
return false;
|
|
t = peek(tk);
|
|
break;
|
|
}
|
|
}
|
|
if (!isUnaryExpression(t, afterParenType))
|
|
return false;
|
|
break;
|
|
|
|
case TOK._Alignof:
|
|
t = peek(t);
|
|
if (t.value != TOK.leftParenthesis)
|
|
return false;
|
|
t = peek(t);
|
|
if (!isTypeName(t) || t.value != TOK.rightParenthesis)
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
// Compound literals are handled by cast and sizeof expressions,
|
|
// so be content with just seeing a primary expression.
|
|
if (!isPrimaryExpression(t))
|
|
return false;
|
|
break;
|
|
}
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
/********************************
|
|
* See if match for primary-expression.
|
|
* Params:
|
|
* pt = starting token, updated to one past end of constant-expression if true
|
|
* Returns:
|
|
* true if primary-expression
|
|
*/
|
|
private bool isPrimaryExpression(ref Token* pt)
|
|
{
|
|
auto t = pt;
|
|
switch (t.value)
|
|
{
|
|
case TOK.identifier:
|
|
case TOK.charLiteral:
|
|
case TOK.int32Literal:
|
|
case TOK.uns32Literal:
|
|
case TOK.int64Literal:
|
|
case TOK.uns64Literal:
|
|
case TOK.float32Literal:
|
|
case TOK.float64Literal:
|
|
case TOK.float80Literal:
|
|
case TOK.imaginary32Literal:
|
|
case TOK.imaginary64Literal:
|
|
case TOK.imaginary80Literal:
|
|
case TOK.string_:
|
|
t = peek(t);
|
|
break;
|
|
|
|
case TOK.leftParenthesis:
|
|
// ( expression )
|
|
if (!skipParens(t, &t))
|
|
return false;
|
|
break;
|
|
|
|
case TOK._Generic:
|
|
t = peek(t);
|
|
if (!skipParens(t, &t))
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
pt = t;
|
|
return true;
|
|
}
|
|
|
|
//}
|
|
/******************************************************************************/
|
|
/********************************* More ***************************************/
|
|
//{
|
|
|
|
/**************
|
|
* Declaration context
|
|
*/
|
|
enum LVL
|
|
{
|
|
global = 1, /// global
|
|
parameter = 2, /// function parameter (declarations for function identifier-list)
|
|
prototype = 4, /// function prototype
|
|
local = 8, /// local
|
|
member = 0x10, /// struct member
|
|
}
|
|
|
|
/// Types of declarator to parse
|
|
enum DTR
|
|
{
|
|
xdirect = 1, /// C11 6.7.6 direct-declarator
|
|
xabstract = 2, /// C11 6.7.7 abstract-declarator
|
|
xparameter = 3, /// parameter declarator may be either direct or abstract
|
|
}
|
|
|
|
/// C11 6.7.1 Storage-class specifiers
|
|
enum SCW : uint
|
|
{
|
|
xnone = 0,
|
|
xtypedef = 1,
|
|
xextern = 2,
|
|
xstatic = 4,
|
|
x_Thread_local = 8,
|
|
xauto = 0x10,
|
|
xregister = 0x20,
|
|
// C11 6.7.4 Function specifiers
|
|
xinline = 0x40,
|
|
x_Noreturn = 0x80,
|
|
|
|
xnoinline = 0x100,
|
|
}
|
|
|
|
/// C11 6.7.3 Type qualifiers
|
|
enum MOD : uint
|
|
{
|
|
xnone = 0,
|
|
xconst = 1,
|
|
xvolatile = 2,
|
|
xrestrict = 4,
|
|
x_Atomic = 8,
|
|
x__stdcall = 0x10, // Windows linkage extension
|
|
}
|
|
|
|
/**********************************
|
|
* Aggregate for all the various specifiers
|
|
*/
|
|
struct Specifier
|
|
{
|
|
bool noreturn; /// noreturn attribute
|
|
bool naked; /// naked attribute
|
|
bool _nothrow; /// nothrow attribute
|
|
bool _pure; /// pure attribute
|
|
bool dllimport; /// dllimport attribute
|
|
bool dllexport; /// dllexport attribute
|
|
bool _deprecated; /// deprecated attribute
|
|
AST.Expression depMsg; /// deprecated message
|
|
uint vector_size; /// positive power of 2 multipe of base type size
|
|
|
|
SCW scw; /// storage-class specifiers
|
|
MOD mod; /// type qualifiers
|
|
AST.Expressions* alignExps; /// alignment
|
|
structalign_t packalign; /// #pragma pack alignment value
|
|
}
|
|
|
|
/***********************
|
|
* Convert from C specifiers to D storage class
|
|
* Params:
|
|
* level = declaration context
|
|
* specifier = specifiers, context, etc.
|
|
* Returns:
|
|
* corresponding D storage class
|
|
*/
|
|
StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
|
|
{
|
|
StorageClass stc;
|
|
if (specifier.scw & SCW.x_Thread_local)
|
|
{
|
|
if (level == LVL.global)
|
|
{
|
|
if (specifier.scw & SCW.xextern)
|
|
stc = AST.STC.extern_;
|
|
else if (specifier.scw & SCW.xstatic)
|
|
stc = AST.STC.static_;
|
|
}
|
|
else if (level == LVL.local)
|
|
{
|
|
if (specifier.scw & SCW.xextern)
|
|
stc = AST.STC.extern_;
|
|
else if (specifier.scw & SCW.xstatic)
|
|
stc = AST.STC.static_;
|
|
}
|
|
else if (level == LVL.member)
|
|
{
|
|
if (specifier.scw & SCW.xextern)
|
|
stc = AST.STC.extern_;
|
|
else if (specifier.scw & SCW.xstatic)
|
|
stc = AST.STC.static_;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (level == LVL.global)
|
|
{
|
|
if (specifier.scw & SCW.xextern)
|
|
stc = AST.STC.extern_ | AST.STC.gshared;
|
|
else if (specifier.scw & SCW.xstatic)
|
|
stc = AST.STC.gshared | AST.STC.static_;
|
|
else
|
|
stc = AST.STC.gshared;
|
|
}
|
|
else if (level == LVL.local)
|
|
{
|
|
if (specifier.scw & SCW.xextern)
|
|
stc = AST.STC.extern_ | AST.STC.gshared;
|
|
else if (specifier.scw & SCW.xstatic)
|
|
stc = AST.STC.gshared;
|
|
else if (specifier.scw & SCW.xregister)
|
|
stc = AST.STC.register;
|
|
}
|
|
else if (level == LVL.parameter)
|
|
{
|
|
if (specifier.scw & SCW.xregister)
|
|
stc = AST.STC.register | AST.STC.parameter;
|
|
else
|
|
stc = AST.STC.parameter;
|
|
}
|
|
else if (level == LVL.member)
|
|
{
|
|
if (specifier.scw & SCW.xextern)
|
|
stc = AST.STC.extern_ | AST.STC.gshared;
|
|
else if (specifier.scw & SCW.xstatic)
|
|
stc = AST.STC.gshared;
|
|
}
|
|
}
|
|
if (specifier._deprecated && !specifier.depMsg)
|
|
stc |= AST.STC.deprecated_;
|
|
return stc;
|
|
}
|
|
|
|
/***********************
|
|
* Add attributes from Specifier to function
|
|
* Params:
|
|
* fd = function to apply them to
|
|
* specifier = specifiers
|
|
*/
|
|
void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
|
|
{
|
|
fd.isNaked = specifier.naked;
|
|
fd.dllImport = specifier.dllimport;
|
|
fd.dllExport = specifier.dllexport;
|
|
|
|
if (specifier.scw & SCW.xnoinline)
|
|
fd.inlining = PINLINE.never;
|
|
else if (specifier.scw & SCW.xinline)
|
|
fd.inlining = PINLINE.always;
|
|
}
|
|
|
|
/***********************
|
|
* Add attributes from Specifier to variable
|
|
* Params:
|
|
* vd = function to apply them to
|
|
* specifier = specifiers
|
|
*/
|
|
void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
|
|
{
|
|
vd.dllImport = specifier.dllimport;
|
|
vd.dllExport = specifier.dllexport;
|
|
}
|
|
|
|
/***********************
|
|
* Return suitable signed integer type for the given size
|
|
* Params:
|
|
* size = size of type
|
|
* Returns:
|
|
* corresponding signed D integer type
|
|
*/
|
|
private AST.Type integerTypeForSize(ubyte size)
|
|
{
|
|
if (size <= 1)
|
|
return AST.Type.tint8;
|
|
if (size <= 2)
|
|
return AST.Type.tint16;
|
|
if (size <= 4)
|
|
return AST.Type.tint32;
|
|
if (size <= 8)
|
|
return AST.Type.tint64;
|
|
if (size == 16)
|
|
{
|
|
error("__int128 not supported");
|
|
return AST.Type.terror;
|
|
}
|
|
error("unsupported integer type");
|
|
return AST.Type.terror;
|
|
}
|
|
|
|
/***********************
|
|
* Return suitable unsigned integer type for the given size
|
|
* Params:
|
|
* size = size of type
|
|
* Returns:
|
|
* corresponding unsigned D integer type
|
|
*/
|
|
private AST.Type unsignedTypeForSize(ubyte size)
|
|
{
|
|
if (size <= 1)
|
|
return AST.Type.tuns8;
|
|
if (size <= 2)
|
|
return AST.Type.tuns16;
|
|
if (size <= 4)
|
|
return AST.Type.tuns32;
|
|
if (size <= 8)
|
|
return AST.Type.tuns64;
|
|
if (size == 16)
|
|
{
|
|
error("unsigned __int128 not supported");
|
|
return AST.Type.terror;
|
|
}
|
|
error("unsupported integer type");
|
|
return AST.Type.terror;
|
|
}
|
|
|
|
/***********************
|
|
* Return suitable D float type for C `long double`
|
|
* Params:
|
|
* flags = kind of float to return (real, imaginary, complex).
|
|
* Returns:
|
|
* corresponding D type
|
|
*/
|
|
private AST.Type realType(RTFlags flags)
|
|
{
|
|
if (long_doublesize == AST.Type.tfloat80.size())
|
|
{
|
|
// On GDC and LDC, D `real` types map to C `long double`, so never
|
|
// return a double type when real.sizeof == double.sizeof.
|
|
final switch (flags)
|
|
{
|
|
case RTFlags.realfloat: return AST.Type.tfloat80;
|
|
case RTFlags.imaginary: return AST.Type.timaginary80;
|
|
case RTFlags.complex: return AST.Type.tcomplex80;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
final switch (flags)
|
|
{
|
|
case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
|
|
case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
|
|
case RTFlags.complex: return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************
|
|
* Flags for realType
|
|
*/
|
|
private enum RTFlags
|
|
{
|
|
realfloat,
|
|
imaginary,
|
|
complex,
|
|
}
|
|
|
|
/********************
|
|
* C11 6.4.2.2 Create declaration to predefine __func__
|
|
* `static const char __func__[] = " function-name ";`
|
|
* Params:
|
|
* loc = location for this declaration
|
|
* id = identifier of function
|
|
* Returns:
|
|
* statement representing the declaration of __func__
|
|
*/
|
|
private AST.Statement createFuncName(Loc loc, Identifier id)
|
|
{
|
|
const fn = id.toString(); // function-name
|
|
auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
|
|
auto ifn = new AST.ExpInitializer(loc, efn);
|
|
auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
|
|
auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
|
|
efn.type = tfn.immutableOf();
|
|
efn.committed = true;
|
|
auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
|
|
auto e = new AST.DeclarationExp(loc, sfn);
|
|
return new AST.ExpStatement(loc, e);
|
|
}
|
|
|
|
/************************
|
|
* After encountering an error, scan forward until a right brace or ; is found
|
|
* or the end of the file.
|
|
*/
|
|
void panic()
|
|
{
|
|
while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
|
|
nextToken();
|
|
}
|
|
|
|
/**************************
|
|
* Apply `const` to a type.
|
|
* Params:
|
|
* t = type to add const to
|
|
* Returns:
|
|
* resulting type
|
|
*/
|
|
private AST.Type toConst(AST.Type t)
|
|
{
|
|
// `const` is always applied to the return type, not the
|
|
// type function itself.
|
|
if (auto tf = t.isTypeFunction())
|
|
tf.next = tf.next.addSTC(STC.const_);
|
|
else if (auto tt = t.isTypeTag())
|
|
tt.mod |= MODFlags.const_;
|
|
else
|
|
{
|
|
/* Ignore const if the result would be const pointer to mutable
|
|
*/
|
|
auto tn = t.nextOf();
|
|
if (!tn || tn.isConst())
|
|
t = t.addSTC(STC.const_);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/***************************
|
|
* Apply specifier to a Dsymbol.
|
|
* Params:
|
|
* s = Dsymbol
|
|
* specifier = specifiers to apply
|
|
* Returns:
|
|
* Dsymbol with specifiers applied
|
|
*/
|
|
private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
|
|
{
|
|
//printf("applySpecifier() %s\n", s.toChars());
|
|
if (specifier._deprecated)
|
|
{
|
|
if (specifier.depMsg)
|
|
{
|
|
// Wrap declaration in a DeprecatedDeclaration
|
|
auto decls = new AST.Dsymbols(1);
|
|
(*decls)[0] = s;
|
|
s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
|
|
}
|
|
}
|
|
|
|
if (specifier.alignExps)
|
|
{
|
|
//printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
|
|
// Wrap declaration in an AlignDeclaration
|
|
auto decls = new AST.Dsymbols(1);
|
|
(*decls)[0] = s;
|
|
s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
|
|
}
|
|
else if (!specifier.packalign.isDefault())
|
|
{
|
|
//printf(" applying packalign %d\n", cast(int)specifier.packalign);
|
|
// Wrap #pragma pack in an AlignDeclaration
|
|
auto decls = new AST.Dsymbols(1);
|
|
(*decls)[0] = s;
|
|
s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
//}
|
|
|
|
/******************************************************************************/
|
|
/************************** typedefTab symbol table ***************************/
|
|
//{
|
|
|
|
/********************************
|
|
* Determines if type t is a function type.
|
|
* Params:
|
|
* t = type to test
|
|
* Returns:
|
|
* true if it represents a function
|
|
*/
|
|
bool isFunctionTypedef(AST.Type t)
|
|
{
|
|
//printf("isFunctionTypedef() %s\n", t.toChars());
|
|
if (t.isTypeFunction())
|
|
return true;
|
|
if (auto tid = t.isTypeIdentifier())
|
|
{
|
|
auto pt = lookupTypedef(tid.ident);
|
|
if (pt && *pt)
|
|
{
|
|
return (*pt).isTypeFunction() !is null;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/********************************
|
|
* Determine if `id` is a symbol for a Typedef.
|
|
* Params:
|
|
* id = possible typedef
|
|
* Returns:
|
|
* true if id is a Type
|
|
*/
|
|
bool isTypedef(Identifier id)
|
|
{
|
|
auto pt = lookupTypedef(id);
|
|
return (pt && *pt);
|
|
}
|
|
|
|
/*******************************
|
|
* Add `id` to typedefTab[], but only if it will mask an existing typedef.
|
|
* Params: id = identifier for non-typedef symbol
|
|
*/
|
|
void insertIdToTypedefTab(Identifier id)
|
|
{
|
|
//printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
|
|
if (isTypedef(id)) // if existing typedef
|
|
{
|
|
/* Add id as null, so we can later distinguish it from a non-null typedef
|
|
*/
|
|
auto tab = cast(void*[void*])(typedefTab[$ - 1]);
|
|
tab[cast(void*)id] = cast(void*)null;
|
|
}
|
|
}
|
|
|
|
/*******************************
|
|
* Add `id` to typedefTab[]
|
|
* Params:
|
|
* id = identifier for typedef symbol
|
|
* t = type of the typedef symbol
|
|
*/
|
|
void insertTypedefToTypedefTab(Identifier id, AST.Type t)
|
|
{
|
|
//printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
|
|
if (auto tid = t.isTypeIdentifier())
|
|
{
|
|
// Try to resolve the TypeIdentifier to its type
|
|
auto pt = lookupTypedef(tid.ident);
|
|
if (pt && *pt)
|
|
t = *pt;
|
|
}
|
|
auto tab = cast(void*[void*])(typedefTab[$ - 1]);
|
|
tab[cast(void*)id] = cast(void*)t;
|
|
typedefTab[$ - 1] = cast(void*)tab;
|
|
}
|
|
|
|
/*********************************
|
|
* Lookup id in typedefTab[].
|
|
* Returns:
|
|
* if not found, then null.
|
|
* if found, then Type*. Deferencing it will yield null if it is not
|
|
* a typedef, and a type if it is a typedef.
|
|
*/
|
|
AST.Type* lookupTypedef(Identifier id)
|
|
{
|
|
foreach_reverse (tab; typedefTab[])
|
|
{
|
|
if (auto pt = cast(void*)id in cast(void*[void*])tab)
|
|
{
|
|
return cast(AST.Type*)pt;
|
|
}
|
|
}
|
|
return null; // not found
|
|
}
|
|
|
|
//}
|
|
|
|
/******************************************************************************/
|
|
/********************************* Directive Parser ***************************/
|
|
//{
|
|
|
|
override bool parseSpecialTokenSequence()
|
|
{
|
|
Token n;
|
|
scan(&n);
|
|
if (n.value == TOK.int32Literal)
|
|
{
|
|
poundLine(n, true);
|
|
return true;
|
|
}
|
|
if (n.value == TOK.identifier)
|
|
{
|
|
if (n.ident == Id.line)
|
|
{
|
|
poundLine(n, false);
|
|
return true;
|
|
}
|
|
else if (defines && (n.ident == Id.define || n.ident == Id.undef))
|
|
{
|
|
/* Append this line to `defines`.
|
|
* Not canonicalizing it - assume it already is
|
|
*/
|
|
defines.writeByte('#');
|
|
defines.writestring(n.ident.toString());
|
|
skipToNextLine(defines);
|
|
defines.writeByte('\n');
|
|
return true;
|
|
}
|
|
else if (n.ident == Id.__pragma)
|
|
{
|
|
pragmaDirective(scanloc);
|
|
return true;
|
|
}
|
|
else if (n.ident == Id.ident) // #ident "string"
|
|
{
|
|
scan(&n);
|
|
if (n.value == TOK.string_ && n.ptr[0] == '"' && n.postfix == 0)
|
|
{
|
|
/* gcc inserts string into the .comment section in the object file.
|
|
* Just ignore it for now, but can support it later by writing
|
|
* the string to obj_exestr()
|
|
*/
|
|
//auto comment = n.ustring;
|
|
|
|
scan(&n);
|
|
if (n.value == TOK.endOfFile || n.value == TOK.endOfLine)
|
|
return true;
|
|
}
|
|
error("\"string\" expected after `#ident`");
|
|
return false;
|
|
}
|
|
}
|
|
if (n.ident != Id.undef)
|
|
error("C preprocessor directive `#%s` is not supported", n.toChars());
|
|
return false;
|
|
}
|
|
|
|
/*********************************************
|
|
* VC __pragma
|
|
* https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
|
|
* Scanner is on the `__pragma`
|
|
* Params:
|
|
* startloc = location to use for error messages
|
|
*/
|
|
private void uupragmaDirective(const ref Loc startloc)
|
|
{
|
|
const loc = startloc;
|
|
nextToken(); // move past __pragma
|
|
if (token.value != TOK.leftParenthesis)
|
|
{
|
|
error(loc, "left parenthesis expected to follow `__pragma` instead of `%s`", token.toChars());
|
|
nextToken();
|
|
return;
|
|
}
|
|
nextToken();
|
|
|
|
if (token.value == TOK.identifier)
|
|
{
|
|
if (token.ident == Id.pack)
|
|
pragmaPack(startloc, false);
|
|
else
|
|
{
|
|
nextToken();
|
|
if (token.value == TOK.leftParenthesis)
|
|
cparseParens();
|
|
}
|
|
|
|
}
|
|
else if (token.value == TOK.endOfFile)
|
|
{
|
|
}
|
|
else if (token.value == TOK.rightParenthesis)
|
|
{
|
|
}
|
|
else
|
|
error(loc, "unrecognized `__pragma(%s)`", token.toChars());
|
|
|
|
if (token.value != TOK.rightParenthesis)
|
|
{
|
|
error(loc, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token.toChars());
|
|
return;
|
|
}
|
|
nextToken();
|
|
}
|
|
|
|
/*********************************************
|
|
* C11 6.10.6 Pragma directive
|
|
* # pragma pp-tokens(opt) new-line
|
|
* The C preprocessor sometimes leaves pragma directives in
|
|
* the preprocessed output. Ignore them.
|
|
* Upon return, p is at start of next line.
|
|
*/
|
|
private void pragmaDirective(const ref Loc loc)
|
|
{
|
|
Token n;
|
|
scan(&n);
|
|
if (n.value == TOK.identifier && n.ident == Id.pack)
|
|
return pragmaPack(loc, true);
|
|
if (n.value != TOK.endOfLine)
|
|
skipToNextLine();
|
|
}
|
|
|
|
/*********
|
|
* # pragma pack
|
|
* https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
|
|
* https://docs.microsoft.com/en-us/cpp/preprocessor/pack
|
|
* Scanner is on the `pack`
|
|
* Params:
|
|
* startloc = location to use for error messages
|
|
* useScan = use scan() to retrieve next token, instead of nextToken()
|
|
*/
|
|
private void pragmaPack(const ref Loc startloc, bool useScan)
|
|
{
|
|
const loc = startloc;
|
|
|
|
/* Pull tokens from scan() or nextToken()
|
|
*/
|
|
void scan(Token* t)
|
|
{
|
|
if (useScan)
|
|
{
|
|
Lexer.scan(t);
|
|
}
|
|
else
|
|
{
|
|
nextToken();
|
|
*t = token;
|
|
}
|
|
}
|
|
|
|
Token n;
|
|
scan(&n);
|
|
if (n.value != TOK.leftParenthesis)
|
|
{
|
|
error(loc, "left parenthesis expected to follow `#pragma pack`");
|
|
if (n.value != TOK.endOfLine)
|
|
skipToNextLine();
|
|
return;
|
|
}
|
|
|
|
void closingParen()
|
|
{
|
|
if (n.value != TOK.rightParenthesis)
|
|
{
|
|
error(loc, "right parenthesis expected to close `#pragma pack(`");
|
|
}
|
|
if (n.value != TOK.endOfLine)
|
|
skipToNextLine();
|
|
}
|
|
|
|
void setPackAlign(ref const Token t)
|
|
{
|
|
const n = t.unsvalue;
|
|
if (n < 1 || n & (n - 1) || ushort.max < n)
|
|
error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
|
|
packalign.set(cast(uint)n);
|
|
packalign.setPack(true);
|
|
}
|
|
|
|
scan(&n);
|
|
|
|
if (!records)
|
|
{
|
|
records = new Array!Identifier;
|
|
packs = new Array!structalign_t;
|
|
}
|
|
|
|
/* # pragma pack ( show )
|
|
*/
|
|
if (n.value == TOK.identifier && n.ident == Id.show)
|
|
{
|
|
if (packalign.isDefault())
|
|
eSink.warning(startloc, "current pack attribute is default");
|
|
else
|
|
eSink.warning(startloc, "current pack attribute is %d", packalign.get());
|
|
scan(&n);
|
|
return closingParen();
|
|
}
|
|
/* # pragma pack ( push )
|
|
* # pragma pack ( push , identifier )
|
|
* # pragma pack ( push , integer )
|
|
* # pragma pack ( push , identifier , integer )
|
|
*/
|
|
if (n.value == TOK.identifier && n.ident == Id.push)
|
|
{
|
|
scan(&n);
|
|
Identifier record = null;
|
|
if (n.value == TOK.comma)
|
|
{
|
|
scan(&n);
|
|
if (n.value == TOK.identifier)
|
|
{
|
|
record = n.ident;
|
|
scan(&n);
|
|
if (n.value == TOK.comma)
|
|
{
|
|
scan(&n);
|
|
if (n.value == TOK.int32Literal)
|
|
{
|
|
setPackAlign(n);
|
|
scan(&n);
|
|
}
|
|
else
|
|
error(loc, "alignment value expected, not `%s`", n.toChars());
|
|
}
|
|
}
|
|
else if (n.value == TOK.int32Literal)
|
|
{
|
|
setPackAlign(n);
|
|
scan(&n);
|
|
}
|
|
else
|
|
error(loc, "alignment value expected, not `%s`", n.toChars());
|
|
}
|
|
this.records.push(record);
|
|
this.packs.push(packalign);
|
|
return closingParen();
|
|
}
|
|
/* # pragma pack ( pop )
|
|
* # pragma pack ( pop PopList )
|
|
* PopList :
|
|
* , IdentifierOrInteger
|
|
* , IdentifierOrInteger PopList
|
|
* IdentifierOrInteger:
|
|
* identifier
|
|
* integer
|
|
*/
|
|
if (n.value == TOK.identifier && n.ident == Id.pop)
|
|
{
|
|
scan(&n);
|
|
size_t len = this.records.length;
|
|
if (n.value == TOK.rightParenthesis) // #pragma pack ( pop )
|
|
{
|
|
if (len == 0) // nothing to pop
|
|
return closingParen();
|
|
|
|
this.records.setDim(len - 1);
|
|
this.packs.setDim(len - 1);
|
|
if (len == 1) // stack is now empty
|
|
packalign.setDefault();
|
|
else
|
|
packalign = (*this.packs)[len - 1];
|
|
return closingParen();
|
|
}
|
|
while (n.value == TOK.comma) // #pragma pack ( pop ,
|
|
{
|
|
scan(&n);
|
|
if (n.value == TOK.identifier)
|
|
{
|
|
/* pragma pack(pop, identifier
|
|
* Pop until identifier is found, pop that one too, and set
|
|
* alignment to the new top of the stack.
|
|
* If identifier is not found, do nothing.
|
|
*/
|
|
for ( ; len; --len)
|
|
{
|
|
if ((*this.records)[len - 1] == n.ident)
|
|
{
|
|
this.records.setDim(len - 1);
|
|
this.packs.setDim(len - 1);
|
|
if (len > 1)
|
|
packalign = (*this.packs)[len - 2];
|
|
else
|
|
packalign.setDefault(); // stack empty, use default
|
|
break;
|
|
}
|
|
}
|
|
scan(&n);
|
|
}
|
|
else if (n.value == TOK.int32Literal)
|
|
{
|
|
setPackAlign(n);
|
|
scan(&n);
|
|
}
|
|
else
|
|
{
|
|
error(loc, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n.toChars());
|
|
scan(&n);
|
|
}
|
|
}
|
|
return closingParen();
|
|
}
|
|
/* # pragma pack ( integer )
|
|
* Sets alignment to integer
|
|
*/
|
|
if (n.value == TOK.int32Literal)
|
|
{
|
|
setPackAlign(n);
|
|
scan(&n);
|
|
return closingParen();
|
|
}
|
|
/* # pragma pack ( )
|
|
* Sets alignment to default
|
|
*/
|
|
if (n.value == TOK.rightParenthesis)
|
|
{
|
|
packalign.setDefault();
|
|
return closingParen();
|
|
}
|
|
|
|
error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
|
|
if (n.value != TOK.endOfLine)
|
|
skipToNextLine();
|
|
}
|
|
|
|
//}
|
|
|
|
/******************************************************************************/
|
|
/********************************* #define Parser *****************************/
|
|
//{
|
|
|
|
/**
|
|
* Go through the #define's in the defines buffer and see what we can convert
|
|
* to Dsymbols, which are then appended to symbols[]
|
|
*/
|
|
void addDefines()
|
|
{
|
|
if (!defines || defines.length < 10) // minimum length of a #define line
|
|
return;
|
|
OutBuffer* buf = defines;
|
|
defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence()
|
|
// from appending to slice[]
|
|
const length = buf.length;
|
|
buf.writeByte(0);
|
|
auto slice = buf.peekChars()[0 .. length];
|
|
resetDefineLines(slice); // reset lexer
|
|
|
|
const(char)* endp = &slice[length - 7];
|
|
|
|
size_t[void*] defineTab; // hash table of #define's turned into Symbol's
|
|
// indexed by Identifier, returns index into symbols[]
|
|
// The memory for this is leaked
|
|
|
|
void addVar(AST.VarDeclaration v)
|
|
{
|
|
//printf("addVar() %s\n", v.toChars());
|
|
v.isCmacro(true); // mark it as coming from a C #define
|
|
/* If it's already defined, replace the earlier
|
|
* definition
|
|
*/
|
|
if (size_t* pd = cast(void*)v.ident in defineTab)
|
|
{
|
|
//printf("replacing %s\n", v.toChars());
|
|
(*symbols)[*pd] = v;
|
|
return;
|
|
}
|
|
defineTab[cast(void*)v.ident] = symbols.length;
|
|
symbols.push(v);
|
|
}
|
|
|
|
Token n;
|
|
|
|
while (p < endp)
|
|
{
|
|
if (p[0 .. 7] == "#define")
|
|
{
|
|
p += 7;
|
|
scan(&n);
|
|
//printf("%s\n", n.toChars());
|
|
if (n.value == TOK.identifier)
|
|
{
|
|
auto id = n.ident;
|
|
scan(&n);
|
|
|
|
AST.Type t;
|
|
|
|
switch (n.value)
|
|
{
|
|
case TOK.endOfLine: // #define identifier
|
|
nextDefineLine();
|
|
continue;
|
|
|
|
case TOK.int32Literal:
|
|
case TOK.charLiteral: t = AST.Type.tint32; goto Linteger;
|
|
case TOK.uns32Literal: t = AST.Type.tuns32; goto Linteger;
|
|
case TOK.int64Literal: t = AST.Type.tint64; goto Linteger;
|
|
case TOK.uns64Literal: t = AST.Type.tuns64; goto Linteger;
|
|
|
|
Linteger:
|
|
const intvalue = n.intvalue;
|
|
scan(&n);
|
|
if (n.value == TOK.endOfLine)
|
|
{
|
|
/* Declare manifest constant:
|
|
* enum id = intvalue;
|
|
*/
|
|
AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
|
|
auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
|
|
addVar(v);
|
|
nextDefineLine();
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case TOK.float32Literal: t = AST.Type.tfloat32; goto Lfloat;
|
|
case TOK.float64Literal: t = AST.Type.tfloat64; goto Lfloat;
|
|
case TOK.float80Literal: t = AST.Type.tfloat80; goto Lfloat;
|
|
case TOK.imaginary32Literal: t = AST.Type.timaginary32; goto Lfloat;
|
|
case TOK.imaginary64Literal: t = AST.Type.timaginary64; goto Lfloat;
|
|
case TOK.imaginary80Literal: t = AST.Type.timaginary80; goto Lfloat;
|
|
|
|
Lfloat:
|
|
const floatvalue = n.floatvalue;
|
|
scan(&n);
|
|
if (n.value == TOK.endOfLine)
|
|
{
|
|
/* Declare manifest constant:
|
|
* enum id = floatvalue;
|
|
*/
|
|
AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
|
|
auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
|
|
addVar(v);
|
|
nextDefineLine();
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case TOK.string_:
|
|
const str = n.ustring;
|
|
const len = n.len;
|
|
const postfix = n.postfix;
|
|
scan(&n);
|
|
if (n.value == TOK.endOfLine)
|
|
{
|
|
/* Declare manifest constant:
|
|
* enum id = "string";
|
|
*/
|
|
AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
|
|
auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
|
|
addVar(v);
|
|
nextDefineLine();
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
skipToNextLine();
|
|
}
|
|
else
|
|
{
|
|
scan(&n);
|
|
if (n.value != TOK.endOfLine)
|
|
{
|
|
skipToNextLine();
|
|
}
|
|
}
|
|
nextDefineLine();
|
|
}
|
|
|
|
defines = buf;
|
|
}
|
|
|
|
//}
|
|
}
|