blob: 9da58af046d2983bbf03b86a59ddb81f3c2e2352 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/parse.c
*/
// This is the D parser
#include "root/dsystem.h" // strlen(),memcpy()
#include "root/rmem.h"
#include "mars.h"
#include "lexer.h"
#include "parse.h"
#include "init.h"
#include "attrib.h"
#include "cond.h"
#include "mtype.h"
#include "template.h"
#include "staticassert.h"
#include "expression.h"
#include "statement.h"
#include "module.h"
#include "dsymbol.h"
#include "import.h"
#include "declaration.h"
#include "aggregate.h"
#include "enum.h"
#include "id.h"
#include "version.h"
#include "aliasthis.h"
#include "nspace.h"
#include "hdrgen.h"
Expression *typeToExpression(Type *t);
// Support C cast syntax:
// (type)(expression)
#define CCASTSYNTAX 1
// Support postfix C array declarations, such as
// int a[3][4];
#define CARRAYDECL 1
Parser::Parser(Module *module, const utf8_t *base, size_t length, bool doDocComment)
: Lexer(module ? module->srcfile->toChars() : NULL, base, 0, length, doDocComment, false)
{
//printf("Parser::Parser()\n");
mod = module;
md = NULL;
linkage = LINKd;
endloc = Loc();
inBrackets = 0;
lookingForElse = Loc();
//nextToken(); // start up the scanner
}
/*********************
* Use this constructor for string mixins.
* Input:
* loc location in source file of mixin
*/
Parser::Parser(Loc loc, Module *module, const utf8_t *base, size_t length, bool doDocComment)
: Lexer(module ? module->srcfile->toChars() : NULL, base, 0, length, doDocComment, false)
{
//printf("Parser::Parser()\n");
scanloc = loc;
if (loc.filename)
{
/* Create a pseudo-filename for the mixin string, as it may not even exist
* in the source file.
*/
char *filename = (char *)mem.xmalloc(strlen(loc.filename) + 7 + sizeof(loc.linnum) * 3 + 1);
sprintf(filename, "%s-mixin-%d", loc.filename, (int)loc.linnum);
scanloc.filename = filename;
}
mod = module;
md = NULL;
linkage = LINKd;
endloc = Loc();
inBrackets = 0;
lookingForElse = Loc();
//nextToken(); // start up the scanner
}
Dsymbols *Parser::parseModule()
{
const utf8_t *comment = token.blockComment;
bool isdeprecated = false;
Expression *msg = NULL;
Expressions *udas = NULL;
Dsymbols *decldefs;
Token *tk;
if (skipAttributes(&token, &tk) && tk->value == TOKmodule)
{
while (token.value != TOKmodule)
{
switch (token.value)
{
case TOKdeprecated:
{
// deprecated (...) module ...
if (isdeprecated)
{
error("there is only one deprecation attribute allowed for module declaration");
}
else
{
isdeprecated = true;
}
nextToken();
if (token.value == TOKlparen)
{
check(TOKlparen);
msg = parseAssignExp();
check(TOKrparen);
}
break;
}
case TOKat:
{
Expressions *exps = NULL;
StorageClass stc = parseAttribute(&exps);
if (stc == STCproperty || stc == STCnogc || stc == STCdisable ||
stc == STCsafe || stc == STCtrusted || stc == STCsystem)
{
error("@%s attribute for module declaration is not supported", token.toChars());
}
else
{
udas = UserAttributeDeclaration::concat(udas, exps);
}
if (stc)
nextToken();
break;
}
default:
{
error("'module' expected instead of %s", token.toChars());
nextToken();
break;
}
}
}
}
if (udas)
{
Dsymbols *a = new Dsymbols();
UserAttributeDeclaration *udad = new UserAttributeDeclaration(udas, a);
mod->userAttribDecl = udad;
}
// ModuleDeclation leads off
if (token.value == TOKmodule)
{
Loc loc = token.loc;
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following module");
goto Lerr;
}
else
{
Identifiers *a = NULL;
Identifier *id;
id = token.ident;
while (nextToken() == TOKdot)
{
if (!a)
a = new Identifiers();
a->push(id);
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following package");
goto Lerr;
}
id = token.ident;
}
md = new ModuleDeclaration(loc, a, id);
md->isdeprecated = isdeprecated;
md->msg = msg;
if (token.value != TOKsemicolon)
error("';' expected following module declaration instead of %s", token.toChars());
nextToken();
addComment(mod, comment);
}
}
decldefs = parseDeclDefs(0);
if (token.value != TOKeof)
{
error(token.loc, "unrecognized declaration");
goto Lerr;
}
return decldefs;
Lerr:
while (token.value != TOKsemicolon && token.value != TOKeof)
nextToken();
nextToken();
return new Dsymbols();
}
struct PrefixAttributes
{
StorageClass storageClass;
Expression *depmsg;
LINK link;
Prot protection;
bool setAlignment;
Expression *ealign;
Expressions *udas;
const utf8_t *comment;
PrefixAttributes()
: storageClass(STCundefined),
depmsg(NULL),
link(LINKdefault),
protection(PROTundefined),
setAlignment(false),
ealign(NULL),
udas(NULL),
comment(NULL)
{
}
};
Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes *pAttrs)
{
Dsymbol *lastDecl = NULL; // used to link unittest to its previous declaration
if (!pLastDecl)
pLastDecl = &lastDecl;
LINK linksave = linkage; // save global state
//printf("Parser::parseDeclDefs()\n");
Dsymbols *decldefs = new Dsymbols();
do
{
// parse result
Dsymbol *s = NULL;
Dsymbols *a = NULL;
PrefixAttributes attrs;
if (!once || !pAttrs)
{
pAttrs = &attrs;
pAttrs->comment = token.blockComment;
}
PROTKIND prot;
StorageClass stc;
Condition *condition;
linkage = linksave;
switch (token.value)
{
case TOKenum:
{
/* Determine if this is a manifest constant declaration,
* or a conventional enum.
*/
Token *t = peek(&token);
if (t->value == TOKlcurly || t->value == TOKcolon)
s = parseEnum();
else if (t->value != TOKidentifier)
goto Ldeclaration;
else
{
t = peek(t);
if (t->value == TOKlcurly || t->value == TOKcolon ||
t->value == TOKsemicolon)
s = parseEnum();
else
goto Ldeclaration;
}
break;
}
case TOKimport:
a = parseImport();
// keep pLastDecl
break;
case TOKtemplate:
s = (Dsymbol *)parseTemplateDeclaration();
break;
case TOKmixin:
{
Loc loc = token.loc;
switch (peekNext())
{
case TOKlparen:
{
// mixin(string)
nextToken();
check(TOKlparen, "mixin");
Expression *e = parseAssignExp();
check(TOKrparen);
check(TOKsemicolon);
s = new CompileDeclaration(loc, e);
break;
}
case TOKtemplate:
// mixin template
nextToken();
s = (Dsymbol *)parseTemplateDeclaration(true);
break;
default:
s = parseMixin();
break;
}
break;
}
case TOKwchar: case TOKdchar:
case TOKbool: case TOKchar:
case TOKint8: case TOKuns8:
case TOKint16: case TOKuns16:
case TOKint32: case TOKuns32:
case TOKint64: case TOKuns64:
case TOKint128: case TOKuns128:
case TOKfloat32: case TOKfloat64: case TOKfloat80:
case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
case TOKvoid:
case TOKalias:
case TOKidentifier:
case TOKsuper:
case TOKtypeof:
case TOKdot:
case TOKvector:
case TOKstruct:
case TOKunion:
case TOKclass:
case TOKinterface:
Ldeclaration:
a = parseDeclarations(false, pAttrs, pAttrs->comment);
if (a && a->dim)
*pLastDecl = (*a)[a->dim-1];
break;
case TOKthis:
if (peekNext() == TOKdot)
goto Ldeclaration;
else
s = parseCtor(pAttrs);
break;
case TOKtilde:
s = parseDtor(pAttrs);
break;
case TOKinvariant:
{
Token *t = peek(&token);
if ((t->value == TOKlparen && peek(t)->value == TOKrparen) ||
t->value == TOKlcurly)
{
// invariant {}
// invariant() {}
s = parseInvariant(pAttrs);
}
else
{
error("invariant body expected, not '%s'", token.toChars());
goto Lerror;
}
break;
}
case TOKunittest:
if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
{
s = parseUnitTest(pAttrs);
if (*pLastDecl)
(*pLastDecl)->ddocUnittest = (UnitTestDeclaration *)s;
}
else
{
// Skip over unittest block by counting { }
Loc loc = token.loc;
int braces = 0;
while (1)
{
nextToken();
switch (token.value)
{
case TOKlcurly:
++braces;
continue;
case TOKrcurly:
if (--braces)
continue;
nextToken();
break;
case TOKeof:
/* { */
error(loc, "closing } of unittest not found before end of file");
goto Lerror;
default:
continue;
}
break;
}
// Workaround 14894. Add an empty unittest declaration to keep
// the number of symbols in this scope independent of -unittest.
s = new UnitTestDeclaration(loc, token.loc, STCundefined, NULL);
}
break;
case TOKnew:
s = parseNew(pAttrs);
break;
case TOKdelete:
s = parseDelete(pAttrs);
break;
case TOKcolon:
case TOKlcurly:
error("declaration expected, not '%s'",token.toChars());
goto Lerror;
case TOKrcurly:
case TOKeof:
if (once)
error("declaration expected, not '%s'", token.toChars());
return decldefs;
case TOKstatic:
{
TOK next = peekNext();
if (next == TOKthis)
s = parseStaticCtor(pAttrs);
else if (next == TOKtilde)
s = parseStaticDtor(pAttrs);
else if (next == TOKassert)
s = parseStaticAssert();
else if (next == TOKif)
{
condition = parseStaticIfCondition();
Dsymbols *athen;
if (token.value == TOKcolon)
athen = parseBlock(pLastDecl);
else
{
Loc lookingForElseSave = lookingForElse;
lookingForElse = token.loc;
athen = parseBlock(pLastDecl);
lookingForElse = lookingForElseSave;
}
Dsymbols *aelse = NULL;
if (token.value == TOKelse)
{
Loc elseloc = token.loc;
nextToken();
aelse = parseBlock(pLastDecl);
checkDanglingElse(elseloc);
}
s = new StaticIfDeclaration(condition, athen, aelse);
}
else if (next == TOKimport)
{
a = parseImport();
// keep pLastDecl
}
else
{
stc = STCstatic;
goto Lstc;
}
break;
}
case TOKconst:
if (peekNext() == TOKlparen)
goto Ldeclaration;
stc = STCconst;
goto Lstc;
case TOKimmutable:
if (peekNext() == TOKlparen)
goto Ldeclaration;
stc = STCimmutable;
goto Lstc;
case TOKshared:
{
TOK next = peekNext();
if (next == TOKlparen)
goto Ldeclaration;
if (next == TOKstatic)
{
TOK next2 = peekNext2();
if (next2 == TOKthis)
{
s = parseSharedStaticCtor(pAttrs);
break;
}
if (next2 == TOKtilde)
{
s = parseSharedStaticDtor(pAttrs);
break;
}
}
stc = STCshared;
goto Lstc;
}
case TOKwild:
if (peekNext() == TOKlparen)
goto Ldeclaration;
stc = STCwild;
goto Lstc;
case TOKfinal: stc = STCfinal; goto Lstc;
case TOKauto: stc = STCauto; goto Lstc;
case TOKscope: stc = STCscope; goto Lstc;
case TOKoverride: stc = STCoverride; goto Lstc;
case TOKabstract: stc = STCabstract; goto Lstc;
case TOKsynchronized: stc = STCsynchronized; goto Lstc;
case TOKnothrow: stc = STCnothrow; goto Lstc;
case TOKpure: stc = STCpure; goto Lstc;
case TOKref: stc = STCref; goto Lstc;
case TOKgshared: stc = STCgshared; goto Lstc;
//case TOKmanifest: stc = STCmanifest; goto Lstc;
case TOKat:
{
Expressions *exps = NULL;
stc = parseAttribute(&exps);
if (stc)
goto Lstc; // it's a predefined attribute
// no redundant/conflicting check for UDAs
pAttrs->udas = UserAttributeDeclaration::concat(pAttrs->udas, exps);
goto Lautodecl;
}
Lstc:
pAttrs->storageClass = appendStorageClass(pAttrs->storageClass, stc);
nextToken();
Lautodecl:
Token *tk;
/* Look for auto initializers:
* storage_class identifier = initializer;
* storage_class identifier(...) = initializer;
*/
if (token.value == TOKidentifier &&
skipParensIf(peek(&token), &tk) &&
tk->value == TOKassign)
{
a = parseAutoDeclarations(pAttrs->storageClass, pAttrs->comment);
pAttrs->storageClass = STCundefined;
if (a && a->dim)
*pLastDecl = (*a)[a->dim-1];
if (pAttrs->udas)
{
s = new UserAttributeDeclaration(pAttrs->udas, a);
pAttrs->udas = NULL;
}
break;
}
/* Look for return type inference for template functions.
*/
if (token.value == TOKidentifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
(tk->value == TOKlparen || tk->value == TOKlcurly || tk->value == TOKin ||
tk->value == TOKout || tk->value == TOKdo ||
(tk->value == TOKidentifier && tk->ident == Id::_body))
)
{
a = parseDeclarations(true, pAttrs, pAttrs->comment);
if (a && a->dim)
*pLastDecl = (*a)[a->dim-1];
if (pAttrs->udas)
{
s = new UserAttributeDeclaration(pAttrs->udas, a);
pAttrs->udas = NULL;
}
break;
}
a = parseBlock(pLastDecl, pAttrs);
if (pAttrs->storageClass != STCundefined)
{
s = new StorageClassDeclaration(pAttrs->storageClass, a);
pAttrs->storageClass = STCundefined;
}
if (pAttrs->udas)
{
if (s)
{
a = new Dsymbols();
a->push(s);
}
s = new UserAttributeDeclaration(pAttrs->udas, a);
pAttrs->udas = NULL;
}
break;
case TOKdeprecated:
{
if (peek(&token)->value != TOKlparen)
{
stc = STCdeprecated;
goto Lstc;
}
nextToken();
check(TOKlparen);
Expression *e = parseAssignExp();
check(TOKrparen);
if (pAttrs->depmsg)
{
error("conflicting storage class 'deprecated(%s)' and 'deprecated(%s)'",
pAttrs->depmsg->toChars(), e->toChars());
}
pAttrs->depmsg = e;
a = parseBlock(pLastDecl, pAttrs);
if (pAttrs->depmsg)
{
s = new DeprecatedDeclaration(pAttrs->depmsg, a);
pAttrs->depmsg = NULL;
}
break;
}
case TOKlbracket:
{
if (peekNext() == TOKrbracket)
error("empty attribute list is not allowed");
error("use @(attributes) instead of [attributes]");
Expressions *exps = parseArguments();
// no redundant/conflicting check for UDAs
pAttrs->udas = UserAttributeDeclaration::concat(pAttrs->udas, exps);
a = parseBlock(pLastDecl, pAttrs);
if (pAttrs->udas)
{
s = new UserAttributeDeclaration(pAttrs->udas, a);
pAttrs->udas = NULL;
}
break;
}
case TOKextern:
{
if (peek(&token)->value != TOKlparen)
{
stc = STCextern;
goto Lstc;
}
Loc linkLoc = token.loc;
Identifiers *idents = NULL;
CPPMANGLE cppmangle = CPPMANGLEdefault;
bool cppMangleOnly = false;
LINK link = parseLinkage(&idents, &cppmangle, &cppMangleOnly);
if (pAttrs->link != LINKdefault)
{
if (pAttrs->link != link)
{
error("conflicting linkage extern (%s) and extern (%s)",
linkageToChars(pAttrs->link), linkageToChars(link));
}
else if (idents)
{
// Allow:
// extern(C++, foo) extern(C++, bar) void foo();
// to be equivalent with:
// extern(C++, foo.bar) void foo();
}
else
error("redundant linkage extern (%s)", linkageToChars(pAttrs->link));
}
pAttrs->link = link;
this->linkage = link;
a = parseBlock(pLastDecl, pAttrs);
if (idents)
{
assert(link == LINKcpp);
assert(idents->dim);
for (size_t i = idents->dim; i;)
{
Identifier *id = (*idents)[--i];
if (s)
{
a = new Dsymbols();
a->push(s);
}
s = new Nspace(linkLoc, id, a, cppMangleOnly);
}
delete idents;
pAttrs->link = LINKdefault;
}
else if (pAttrs->link != LINKdefault)
{
s = new LinkDeclaration(pAttrs->link, a);
pAttrs->link = LINKdefault;
}
else if (cppmangle != CPPMANGLEdefault)
{
assert(link == LINKcpp);
s = new CPPMangleDeclaration(cppmangle, a);
}
break;
}
case TOKprivate: prot = PROTprivate; goto Lprot;
case TOKpackage: prot = PROTpackage; goto Lprot;
case TOKprotected: prot = PROTprotected; goto Lprot;
case TOKpublic: prot = PROTpublic; goto Lprot;
case TOKexport: prot = PROTexport; goto Lprot;
Lprot:
{
if (pAttrs->protection.kind != PROTundefined)
{
if (pAttrs->protection.kind != prot)
error("conflicting protection attribute '%s' and '%s'",
protectionToChars(pAttrs->protection.kind), protectionToChars(prot));
else
error("redundant protection attribute '%s'", protectionToChars(prot));
}
pAttrs->protection.kind = prot;
nextToken();
// optional qualified package identifier to bind
// protection to
Identifiers *pkg_prot_idents = NULL;
if (pAttrs->protection.kind == PROTpackage && token.value == TOKlparen)
{
pkg_prot_idents = parseQualifiedIdentifier("protection package");
if (pkg_prot_idents)
check(TOKrparen);
else
{
while (token.value != TOKsemicolon && token.value != TOKeof)
nextToken();
nextToken();
break;
}
}
Loc attrloc = token.loc;
a = parseBlock(pLastDecl, pAttrs);
if (pAttrs->protection.kind != PROTundefined)
{
if (pAttrs->protection.kind == PROTpackage && pkg_prot_idents)
s = new ProtDeclaration(attrloc, pkg_prot_idents, a);
else
s = new ProtDeclaration(attrloc, pAttrs->protection, a);
pAttrs->protection = Prot(PROTundefined);
}
break;
}
case TOKalign:
{
const Loc attrLoc = token.loc;
nextToken();
Expression *e = NULL; // default
if (token.value == TOKlparen)
{
nextToken();
e = parseAssignExp();
check(TOKrparen);
}
if (pAttrs->setAlignment)
{
const char *s1 = "";
OutBuffer buf1;
if (e)
{
buf1.printf("(%s)", e->toChars());
s1 = buf1.peekString();
}
error("redundant alignment attribute align%s", s1);
}
pAttrs->setAlignment = true;
pAttrs->ealign = e;
a = parseBlock(pLastDecl, pAttrs);
if (pAttrs->setAlignment)
{
s = new AlignDeclaration(attrLoc, pAttrs->ealign, a);
pAttrs->setAlignment = false;
pAttrs->ealign = NULL;
}
break;
}
case TOKpragma:
{
Expressions *args = NULL;
Loc loc = token.loc;
nextToken();
check(TOKlparen);
if (token.value != TOKidentifier)
{
error("pragma(identifier) expected");
goto Lerror;
}
Identifier *ident = token.ident;
nextToken();
if (token.value == TOKcomma && peekNext() != TOKrparen)
args = parseArguments(); // pragma(identifier, args...)
else
check(TOKrparen); // pragma(identifier)
Dsymbols *a2 = NULL;
if (token.value == TOKsemicolon)
{
/* Bugzilla 2354: Accept single semicolon as an empty
* DeclarationBlock following attribute.
*
* Attribute DeclarationBlock
* Pragma DeclDef
* ;
*/
nextToken();
}
else
a2 = parseBlock(pLastDecl);
s = new PragmaDeclaration(loc, ident, args, a2);
break;
}
case TOKdebug:
nextToken();
if (token.value == TOKassign)
{
nextToken();
if (token.value == TOKidentifier)
s = new DebugSymbol(token.loc, token.ident);
else if (token.value == TOKint32v || token.value == TOKint64v)
s = new DebugSymbol(token.loc, (unsigned)token.uns64value);
else
{
error("identifier or integer expected, not %s", token.toChars());
s = NULL;
}
nextToken();
if (token.value != TOKsemicolon)
error("semicolon expected");
nextToken();
break;
}
condition = parseDebugCondition();
goto Lcondition;
case TOKversion:
nextToken();
if (token.value == TOKassign)
{
nextToken();
if (token.value == TOKidentifier)
s = new VersionSymbol(token.loc, token.ident);
else if (token.value == TOKint32v || token.value == TOKint64v)
s = new VersionSymbol(token.loc, (unsigned)token.uns64value);
else
{
error("identifier or integer expected, not %s", token.toChars());
s = NULL;
}
nextToken();
if (token.value != TOKsemicolon)
error("semicolon expected");
nextToken();
break;
}
condition = parseVersionCondition();
goto Lcondition;
Lcondition:
{
Dsymbols *athen;
if (token.value == TOKcolon)
athen = parseBlock(pLastDecl);
else
{
Loc lookingForElseSave = lookingForElse;
lookingForElse = token.loc;
athen = parseBlock(pLastDecl);
lookingForElse = lookingForElseSave;
}
Dsymbols *aelse = NULL;
if (token.value == TOKelse)
{
Loc elseloc = token.loc;
nextToken();
aelse = parseBlock(pLastDecl);
checkDanglingElse(elseloc);
}
s = new ConditionalDeclaration(condition, athen, aelse);
break;
}
case TOKsemicolon: // empty declaration
//error("empty declaration");
nextToken();
continue;
default:
error("declaration expected, not '%s'",token.toChars());
Lerror:
while (token.value != TOKsemicolon && token.value != TOKeof)
nextToken();
nextToken();
s = NULL;
continue;
}
if (s)
{
if (!s->isAttribDeclaration())
*pLastDecl = s;
decldefs->push(s);
addComment(s, pAttrs->comment);
}
else if (a && a->dim)
{
decldefs->append(a);
}
} while (!once);
linkage = linksave;
return decldefs;
}
/*********************************************
* Give error on redundant/conflicting storage class.
*
* TODO: remove deprecation in 2.068 and keep only error
*/
StorageClass Parser::appendStorageClass(StorageClass storageClass, StorageClass stc,
bool deprec)
{
if ((storageClass & stc) ||
(storageClass & STCin && stc & (STCconst | STCscope)) ||
(stc & STCin && storageClass & (STCconst | STCscope)))
{
OutBuffer buf;
stcToBuffer(&buf, stc);
if (deprec)
deprecation("redundant attribute '%s'", buf.peekString());
else
error("redundant attribute '%s'", buf.peekString());
return storageClass | stc;
}
storageClass |= stc;
if (stc & (STCconst | STCimmutable | STCmanifest))
{
StorageClass u = storageClass & (STCconst | STCimmutable | STCmanifest);
if (u & (u - 1))
error("conflicting attribute '%s'", Token::toChars(token.value));
}
if (stc & (STCgshared | STCshared | STCtls))
{
StorageClass u = storageClass & (STCgshared | STCshared | STCtls);
if (u & (u - 1))
error("conflicting attribute '%s'", Token::toChars(token.value));
}
if (stc & (STCsafe | STCsystem | STCtrusted))
{
StorageClass u = storageClass & (STCsafe | STCsystem | STCtrusted);
if (u & (u - 1))
error("conflicting attribute '@%s'", token.toChars());
}
return storageClass;
}
/***********************************************
* Parse attribute, lexer is on '@'.
* Input:
* pudas array of UDAs to append to
* Returns:
* storage class if a predefined attribute; also scanner remains on identifier.
* 0 if not a predefined attribute
* *pudas set if user defined attribute, scanner is past UDA
* *pudas NULL if not a user defined attribute
*/
StorageClass Parser::parseAttribute(Expressions **pudas)
{
nextToken();
Expressions *udas = NULL;
StorageClass stc = 0;
if (token.value == TOKidentifier)
{
if (token.ident == Id::property)
stc = STCproperty;
else if (token.ident == Id::nogc)
stc = STCnogc;
else if (token.ident == Id::safe)
stc = STCsafe;
else if (token.ident == Id::trusted)
stc = STCtrusted;
else if (token.ident == Id::system)
stc = STCsystem;
else if (token.ident == Id::disable)
stc = STCdisable;
else if (token.ident == Id::future)
stc = STCfuture;
else
{
// Allow identifier, template instantiation, or function call
Expression *exp = parsePrimaryExp();
if (token.value == TOKlparen)
{
Loc loc = token.loc;
exp = new CallExp(loc, exp, parseArguments());
}
udas = new Expressions();
udas->push(exp);
}
}
else if (token.value == TOKlparen)
{
// @( ArgumentList )
// Concatenate with existing
if (peekNext() == TOKrparen)
error("empty attribute list is not allowed");
udas = parseArguments();
}
else
{
error("@identifier or @(ArgumentList) expected, not @%s", token.toChars());
}
if (stc)
{
}
else if (udas)
{
*pudas = UserAttributeDeclaration::concat(*pudas, udas);
}
else
error("valid attributes are @property, @safe, @trusted, @system, @disable");
return stc;
}
/***********************************************
* Parse const/immutable/shared/inout/nothrow/pure postfix
*/
StorageClass Parser::parsePostfix(StorageClass storageClass, Expressions **pudas)
{
while (1)
{
StorageClass stc;
switch (token.value)
{
case TOKconst: stc = STCconst; break;
case TOKimmutable: stc = STCimmutable; break;
case TOKshared: stc = STCshared; break;
case TOKwild: stc = STCwild; break;
case TOKnothrow: stc = STCnothrow; break;
case TOKpure: stc = STCpure; break;
case TOKreturn: stc = STCreturn; break;
case TOKscope: stc = STCscope; break;
case TOKat:
{
Expressions *udas = NULL;
stc = parseAttribute(&udas);
if (udas)
{
if (pudas)
*pudas = UserAttributeDeclaration::concat(*pudas, udas);
else
{
// Disallow:
// void function() @uda fp;
// () @uda { return 1; }
error("user defined attributes cannot appear as postfixes");
}
continue;
}
break;
}
default:
return storageClass;
}
storageClass = appendStorageClass(storageClass, stc, true);
nextToken();
}
}
StorageClass Parser::parseTypeCtor()
{
StorageClass storageClass = STCundefined;
while (1)
{
if (peek(&token)->value == TOKlparen)
return storageClass;
StorageClass stc;
switch (token.value)
{
case TOKconst: stc = STCconst; break;
case TOKimmutable: stc = STCimmutable; break;
case TOKshared: stc = STCshared; break;
case TOKwild: stc = STCwild; break;
default:
return storageClass;
}
storageClass = appendStorageClass(storageClass, stc);
nextToken();
}
}
/********************************************
* Parse declarations after an align, protection, or extern decl.
*/
Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl, PrefixAttributes *pAttrs)
{
Dsymbols *a = NULL;
//printf("parseBlock()\n");
switch (token.value)
{
case TOKsemicolon:
error("declaration expected following attribute, not ';'");
nextToken();
break;
case TOKeof:
error("declaration expected following attribute, not EOF");
break;
case TOKlcurly:
{
Loc lookingForElseSave = lookingForElse;
lookingForElse = Loc();
nextToken();
a = parseDeclDefs(0, pLastDecl);
if (token.value != TOKrcurly)
{
/* { */
error("matching '}' expected, not %s", token.toChars());
}
else
nextToken();
lookingForElse = lookingForElseSave;
break;
}
case TOKcolon:
nextToken();
a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
break;
default:
a = parseDeclDefs(1, pLastDecl, pAttrs);
break;
}
return a;
}
/**********************************
* Parse a static assertion.
* Current token is 'static'.
*/
StaticAssert *Parser::parseStaticAssert()
{
Loc loc = token.loc;
Expression *exp;
Expression *msg = NULL;
//printf("parseStaticAssert()\n");
nextToken();
nextToken();
check(TOKlparen);
exp = parseAssignExp();
if (token.value == TOKcomma)
{
nextToken();
if (token.value != TOKrparen)
{
msg = parseAssignExp();
if (token.value == TOKcomma)
nextToken();
}
}
check(TOKrparen);
check(TOKsemicolon);
return new StaticAssert(loc, exp, msg);
}
/***********************************
* Parse typeof(expression).
* Current token is on the 'typeof'.
*/
TypeQualified *Parser::parseTypeof()
{
TypeQualified *t;
Loc loc = token.loc;
nextToken();
check(TOKlparen);
if (token.value == TOKreturn) // typeof(return)
{
nextToken();
t = new TypeReturn(loc);
}
else
{
Expression *exp = parseExpression(); // typeof(expression)
t = new TypeTypeof(loc, exp);
}
check(TOKrparen);
return t;
}
/***********************************
* Parse __vector(type).
* Current token is on the '__vector'.
*/
Type *Parser::parseVector()
{
nextToken();
check(TOKlparen);
Type *tb = parseType();
check(TOKrparen);
return new TypeVector(tb);
}
/***********************************
* Parse:
* extern (linkage)
* extern (C++, namespaces)
* extern (C++, "namespace", "namespaces", ...)
* The parser is on the 'extern' token.
*/
LINK Parser::parseLinkage(Identifiers **pidents, CPPMANGLE *pcppmangle, bool *pcppMangleOnly)
{
Identifiers *idents = NULL;
CPPMANGLE cppmangle = CPPMANGLEdefault;
bool cppMangleOnly = false;
LINK link = LINKdefault;
nextToken();
assert(token.value == TOKlparen);
nextToken();
if (token.value == TOKidentifier)
{ Identifier *id = token.ident;
nextToken();
if (id == Id::Windows)
link = LINKwindows;
else if (id == Id::Pascal)
link = LINKpascal;
else if (id == Id::D)
link = LINKd;
else if (id == Id::C)
{
link = LINKc;
if (token.value == TOKplusplus)
{
link = LINKcpp;
nextToken();
if (token.value == TOKcomma) // , namespaces or class or struct
{
nextToken();
if (token.value == TOKclass || token.value == TOKstruct)
{
cppmangle = token.value == TOKclass ? CPPMANGLEclass : CPPMANGLEstruct;
nextToken();
}
else if (token.value == TOKstring) // extern(C++, "namespace", "namespaces")
{
cppMangleOnly = true;
idents = new Identifiers();
while (1)
{
StringExp *stringExp = (StringExp *)parsePrimaryExp();
const char *name = stringExp->toPtr();
if (stringExp->len == 0)
{
error("invalid zero length C++ namespace");
idents = NULL;
break;
}
else if (!Identifier::isValidIdentifier(name))
{
error("expected valid identifer for C++ namespace but got `%s`", name);
idents = NULL;
break;
}
idents->push(Identifier::idPool(name));
if (token.value == TOKcomma)
{
nextToken();
if (token.value != TOKstring)
{
error("string expected following `,` for C++ namespace, not `%s`", token.toChars());
idents = NULL;
break;
}
}
else
break;
}
}
else
{
idents = new Identifiers();
while (1)
{
if (token.value == TOKidentifier)
{
Identifier *idn = token.ident;
idents->push(idn);
nextToken();
if (token.value == TOKdot)
{
nextToken();
continue;
}
}
else
{
error("identifier expected for C++ namespace");
idents = NULL; // error occurred, invalidate list of elements.
}
break;
}
}
}
}
}
else if (id == Id::Objective) // Looking for tokens "Objective-C"
{
if (token.value == TOKmin)
{
nextToken();
if (token.ident == Id::C)
{
link = LINKobjc;
nextToken();
}
else
goto LinvalidLinkage;
}
else
goto LinvalidLinkage;
}
else if (id == Id::System)
{
link = LINKsystem;
}
else
{
LinvalidLinkage:
error("valid linkage identifiers are D, C, C++, Objective-C, Pascal, Windows, System");
link = LINKd;
}
}
else
{
link = LINKd; // default
}
check(TOKrparen);
*pidents = idents;
*pcppmangle = cppmangle;
*pcppMangleOnly = cppMangleOnly;
return link;
}
/***********************************
* Parse ident1.ident2.ident3
*
* Params:
* entity = what qualified identifier is expected to resolve into.
* Used only for better error message
*
* Returns:
* array of identifiers with actual qualified one stored last
*/
Identifiers *Parser::parseQualifiedIdentifier(const char *entity)
{
Identifiers *qualified = NULL;
do
{
nextToken();
if (token.value != TOKidentifier)
{
error("%s expected as dot-separated identifiers, got '%s'",
entity, token.toChars());
return NULL;
}
Identifier *id = token.ident;
if (!qualified)
qualified = new Identifiers();
qualified->push(id);
nextToken();
} while (token.value == TOKdot);
return qualified;
}
/**************************************
* Parse a debug conditional
*/
Condition *Parser::parseDebugCondition()
{
Condition *c;
if (token.value == TOKlparen)
{
nextToken();
unsigned level = 1;
Identifier *id = NULL;
if (token.value == TOKidentifier)
id = token.ident;
else if (token.value == TOKint32v || token.value == TOKint64v)
level = (unsigned)token.uns64value;
else
error("identifier or integer expected, not %s", token.toChars());
nextToken();
check(TOKrparen);
c = new DebugCondition(mod, level, id);
}
else
c = new DebugCondition(mod, 1, NULL);
return c;
}
/**************************************
* Parse a version conditional
*/
Condition *Parser::parseVersionCondition()
{
Condition *c;
unsigned level = 1;
Identifier *id = NULL;
if (token.value == TOKlparen)
{
nextToken();
/* Allow:
* version (unittest)
* version (assert)
* even though they are keywords
*/
if (token.value == TOKidentifier)
id = token.ident;
else if (token.value == TOKint32v || token.value == TOKint64v)
level = (unsigned)token.uns64value;
else if (token.value == TOKunittest)
id = Identifier::idPool(Token::toChars(TOKunittest));
else if (token.value == TOKassert)
id = Identifier::idPool(Token::toChars(TOKassert));
else
error("identifier or integer expected, not %s", token.toChars());
nextToken();
check(TOKrparen);
}
else
error("(condition) expected following version");
c = new VersionCondition(mod, level, id);
return c;
}
/***********************************************
* static if (expression)
* body
* else
* body
* Current token is 'static'.
*/
Condition *Parser::parseStaticIfCondition()
{
Expression *exp;
Condition *condition;
Loc loc = token.loc;
nextToken();
nextToken();
if (token.value == TOKlparen)
{
nextToken();
exp = parseAssignExp();
check(TOKrparen);
}
else
{
error("(expression) expected following static if");
exp = NULL;
}
condition = new StaticIfCondition(loc, exp);
return condition;
}
/*****************************************
* Parse a constructor definition:
* this(parameters) { body }
* or postblit:
* this(this) { body }
* or constructor template:
* this(templateparameters)(parameters) { body }
* Current token is 'this'.
*/
Dsymbol *Parser::parseCtor(PrefixAttributes *pAttrs)
{
Expressions *udas = NULL;
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
if (token.value == TOKlparen && peekNext() == TOKthis && peekNext2() == TOKrparen)
{
// this(this) { ... }
nextToken();
nextToken();
check(TOKrparen);
stc = parsePostfix(stc, &udas);
if (stc & STCstatic)
error(loc, "postblit cannot be static");
PostBlitDeclaration *f = new PostBlitDeclaration(loc, Loc(), stc, Id::postblit);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
if (udas)
{
Dsymbols *a = new Dsymbols();
a->push(f);
s = new UserAttributeDeclaration(udas, a);
}
return s;
}
/* Look ahead to see if:
* this(...)(...)
* which is a constructor template
*/
TemplateParameters *tpl = NULL;
if (token.value == TOKlparen && peekPastParen(&token)->value == TOKlparen)
{
tpl = parseTemplateParameterList();
}
/* Just a regular constructor
*/
int varargs;
Parameters *parameters = parseParameters(&varargs);
stc = parsePostfix(stc, &udas);
if (varargs != 0 || Parameter::dim(parameters) != 0)
{
if (stc & STCstatic)
error(loc, "constructor cannot be static");
}
else if (StorageClass ss = stc & (STCshared | STCstatic)) // this()
{
if (ss == STCstatic)
error(loc, "use 'static this()' to declare a static constructor");
else if (ss == (STCshared | STCstatic))
error(loc, "use 'shared static this()' to declare a shared static constructor");
}
Expression *constraint = tpl ? parseConstraint() : NULL;
Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto
tf = tf->addSTC(stc);
CtorDeclaration *f = new CtorDeclaration(loc, Loc(), stc, tf);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
if (udas)
{
Dsymbols *a = new Dsymbols();
a->push(f);
s = new UserAttributeDeclaration(udas, a);
}
if (tpl)
{
// Wrap a template around it
Dsymbols *decldefs = new Dsymbols();
decldefs->push(s);
s = new TemplateDeclaration(loc, f->ident, tpl, constraint, decldefs);
}
return s;
}
/*****************************************
* Parse a destructor definition:
* ~this() { body }
* Current token is '~'.
*/
Dsymbol *Parser::parseDtor(PrefixAttributes *pAttrs)
{
Expressions *udas = NULL;
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
check(TOKthis);
check(TOKlparen);
check(TOKrparen);
stc = parsePostfix(stc, &udas);
if (StorageClass ss = stc & (STCshared | STCstatic))
{
if (ss == STCstatic)
error(loc, "use 'static ~this()' to declare a static destructor");
else if (ss == (STCshared | STCstatic))
error(loc, "use 'shared static ~this()' to declare a shared static destructor");
}
DtorDeclaration *f = new DtorDeclaration(loc, Loc(), stc, Id::dtor);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
if (udas)
{
Dsymbols *a = new Dsymbols();
a->push(f);
s = new UserAttributeDeclaration(udas, a);
}
return s;
}
/*****************************************
* Parse a static constructor definition:
* static this() { body }
* Current token is 'static'.
*/
Dsymbol *Parser::parseStaticCtor(PrefixAttributes *pAttrs)
{
//Expressions *udas = NULL;
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
nextToken();
check(TOKlparen);
check(TOKrparen);
stc = parsePostfix(stc & ~STC_TYPECTOR, NULL) | stc;
if (stc & STCshared)
error(loc, "use 'shared static this()' to declare a shared static constructor");
else if (stc & STCstatic)
appendStorageClass(stc, STCstatic); // complaint for the redundancy
else if (StorageClass modStc = stc & STC_TYPECTOR)
{
OutBuffer buf;
stcToBuffer(&buf, modStc);
error(loc, "static constructor cannot be %s", buf.peekString());
}
stc &= ~(STCstatic | STC_TYPECTOR);
StaticCtorDeclaration *f = new StaticCtorDeclaration(loc, Loc(), stc);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
return s;
}
/*****************************************
* Parse a static destructor definition:
* static ~this() { body }
* Current token is 'static'.
*/
Dsymbol *Parser::parseStaticDtor(PrefixAttributes *pAttrs)
{
Expressions *udas = NULL;
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
nextToken();
check(TOKthis);
check(TOKlparen);
check(TOKrparen);
stc = parsePostfix(stc & ~STC_TYPECTOR, &udas) | stc;
if (stc & STCshared)
error(loc, "use 'shared static ~this()' to declare a shared static destructor");
else if (stc & STCstatic)
appendStorageClass(stc, STCstatic); // complaint for the redundancy
else if (StorageClass modStc = stc & STC_TYPECTOR)
{
OutBuffer buf;
stcToBuffer(&buf, modStc);
error(loc, "static destructor cannot be %s", buf.peekString());
}
stc &= ~(STCstatic | STC_TYPECTOR);
StaticDtorDeclaration *f = new StaticDtorDeclaration(loc, Loc(), stc);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
if (udas)
{
Dsymbols *a = new Dsymbols();
a->push(f);
s = new UserAttributeDeclaration(udas, a);
}
return s;
}
/*****************************************
* Parse a shared static constructor definition:
* shared static this() { body }
* Current token is 'shared'.
*/
Dsymbol *Parser::parseSharedStaticCtor(PrefixAttributes *pAttrs)
{
//Expressions *udas = NULL;
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
nextToken();
nextToken();
check(TOKlparen);
check(TOKrparen);
stc = parsePostfix(stc & ~STC_TYPECTOR, NULL) | stc;
if (StorageClass ss = stc & (STCshared | STCstatic))
appendStorageClass(stc, ss); // complaint for the redundancy
else if (StorageClass modStc = stc & STC_TYPECTOR)
{
OutBuffer buf;
stcToBuffer(&buf, modStc);
error(loc, "shared static constructor cannot be %s", buf.peekString());
}
stc &= ~(STCstatic | STC_TYPECTOR);
SharedStaticCtorDeclaration *f = new SharedStaticCtorDeclaration(loc, Loc(), stc);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
return s;
}
/*****************************************
* Parse a shared static destructor definition:
* shared static ~this() { body }
* Current token is 'shared'.
*/
Dsymbol *Parser::parseSharedStaticDtor(PrefixAttributes *pAttrs)
{
Expressions *udas = NULL;
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
nextToken();
nextToken();
check(TOKthis);
check(TOKlparen);
check(TOKrparen);
stc = parsePostfix(stc & ~STC_TYPECTOR, &udas) | stc;
if (StorageClass ss = stc & (STCshared | STCstatic))
appendStorageClass(stc, ss); // complaint for the redundancy
else if (StorageClass modStc = stc & STC_TYPECTOR)
{
OutBuffer buf;
stcToBuffer(&buf, modStc);
error(loc, "shared static destructor cannot be %s", buf.peekString());
}
stc &= ~(STCstatic | STC_TYPECTOR);
SharedStaticDtorDeclaration *f = new SharedStaticDtorDeclaration(loc, Loc(), stc);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
if (udas)
{
Dsymbols *a = new Dsymbols();
a->push(f);
s = new UserAttributeDeclaration(udas, a);
}
return s;
}
/*****************************************
* Parse an invariant definition:
* invariant() { body }
* Current token is 'invariant'.
*/
Dsymbol *Parser::parseInvariant(PrefixAttributes *pAttrs)
{
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
if (token.value == TOKlparen) // optional ()
{
nextToken();
check(TOKrparen);
}
InvariantDeclaration *f = new InvariantDeclaration(loc, Loc(), stc);
if (pAttrs)
pAttrs->storageClass = STCundefined;
f->fbody = parseStatement(PScurly);
return f;
}
/*****************************************
* Parse a unittest definition:
* unittest { body }
* Current token is 'unittest'.
*/
Dsymbol *Parser::parseUnitTest(PrefixAttributes *pAttrs)
{
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
const utf8_t *begPtr = token.ptr + 1; // skip '{'
const utf8_t *endPtr = NULL;
Statement *sbody = parseStatement(PScurly, &endPtr);
/** Extract unittest body as a string. Must be done eagerly since memory
will be released by the lexer before doc gen. */
char *docline = NULL;
if (global.params.doDocComments && endPtr > begPtr)
{
/* Remove trailing whitespaces */
for (const utf8_t *p = endPtr - 1;
begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
{
endPtr = p;
}
size_t len = endPtr - begPtr;
if (len > 0)
{
docline = (char *)mem.xmalloc(len + 2);
memcpy(docline, begPtr, len);
docline[len ] = '\n'; // Terminate all lines by LF
docline[len+1] = '\0';
}
}
UnitTestDeclaration *f = new UnitTestDeclaration(loc, token.loc, stc, docline);
if (pAttrs)
pAttrs->storageClass = STCundefined;
f->fbody = sbody;
return f;
}
/*****************************************
* Parse a new definition:
* new(parameters) { body }
* Current token is 'new'.
*/
Dsymbol *Parser::parseNew(PrefixAttributes *pAttrs)
{
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
int varargs;
Parameters *parameters = parseParameters(&varargs);
NewDeclaration *f = new NewDeclaration(loc, Loc(), stc, parameters, varargs);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
return s;
}
/*****************************************
* Parse a delete definition:
* delete(parameters) { body }
* Current token is 'delete'.
*/
Dsymbol *Parser::parseDelete(PrefixAttributes *pAttrs)
{
Loc loc = token.loc;
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
int varargs;
Parameters *parameters = parseParameters(&varargs);
if (varargs)
error("... not allowed in delete function parameter list");
DeleteDeclaration *f = new DeleteDeclaration(loc, Loc(), stc, parameters);
if (pAttrs)
pAttrs->storageClass = STCundefined;
Dsymbol *s = parseContracts(f);
return s;
}
/**********************************************
* Parse parameter list.
*/
Parameters *Parser::parseParameters(int *pvarargs, TemplateParameters **tpl)
{
Parameters *parameters = new Parameters();
int varargs = 0;
int hasdefault = 0;
check(TOKlparen);
while (1)
{
Identifier *ai = NULL;
Type *at;
StorageClass storageClass = 0;
StorageClass stc;
Expression *ae;
for (;1; nextToken())
{
switch (token.value)
{
case TOKrparen:
break;
case TOKdotdotdot:
varargs = 1;
nextToken();
break;
case TOKconst:
if (peek(&token)->value == TOKlparen)
goto Ldefault;
stc = STCconst;
goto L2;
case TOKimmutable:
if (peek(&token)->value == TOKlparen)
goto Ldefault;
stc = STCimmutable;
goto L2;
case TOKshared:
if (peek(&token)->value == TOKlparen)
goto Ldefault;
stc = STCshared;
goto L2;
case TOKwild:
if (peek(&token)->value == TOKlparen)
goto Ldefault;
stc = STCwild;
goto L2;
case TOKin: stc = STCin; goto L2;
case TOKout: stc = STCout; goto L2;
case TOKref: stc = STCref; goto L2;
case TOKlazy: stc = STClazy; goto L2;
case TOKscope: stc = STCscope; goto L2;
case TOKfinal: stc = STCfinal; goto L2;
case TOKauto: stc = STCauto; goto L2;
case TOKreturn: stc = STCreturn; goto L2;
L2:
storageClass = appendStorageClass(storageClass, stc);
continue;
default:
Ldefault:
{ stc = storageClass & (STCin | STCout | STCref | STClazy);
// if stc is not a power of 2
if (stc & (stc - 1) &&
!(stc == (STCin | STCref)))
error("incompatible parameter storage classes");
//if ((storageClass & STCscope) && (storageClass & (STCref | STCout)))
//error("scope cannot be ref or out");
Token *t;
if (tpl && token.value == TOKidentifier &&
(t = peek(&token), (t->value == TOKcomma ||
t->value == TOKrparen ||
t->value == TOKdotdotdot)))
{
Identifier *id = Identifier::generateId("__T");
Loc loc = token.loc;
at = new TypeIdentifier(loc, id);
if (!*tpl)
*tpl = new TemplateParameters();
TemplateParameter *tp = new TemplateTypeParameter(loc, id, NULL, NULL);
(*tpl)->push(tp);
ai = token.ident;
nextToken();
}
else
at = parseType(&ai);
ae = NULL;
if (token.value == TOKassign) // = defaultArg
{ nextToken();
ae = parseDefaultInitExp();
hasdefault = 1;
}
else
{ if (hasdefault)
error("default argument expected for %s",
ai ? ai->toChars() : at->toChars());
}
if (token.value == TOKdotdotdot)
{ /* This is:
* at ai ...
*/
if (storageClass & (STCout | STCref))
error("variadic argument cannot be out or ref");
varargs = 2;
parameters->push(new Parameter(storageClass, at, ai, ae));
nextToken();
break;
}
parameters->push(new Parameter(storageClass, at, ai, ae));
if (token.value == TOKcomma)
{ nextToken();
goto L1;
}
break;
}
}
break;
}
break;
L1: ;
}
check(TOKrparen);
*pvarargs = varargs;
return parameters;
}
/*************************************
*/
EnumDeclaration *Parser::parseEnum()
{
EnumDeclaration *e;
Identifier *id;
Type *memtype;
Loc loc = token.loc;
//printf("Parser::parseEnum()\n");
nextToken();
if (token.value == TOKidentifier)
{
id = token.ident;
nextToken();
}
else
id = NULL;
if (token.value == TOKcolon)
{
nextToken();
int alt = 0;
Loc typeLoc = token.loc;
memtype = parseBasicType();
memtype = parseDeclarator(memtype, &alt, NULL);
checkCstyleTypeSyntax(typeLoc, memtype, alt, NULL);
}
else
memtype = NULL;
e = new EnumDeclaration(loc, id, memtype);
if (token.value == TOKsemicolon && id)
nextToken();
else if (token.value == TOKlcurly)
{
//printf("enum definition\n");
e->members = new Dsymbols();
nextToken();
const utf8_t *comment = token.blockComment;
while (token.value != TOKrcurly)
{
/* Can take the following forms:
* 1. ident
* 2. ident = value
* 3. type ident = value
*/
loc = token.loc;
Type *type = NULL;
Identifier *ident = NULL;
Token *tp = peek(&token);
if (token.value == TOKidentifier &&
(tp->value == TOKassign || tp->value == TOKcomma || tp->value == TOKrcurly))
{
ident = token.ident;
type = NULL;
nextToken();
}
else
{
type = parseType(&ident, NULL);
if (!ident)
error("no identifier for declarator %s", type->toChars());
if (id || memtype)
error("type only allowed if anonymous enum and no enum type");
}
Expression *value;
if (token.value == TOKassign)
{
nextToken();
value = parseAssignExp();
}
else
{
value = NULL;
if (type)
error("if type, there must be an initializer");
}
EnumMember *em = new EnumMember(loc, ident, value, type);
e->members->push(em);
if (token.value == TOKrcurly)
;
else
{
addComment(em, comment);
comment = NULL;
check(TOKcomma);
}
addComment(em, comment);
comment = token.blockComment;
if (token.value == TOKeof)
{
error("premature end of file");
break;
}
}
nextToken();
}
else
error("enum declaration is invalid");
//printf("-parseEnum() %s\n", e->toChars());
return e;
}
/********************************
* Parse struct, union, interface, class.
*/
Dsymbol *Parser::parseAggregate()
{
AggregateDeclaration *a = NULL;
int anon = 0;
Identifier *id;
TemplateParameters *tpl = NULL;
Expression *constraint = NULL;
Loc loc = token.loc;
TOK tok = token.value;
//printf("Parser::parseAggregate()\n");
nextToken();
if (token.value != TOKidentifier)
{
id = NULL;
}
else
{
id = token.ident;
nextToken();
if (token.value == TOKlparen)
{
// Class template declaration.
// Gather template parameter list
tpl = parseTemplateParameterList();
constraint = parseConstraint();
}
}
switch (tok)
{
case TOKclass:
case TOKinterface:
{
if (!id)
error(loc, "anonymous classes not allowed");
// Collect base class(es)
BaseClasses *baseclasses = NULL;
if (token.value == TOKcolon)
{
nextToken();
baseclasses = parseBaseClasses();
if (tpl)
{
Expression *tempCons = parseConstraint();
if (tempCons)
{
if (constraint)
error("members expected");
else
constraint = tempCons;
}
}
if (token.value != TOKlcurly)
error("members expected");
}
if (tok == TOKclass)
{
bool inObject = md && !md->packages && md->id == Id::object;
a = new ClassDeclaration(loc, id, baseclasses, NULL, inObject);
}
else
a = new InterfaceDeclaration(loc, id, baseclasses);
break;
}
case TOKstruct:
if (id)
{
bool inObject = md && !md->packages && md->id == Id::object;
a = new StructDeclaration(loc, id, inObject);
}
else
anon = 1;
break;
case TOKunion:
if (id)
a = new UnionDeclaration(loc, id);
else
anon = 2;
break;
default:
assert(0);
break;
}
if (a && token.value == TOKsemicolon)
{
nextToken();
}
else if (token.value == TOKlcurly)
{
const Loc lookingForElseSave = lookingForElse;
lookingForElse = Loc();
//printf("aggregate definition\n");
nextToken();
Dsymbols *decl = parseDeclDefs(0);
lookingForElse = lookingForElseSave;
if (token.value != TOKrcurly)
error("} expected following members in %s declaration at %s",
Token::toChars(tok), loc.toChars());
nextToken();
if (anon)
{
/* Anonymous structs/unions are more like attributes.
*/
return new AnonDeclaration(loc, anon == 2, decl);
}
else
a->members = decl;
}
else
{
error("{ } expected following %s declaration", Token::toChars(tok));
a = new StructDeclaration(loc, NULL, false);
}
if (tpl)
{
// Wrap a template around the aggregate declaration
Dsymbols *decldefs = new Dsymbols();
decldefs->push(a);
TemplateDeclaration *tempdecl =
new TemplateDeclaration(loc, id, tpl, constraint, decldefs);
return tempdecl;
}
return a;
}
/*******************************************
*/
BaseClasses *Parser::parseBaseClasses()
{
BaseClasses *baseclasses = new BaseClasses();
for (; 1; nextToken())
{
bool prot = false;
Prot protection = Prot(PROTpublic);
switch (token.value)
{
case TOKprivate:
prot = true;
protection = Prot(PROTprivate);
nextToken();
break;
case TOKpackage:
prot = true;
protection = Prot(PROTpackage);
nextToken();
break;
case TOKprotected:
prot = true;
protection = Prot(PROTprotected);
nextToken();
break;
case TOKpublic:
prot = true;
protection = Prot(PROTpublic);
nextToken();
break;
default: break;
}
if (prot)
error("use of base class protection is no longer supported");
BaseClass *b = new BaseClass(parseBasicType());
baseclasses->push(b);
if (token.value != TOKcomma)
break;
}
return baseclasses;
}
/**************************************
* Parse constraint.
* Constraint is of the form:
* if ( ConstraintExpression )
*/
Expression *Parser::parseConstraint()
{ Expression *e = NULL;
if (token.value == TOKif)
{
nextToken(); // skip over 'if'
check(TOKlparen);
e = parseExpression();
check(TOKrparen);
}
return e;
}
/**************************************
* Parse a TemplateDeclaration.
*/
TemplateDeclaration *Parser::parseTemplateDeclaration(bool ismixin)
{
TemplateDeclaration *tempdecl;
Identifier *id;
TemplateParameters *tpl;
Dsymbols *decldefs;
Expression *constraint = NULL;
Loc loc = token.loc;
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following template");
goto Lerr;
}
id = token.ident;
nextToken();
tpl = parseTemplateParameterList();
if (!tpl)
goto Lerr;
constraint = parseConstraint();
if (token.value != TOKlcurly)
{
error("members of template declaration expected");
goto Lerr;
}
else
decldefs = parseBlock(NULL);
tempdecl = new TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
return tempdecl;
Lerr:
return NULL;
}
/******************************************
* Parse template parameter list.
* Input:
* flag 0: parsing "( list )"
* 1: parsing non-empty "list )"
*/
TemplateParameters *Parser::parseTemplateParameterList(int flag)
{
TemplateParameters *tpl = new TemplateParameters();
if (!flag && token.value != TOKlparen)
{ error("parenthesized TemplateParameterList expected following TemplateIdentifier");
goto Lerr;
}
nextToken();
// Get array of TemplateParameters
if (flag || token.value != TOKrparen)
{
int isvariadic = 0;
while (token.value != TOKrparen)
{
TemplateParameter *tp;
Loc loc;
Identifier *tp_ident = NULL;
Type *tp_spectype = NULL;
Type *tp_valtype = NULL;
Type *tp_defaulttype = NULL;
Expression *tp_specvalue = NULL;
Expression *tp_defaultvalue = NULL;
Token *t;
// Get TemplateParameter
// First, look ahead to see if it is a TypeParameter or a ValueParameter
t = peek(&token);
if (token.value == TOKalias)
{ // AliasParameter
nextToken();
loc = token.loc; // todo
Type *spectype = NULL;
if (isDeclaration(&token, 2, TOKreserved, NULL))
{
spectype = parseType(&tp_ident);
}
else
{
if (token.value != TOKidentifier)
{
error("identifier expected for template alias parameter");
goto Lerr;
}
tp_ident = token.ident;
nextToken();
}
RootObject *spec = NULL;
if (token.value == TOKcolon) // : Type
{
nextToken();
if (isDeclaration(&token, 0, TOKreserved, NULL))
spec = parseType();
else
spec = parseCondExp();
}
RootObject *def = NULL;
if (token.value == TOKassign) // = Type
{
nextToken();
if (isDeclaration(&token, 0, TOKreserved, NULL))
def = parseType();
else
def = parseCondExp();
}
tp = new TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
}
else if (t->value == TOKcolon || t->value == TOKassign ||
t->value == TOKcomma || t->value == TOKrparen)
{
// TypeParameter
if (token.value != TOKidentifier)
{
error("identifier expected for template type parameter");
goto Lerr;
}
loc = token.loc;
tp_ident = token.ident;
nextToken();
if (token.value == TOKcolon) // : Type
{
nextToken();
tp_spectype = parseType();
}
if (token.value == TOKassign) // = Type
{
nextToken();
tp_defaulttype = parseType();
}
tp = new TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
}
else if (token.value == TOKidentifier && t->value == TOKdotdotdot)
{
// ident...
if (isvariadic)
error("variadic template parameter must be last");
isvariadic = 1;
loc = token.loc;
tp_ident = token.ident;
nextToken();
nextToken();
tp = new TemplateTupleParameter(loc, tp_ident);
}
else if (token.value == TOKthis)
{
// ThisParameter
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected for template this parameter");
goto Lerr;
}
loc = token.loc;
tp_ident = token.ident;
nextToken();
if (token.value == TOKcolon) // : Type
{
nextToken();
tp_spectype = parseType();
}
if (token.value == TOKassign) // = Type
{
nextToken();
tp_defaulttype = parseType();
}
tp = new TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
}
else
{
// ValueParameter
loc = token.loc; // todo
tp_valtype = parseType(&tp_ident);
if (!tp_ident)
{
error("identifier expected for template value parameter");
tp_ident = Identifier::idPool("error");
}
if (token.value == TOKcolon) // : CondExpression
{
nextToken();
tp_specvalue = parseCondExp();
}
if (token.value == TOKassign) // = CondExpression
{
nextToken();
tp_defaultvalue = parseDefaultInitExp();
}
tp = new TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
}
tpl->push(tp);
if (token.value != TOKcomma)
break;
nextToken();
}
}
check(TOKrparen);
Lerr:
return tpl;
}
/******************************************
* Parse template mixin.
* mixin Foo;
* mixin Foo!(args);
* mixin a.b.c!(args).Foo!(args);
* mixin Foo!(args) identifier;
* mixin typeof(expr).identifier!(args);
*/
Dsymbol *Parser::parseMixin()
{
TemplateMixin *tm;
Identifier *id;
Objects *tiargs;
//printf("parseMixin()\n");
Loc locMixin = token.loc;
nextToken(); // skip 'mixin'
Loc loc = token.loc;
TypeQualified *tqual = NULL;
if (token.value == TOKdot)
{
id = Id::empty;
}
else
{
if (token.value == TOKtypeof)
{
tqual = parseTypeof();
check(TOKdot);
}
if (token.value != TOKidentifier)
{
error("identifier expected, not %s", token.toChars());
id = Id::empty;
}
else
id = token.ident;
nextToken();
}
while (1)
{
tiargs = NULL;
if (token.value == TOKnot)
{
tiargs = parseTemplateArguments();
}
if (tiargs && token.value == TOKdot)
{
TemplateInstance *tempinst = new TemplateInstance(loc, id);
tempinst->tiargs = tiargs;
if (!tqual)
tqual = new TypeInstance(loc, tempinst);
else
tqual->addInst(tempinst);
tiargs = NULL;
}
else
{
if (!tqual)
tqual = new TypeIdentifier(loc, id);
else
tqual->addIdent(id);
}
if (token.value != TOKdot)
break;
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following '.' instead of '%s'", token.toChars());
break;
}
loc = token.loc;
id = token.ident;
nextToken();
}
if (token.value == TOKidentifier)
{
id = token.ident;
nextToken();
}
else
id = NULL;
tm = new TemplateMixin(locMixin, id, tqual, tiargs);
if (token.value != TOKsemicolon)
error("';' expected after mixin");
nextToken();
return tm;
}
/******************************************
* Parse template arguments.
* Input:
* current token is opening '!'
* Output:
* current token is one after closing ')'
*/
Objects *Parser::parseTemplateArguments()
{
Objects *tiargs;
nextToken();
if (token.value == TOKlparen)
{
// ident!(template_arguments)
tiargs = parseTemplateArgumentList();
}
else
{
// ident!template_argument
tiargs = parseTemplateSingleArgument();
}
if (token.value == TOKnot)
{
TOK tok = peekNext();
if (tok != TOKis && tok != TOKin)
{
error("multiple ! arguments are not allowed");
Lagain:
nextToken();
if (token.value == TOKlparen)
parseTemplateArgumentList();
else
parseTemplateSingleArgument();
if (token.value == TOKnot && (tok = peekNext()) != TOKis && tok != TOKin)
goto Lagain;
}
}
return tiargs;
}
/******************************************
* Parse template argument list.
* Input:
* current token is opening '(',
* or ',' for __traits
* Output:
* current token is one after closing ')'
*/
Objects *Parser::parseTemplateArgumentList()
{
//printf("Parser::parseTemplateArgumentList()\n");
Objects *tiargs = new Objects();
TOK endtok = TOKrparen;
assert(token.value == TOKlparen || token.value == TOKcomma);
nextToken();
// Get TemplateArgumentList
while (token.value != endtok)
{
// See if it is an Expression or a Type
if (isDeclaration(&token, 0, TOKreserved, NULL))
{ // Template argument is a type
Type *ta = parseType();
tiargs->push(ta);
}
else
{ // Template argument is an expression
Expression *ea = parseAssignExp();
tiargs->push(ea);
}
if (token.value != TOKcomma)
break;
nextToken();
}
check(endtok, "template argument list");
return tiargs;
}
/*****************************
* Parse single template argument, to support the syntax:
* foo!arg
* Input:
* current token is the arg
*/
Objects *Parser::parseTemplateSingleArgument()
{
//printf("parseTemplateSingleArgument()\n");
Objects *tiargs = new Objects();
Type *ta;
switch (token.value)
{
case TOKidentifier:
ta = new TypeIdentifier(token.loc, token.ident);
goto LabelX;
case TOKvector:
ta = parseVector();
goto LabelX;
case TOKvoid: ta = Type::tvoid; goto LabelX;
case TOKint8: ta = Type::tint8; goto LabelX;
case TOKuns8: ta = Type::tuns8; goto LabelX;
case TOKint16: ta = Type::tint16; goto LabelX;
case TOKuns16: ta = Type::tuns16; goto LabelX;
case TOKint32: ta = Type::tint32; goto LabelX;
case TOKuns32: ta = Type::tuns32; goto LabelX;
case TOKint64: ta = Type::tint64; goto LabelX;
case TOKuns64: ta = Type::tuns64; goto LabelX;
case TOKint128: ta = Type::tint128; goto LabelX;
case TOKuns128: ta = Type::tuns128; goto LabelX;
case TOKfloat32: ta = Type::tfloat32; goto LabelX;
case TOKfloat64: ta = Type::tfloat64; goto LabelX;
case TOKfloat80: ta = Type::tfloat80; goto LabelX;
case TOKimaginary32: ta = Type::timaginary32; goto LabelX;
case TOKimaginary64: ta = Type::timaginary64; goto LabelX;
case TOKimaginary80: ta = Type::timaginary80; goto LabelX;
case TOKcomplex32: ta = Type::tcomplex32; goto LabelX;
case TOKcomplex64: ta = Type::tcomplex64; goto LabelX;
case TOKcomplex80: ta = Type::tcomplex80; goto LabelX;
case TOKbool: ta = Type::tbool; goto LabelX;
case TOKchar: ta = Type::tchar; goto LabelX;
case TOKwchar: ta = Type::twchar; goto LabelX;
case TOKdchar: ta = Type::tdchar; goto LabelX;
LabelX:
tiargs->push(ta);
nextToken();
break;
case TOKint32v:
case TOKuns32v:
case TOKint64v:
case TOKuns64v:
case TOKint128v:
case TOKuns128v:
case TOKfloat32v:
case TOKfloat64v:
case TOKfloat80v:
case TOKimaginary32v:
case TOKimaginary64v:
case TOKimaginary80v:
case TOKnull:
case TOKtrue:
case TOKfalse:
case TOKcharv:
case TOKwcharv:
case TOKdcharv:
case TOKstring:
case TOKxstring:
case TOKfile:
case TOKfilefullpath:
case TOKline:
case TOKmodulestring:
case TOKfuncstring:
case TOKprettyfunc:
case TOKthis:
{ // Template argument is an expression
Expression *ea = parsePrimaryExp();
tiargs->push(ea);
break;
}
default:
error("template argument expected following !");
break;
}
return tiargs;
}
Dsymbols *Parser::parseImport()
{
Dsymbols *decldefs = new Dsymbols();
Identifier *aliasid = NULL;
int isstatic = token.value == TOKstatic;
if (isstatic)
nextToken();
//printf("Parser::parseImport()\n");
do
{
L1:
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following import");
break;
}
Loc loc = token.loc;
Identifier *id = token.ident;
Identifiers *a = NULL;
nextToken();
if (!aliasid && token.value == TOKassign)
{
aliasid = id;
goto L1;
}
while (token.value == TOKdot)
{
if (!a)
a = new Identifiers();
a->push(id);
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following package");
break;
}
id = token.ident;
nextToken();
}
Import *s = new Import(loc, a, id, aliasid, isstatic);
decldefs->push(s);
/* Look for
* : alias=name, alias=name;
* syntax.
*/
if (token.value == TOKcolon)
{
do
{
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following :");
break;
}
Identifier *alias = token.ident;
Identifier *name;
nextToken();
if (token.value == TOKassign)
{
nextToken();
if (token.value != TOKidentifier)
{
error("identifier expected following %s=", alias->toChars());
break;
}
name = token.ident;
nextToken();
}
else
{
name = alias;
alias = NULL;
}
s->addAlias(name, alias);
} while (token.value == TOKcomma);
break; // no comma-separated imports of this form
}
aliasid = NULL;
} while (token.value == TOKcomma);
if (token.value == TOKsemicolon)
nextToken();
else
{
error("';' expected");
nextToken();
}
return decldefs;
}
Type *Parser::parseType(Identifier **pident, TemplateParameters **ptpl)
{
/* Take care of the storage class prefixes that
* serve as type attributes:
* const type
* immutable type
* shared type
* inout type
* inout const type
* shared const type
* shared inout type
* shared inout const type
*/
StorageClass stc = 0;
while (1)
{
switch (token.value)
{
case TOKconst:
if (peekNext() == TOKlparen)
break; // const as type constructor
stc |= STCconst; // const as storage class
nextToken();
continue;
case TOKimmutable:
if (peekNext() == TOKlparen)
break;
stc |= STCimmutable;
nextToken();
continue;
case TOKshared:
if (peekNext() == TOKlparen)
break;
stc |= STCshared;
nextToken();
continue;
case TOKwild:
if (peekNext() == TOKlparen)
break;
stc |= STCwild;
nextToken();
continue;
default:
break;
}
break;
}
Loc typeLoc = token.loc;
Type *t;
t = parseBasicType();
int alt = 0;