D front-end changes: - Explicit package visibility attribute is now always applied to introducing scopes. - Added `__traits(totype, string)' to convert mangled type string to an existing type. - Printf-like and scanf-like functions are now detected by prefixing them with `pragma(printf)' for printf-like functions or `pragma(scanf)' for scanf-like functions. - Added `__c_wchar_t', `__c_complex_float', `__c_complex_double', and `__c_complex_real' types for interfacing with C and C++. - Template alias parameters can now be instantiated with basic types, such as `int` or `void function()`. - Mixins can now be used as types in the form `mixin(string) var'. - Mixin expressions can take an argument list, same as `pragma(msg)'. - Implement DIP1034, add `typeof(*null)' types to represent `noreturn'. - `pragma(msg)' can print expressions of type `void'. - It is now an error to use private variables selectively imported from other modules. Due to a bug, some imported private members were visible from other modules, violating the specification. - Added new syntax to declare an alias to a function type using the `alias' syntax based on the assignment operator. - Function literals can now return a value by reference. Phobos changes: - Synchronize C bindings with the latest port fixes in upstream druntime. - Added alias for a `noreturn' type in object.d - Make use of the new `pragma(printf)' and `pragma(scanf)' pragmas, fix all code that got flagged as being incorrect. - Fixed code that relied on bugs in the D import package system. Reviewed-on: https://github.com/dlang/dmd/pull/12339 https://github.com/dlang/druntime/pull/3422 https://github.com/dlang/phobos/pull/7932 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 3b808e838. * Make-lang.in (D_FRONTEND_OBJS): Add d/chkformat.o. * d-codegen.cc (build_struct_literal): Handle special enums. * d-convert.cc (convert_expr): Handle noreturn type. (convert_for_condition): Likewise. * d-target.cc (Target::_init): Set type for wchar_t. (TargetCPP::derivedClassOffset): New method. (Target::libraryObjectMonitors): New method. * decl.cc (get_symbol_decl): Set TREE_THIS_VOLATILE for functions of type noreturn. * toir.cc (IRVisitor::visit (ReturnStatement *)): Handle returning noreturn types. * types.cc (TypeVisitor::visit (TypeNoreturn *)): New method. (TypeVisitor::visit (TypeEnum *)): Handle special enums. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 483bc129. * libdruntime/Makefile.am (DRUNTIME_DSOURCES_DARWIN): Add core/sys/darwin/fcntl.d. (DRUNTIME_DSOURCES_OPENBSD): Add core/sys/openbsd/unistd.d. (DRUNTIME_DSOURCES_WINDOWS): Add core/sys/windows/stdc/malloc.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos f89dc217a. * src/Makefile.am (PHOBOS_DSOURCES): Add std/regex/internal/tests2.d. * src/Makefile.in: Regenerate. * testsuite/libphobos.exceptions/chain.d: Fix format arguments. * testsuite/libphobos.exceptions/line_trace.d: Likewise.
8493 lines
257 KiB
C
8493 lines
257 KiB
C
|
|
/* Compiler implementation of the D programming language
|
|
* Copyright (C) 1999-2021 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();
|
|
}
|
|
|
|
static StorageClass parseDeprecatedAttribute(Parser *p, Expression **msg)
|
|
{
|
|
if (p->peekNext() != TOKlparen)
|
|
return STCdeprecated;
|
|
|
|
p->nextToken();
|
|
p->check(TOKlparen);
|
|
Expression *e = p->parseAssignExp();
|
|
p->check(TOKrparen);
|
|
if (*msg)
|
|
{
|
|
p->error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`",
|
|
(*msg)->toChars(), e->toChars());
|
|
}
|
|
*msg = e;
|
|
return STCundefined;
|
|
}
|
|
|
|
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(Prot::undefined),
|
|
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;
|
|
}
|
|
Prot::Kind 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();
|
|
Expressions *exps = parseArguments();
|
|
check(TOKsemicolon);
|
|
s = new CompileDeclaration(loc, exps);
|
|
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:
|
|
case TOKtraits:
|
|
Ldeclaration:
|
|
a = parseDeclarations(false, pAttrs, pAttrs->comment);
|
|
if (a && a->length)
|
|
*pLastDecl = (*a)[a->length-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 || t->value == TOKlcurly)
|
|
{
|
|
// invariant { statements... }
|
|
// invariant() { statements... }
|
|
// invariant (expression);
|
|
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 if (next == TOKforeach || next == TOKforeach_reverse)
|
|
{
|
|
s = parseForeachStaticDecl(token.loc, 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->length)
|
|
*pLastDecl = (*a)[a->length-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->length)
|
|
*pLastDecl = (*a)[a->length-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 (StorageClass _stc = parseDeprecatedAttribute(this, &pAttrs->depmsg))
|
|
{
|
|
stc = _stc;
|
|
goto Lstc;
|
|
}
|
|
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->length);
|
|
for (size_t i = idents->length; 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 = Prot::private_; goto Lprot;
|
|
case TOKpackage: prot = Prot::package_; goto Lprot;
|
|
case TOKprotected: prot = Prot::protected_; goto Lprot;
|
|
case TOKpublic: prot = Prot::public_; goto Lprot;
|
|
case TOKexport: prot = Prot::export_; goto Lprot;
|
|
Lprot:
|
|
{
|
|
if (pAttrs->protection.kind != Prot::undefined)
|
|
{
|
|
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 == Prot::package_ && 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 != Prot::undefined)
|
|
{
|
|
if (pAttrs->protection.kind == Prot::package_ && pkg_prot_idents)
|
|
s = new ProtDeclaration(attrloc, pkg_prot_idents, a);
|
|
else
|
|
s = new ProtDeclaration(attrloc, pAttrs->protection, a);
|
|
|
|
pAttrs->protection = Prot(Prot::undefined);
|
|
}
|
|
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.peekChars();
|
|
}
|
|
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->length)
|
|
{
|
|
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.peekChars());
|
|
else
|
|
error("redundant attribute `%s`", buf.peekChars());
|
|
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::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 identifier 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, 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
|
|
*/
|
|
VarArg varargs;
|
|
Parameters *parameters = parseParameters(&varargs);
|
|
stc = parsePostfix(stc, &udas);
|
|
if (varargs != VARARGnone || 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(ParameterList(parameters, varargs),
|
|
NULL, linkage, stc); // ReturnType -> 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.peekChars());
|
|
}
|
|
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.peekChars());
|
|
}
|
|
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.peekChars());
|
|
}
|
|
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.peekChars());
|
|
}
|
|
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 { statements... }
|
|
* invariant() { statements... }
|
|
* invariant (expression);
|
|
* 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 () or invariant (expression);
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKrparen) // invariant (expression);
|
|
{
|
|
Expression *e = parseAssignExp();
|
|
Expression *msg = NULL;
|
|
if (token.value == TOKcomma)
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKrparen)
|
|
{
|
|
msg = parseAssignExp();
|
|
if (token.value == TOKcomma)
|
|
nextToken();
|
|
}
|
|
}
|
|
check(TOKrparen);
|
|
check(TOKsemicolon);
|
|
e = new AssertExp(loc, e, msg);
|
|
ExpStatement *fbody = new ExpStatement(loc, e);
|
|
InvariantDeclaration *f = new InvariantDeclaration(loc, token.loc, stc);
|
|
f->fbody = fbody;
|
|
return f;
|
|
}
|
|
else
|
|
{
|
|
nextToken();
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
VarArg 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();
|
|
|
|
VarArg varargs;
|
|
Parameters *parameters = parseParameters(&varargs);
|
|
if (varargs != VARARGnone)
|
|
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(VarArg *pvarargs, TemplateParameters **tpl)
|
|
{
|
|
Parameters *parameters = new Parameters();
|
|
VarArg varargs = VARARGnone;
|
|
int hasdefault = 0;
|
|
|
|
check(TOKlparen);
|
|
while (1)
|
|
{
|
|
Identifier *ai = NULL;
|
|
Type *at;
|
|
StorageClass storageClass = 0;
|
|
StorageClass stc;
|
|
Expression *ae;
|
|
Expressions *udas = NULL;
|
|
|
|
for (;1; nextToken())
|
|
{
|
|
L3:
|
|
switch (token.value)
|
|
{
|
|
case TOKrparen:
|
|
break;
|
|
|
|
case TOKdotdotdot:
|
|
varargs = VARARGvariadic;
|
|
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 TOKat:
|
|
{
|
|
Expressions *exps = NULL;
|
|
StorageClass stc2 = parseAttribute(&exps);
|
|
if (stc2 == STCproperty || stc2 == STCnogc ||
|
|
stc2 == STCdisable || stc2 == STCsafe ||
|
|
stc2 == STCtrusted || stc2 == STCsystem)
|
|
{
|
|
error("`@%s` attribute for function parameter is not supported", token.toChars());
|
|
}
|
|
else
|
|
{
|
|
udas = UserAttributeDeclaration::concat(udas, exps);
|
|
}
|
|
if (token.value == TOKdotdotdot)
|
|
error("variadic parameter cannot have user-defined attributes");
|
|
if (stc2)
|
|
nextToken();
|
|
goto L3;
|
|
// Don't call nextToken again.
|
|
}
|
|
|
|
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());
|
|
}
|
|
Parameter *param = new Parameter(storageClass, at, ai, ae, NULL);
|
|
if (udas)
|
|
{
|
|
Dsymbols *a = new Dsymbols();
|
|
UserAttributeDeclaration *udad = new UserAttributeDeclaration(udas, a);
|
|
param->userAttribDecl = udad;
|
|
}
|
|
if (token.value == TOKat)
|
|
{
|
|
Expressions *exps = NULL;
|
|
StorageClass stc2 = parseAttribute(&exps);
|
|
if (stc2 == STCproperty || stc2 == STCnogc ||
|
|
stc2 == STCdisable || stc2 == STCsafe ||
|
|
stc2 == STCtrusted || stc2 == STCsystem)
|
|
{
|
|
error("`@%s` attribute for function parameter is not supported", token.toChars());
|
|
}
|
|
else
|
|
{
|
|
error("user-defined attributes cannot appear as postfixes", token.toChars());
|
|
}
|
|
if (stc2)
|
|
nextToken();
|
|
}
|
|
if (token.value == TOKdotdotdot)
|
|
{ /* This is:
|
|
* at ai ...
|
|
*/
|
|
|
|
if (storageClass & (STCout | STCref))
|
|
error("variadic argument cannot be out or ref");
|
|
varargs = VARARGtypesafe;
|
|
parameters->push(param);
|
|
nextToken();
|
|
break;
|
|
}
|
|
parameters->push(param);
|
|
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)
|
|
{
|
|
bool isAnonymousEnum = !id;
|
|
|
|
//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
|
|
* ... prefixed by valid attributes
|
|
*/
|
|
loc = token.loc;
|
|
|
|
Type *type = NULL;
|
|
Identifier *ident = NULL;
|
|
|
|
Expressions *udas = NULL;
|
|
StorageClass stc = STCundefined;
|
|
Expression *deprecationMessage = NULL;
|
|
|
|
while (token.value != TOKrcurly &&
|
|
token.value != TOKcomma &&
|
|
token.value != TOKassign)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKat:
|
|
if (StorageClass _stc = parseAttribute(&udas))
|
|
{
|
|
if (_stc == STCdisable)
|
|
stc |= _stc;
|
|
else
|
|
{
|
|
OutBuffer buf;
|
|
stcToBuffer(&buf, _stc);
|
|
error("`%s` is not a valid attribute for enum members", buf.peekChars());
|
|
}
|
|
nextToken();
|
|
}
|
|
break;
|
|
case TOKdeprecated:
|
|
if (StorageClass _stc = parseDeprecatedAttribute(this, &deprecationMessage))
|
|
{
|
|
stc |= _stc;
|
|
nextToken();
|
|
}
|
|
break;
|
|
case TOKidentifier:
|
|
{
|
|
Token *tp = peek(&token);
|
|
if (tp->value == TOKassign || tp->value == TOKcomma || tp->value == TOKrcurly)
|
|
{
|
|
ident = token.ident;
|
|
type = NULL;
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
goto Ldefault;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
Ldefault:
|
|
if (isAnonymousEnum)
|
|
{
|
|
type = parseType(&ident, NULL);
|
|
if (type == Type::terror)
|
|
{
|
|
type = NULL;
|
|
nextToken();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("`%s` is not a valid attribute for enum members", token.toChars());
|
|
nextToken();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type && type != Type::terror)
|
|
{
|
|
if (!ident)
|
|
error("no identifier for declarator %s", type->toChars());
|
|
if (!isAnonymousEnum)
|
|
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 && type != Type::terror && isAnonymousEnum)
|
|
error("if type, there must be an initializer");
|
|
}
|
|
|
|
UserAttributeDeclaration *uad = NULL;
|
|
if (udas)
|
|
uad = new UserAttributeDeclaration(udas, NULL);
|
|
|
|
DeprecatedDeclaration *dd = NULL;
|
|
if (deprecationMessage)
|
|
{
|
|
dd = new DeprecatedDeclaration(deprecationMessage, NULL);
|
|
stc |= STCdeprecated;
|
|
}
|
|
|
|
EnumMember *em = new EnumMember(loc, ident, value, type, stc, uad, dd);
|
|
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(Prot::public_);
|
|
switch (token.value)
|
|
{
|
|
case TOKprivate:
|
|
prot = true;
|
|
protection = Prot(Prot::private_);
|
|
nextToken();
|
|
break;
|
|
case TOKpackage:
|
|
prot = true;
|
|
protection = Prot(Prot::package_);
|
|
nextToken();
|
|
break;
|
|
case TOKprotected:
|
|
prot = true;
|
|
protection = Prot(Prot::protected_);
|
|
nextToken();
|
|
break;
|
|
case TOKpublic:
|
|
prot = true;
|
|
protection = Prot(Prot::public_);
|
|
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 a Type or an Expression
|
|
* Returns:
|
|
* RootObject representing the AST
|
|
*/
|
|
RootObject *Parser::parseTypeOrAssignExp(TOK endtoken)
|
|
{
|
|
return isDeclaration(&token, 0, endtoken, NULL)
|
|
? (RootObject *)parseType() // argument is a type
|
|
: (RootObject *)parseAssignExp(); // argument is an expression
|
|
}
|
|
|
|
/******************************************
|
|
* 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)
|
|
{
|
|
tiargs->push(parseTypeOrAssignExp());
|
|
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;
|
|
t = parseDeclarator(t, &alt, pident, ptpl);
|
|
checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : NULL);
|
|
|
|
t = t->addSTC(stc);
|
|
return t;
|
|
}
|
|
|
|
Type *Parser::parseBasicType(bool dontLookDotIdents)
|
|
{
|
|
Type *t;
|
|
Loc loc;
|
|
Identifier *id;
|
|
|
|
//printf("parseBasicType()\n");
|
|
switch (token.value)
|
|
{
|
|
case TOKvoid: t = Type::tvoid; goto LabelX;
|
|
case TOKint8: t = Type::tint8; goto LabelX;
|
|
case TOKuns8: t = Type::tuns8; goto LabelX;
|
|
case TOKint16: t = Type::tint16; goto LabelX;
|
|
case TOKuns16: t = Type::tuns16; goto LabelX;
|
|
case TOKint32: t = Type::tint32; goto LabelX;
|
|
case TOKuns32: t = Type::tuns32; goto LabelX;
|
|
case TOKint64:
|
|
t = Type::tint64;
|
|
nextToken();
|
|
if (token.value == TOKint64) // if `long long`
|
|
{
|
|
error("use `long` for a 64 bit integer instead of `long long`");
|
|
nextToken();
|
|
}
|
|
else if (token.value == TOKfloat64) // if `long double`
|
|
{
|
|
error("use `real` instead of `long double`");
|
|
t = Type::tfloat80;
|
|
nextToken();
|
|
|
|
}
|
|
break;
|
|
|
|
case TOKuns64: t = Type::tuns64; goto LabelX;
|
|
case TOKint128: t = Type::tint128; goto LabelX;
|
|
case TOKuns128: t = Type::tuns128; goto LabelX;
|
|
case TOKfloat32: t = Type::tfloat32; goto LabelX;
|
|
case TOKfloat64: t = Type::tfloat64; goto LabelX;
|
|
case TOKfloat80: t = Type::tfloat80; goto LabelX;
|
|
case TOKimaginary32: t = Type::timaginary32; goto LabelX;
|
|
case TOKimaginary64: t = Type::timaginary64; goto LabelX;
|
|
case TOKimaginary80: t = Type::timaginary80; goto LabelX;
|
|
case TOKcomplex32: t = Type::tcomplex32; goto LabelX;
|
|
case TOKcomplex64: t = Type::tcomplex64; goto LabelX;
|
|
case TOKcomplex80: t = Type::tcomplex80; goto LabelX;
|
|
case TOKbool: t = Type::tbool; goto LabelX;
|
|
case TOKchar: t = Type::tchar; goto LabelX;
|
|
case TOKwchar: t = Type::twchar; goto LabelX;
|
|
case TOKdchar: t = Type::tdchar; goto LabelX;
|
|
LabelX:
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKthis:
|
|
case TOKsuper:
|
|
case TOKidentifier:
|
|
loc = token.loc;
|
|
id = token.ident;
|
|
nextToken();
|
|
if (token.value == TOKnot)
|
|
{
|
|
// ident!(template_arguments)
|
|
TemplateInstance *tempinst = new TemplateInstance(loc, id);
|
|
tempinst->tiargs = parseTemplateArguments();
|
|
t = parseBasicTypeStartingAt(new TypeInstance(loc, tempinst), dontLookDotIdents);
|
|
}
|
|
else
|
|
{
|
|
t = parseBasicTypeStartingAt(new TypeIdentifier(loc, id), dontLookDotIdents);
|
|
}
|
|
break;
|
|
|
|
case TOKmixin:
|
|
// https://dlang.org/spec/expression.html#mixin_types
|
|
loc = token.loc;
|
|
nextToken();
|
|
if (token.value != TOKlparen)
|
|
error("found `%s` when expecting `%s` following %s", token.toChars(), Token::toChars(TOKlparen), "`mixin`");
|
|
t = new TypeMixin(loc, parseArguments());
|
|
break;
|
|
|
|
case TOKdot:
|
|
// Leading . as in .foo
|
|
t = parseBasicTypeStartingAt(new TypeIdentifier(token.loc, Id::empty), dontLookDotIdents);
|
|
break;
|
|
|
|
case TOKtypeof:
|
|
// typeof(expression)
|
|
t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
|
|
break;
|
|
|
|
case TOKvector:
|
|
t = parseVector();
|
|
break;
|
|
|
|
case TOKtraits:
|
|
if (TraitsExp *te = (TraitsExp *) parsePrimaryExp())
|
|
{
|
|
if (te->ident && te->args)
|
|
{
|
|
t = new TypeTraits(token.loc, te);
|
|
break;
|
|
}
|
|
}
|
|
t = new TypeError();
|
|
break;
|
|
|
|
case TOKconst:
|
|
// const(type)
|
|
nextToken();
|
|
check(TOKlparen);
|
|
t = parseType()->addSTC(STCconst);
|
|
check(TOKrparen);
|
|
break;
|
|
|
|
case TOKimmutable:
|
|
// immutable(type)
|
|
nextToken();
|
|
check(TOKlparen);
|
|
t = parseType()->addSTC(STCimmutable);
|
|
check(TOKrparen);
|
|
break;
|
|
|
|
case TOKshared:
|
|
// shared(type)
|
|
nextToken();
|
|
check(TOKlparen);
|
|
t = parseType()->addSTC(STCshared);
|
|
check(TOKrparen);
|
|
break;
|
|
|
|
case TOKwild:
|
|
// wild(type)
|
|
nextToken();
|
|
check(TOKlparen);
|
|
t = parseType()->addSTC(STCwild);
|
|
check(TOKrparen);
|
|
break;
|
|
|
|
default:
|
|
error("basic type expected, not %s", token.toChars());
|
|
t = Type::terror;
|
|
break;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
Type *Parser::parseBasicTypeStartingAt(TypeQualified *tid, bool dontLookDotIdents)
|
|
{
|
|
Type *maybeArray = NULL;
|
|
// See https://issues.dlang.org/show_bug.cgi?id=1215
|
|
// A basic type can look like MyType (typical case), but also:
|
|
// MyType.T -> A type
|
|
// MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
|
|
// MyType[expr].T -> A type.
|
|
// MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
|
|
// (iif MyType[expr].T is a Ttuple)
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKdot:
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKidentifier)
|
|
{
|
|
error("identifier expected following `.` instead of `%s`", token.toChars());
|
|
break;
|
|
}
|
|
if (maybeArray)
|
|
{
|
|
// This is actually a TypeTuple index, not an {a/s}array.
|
|
// We need to have a while loop to unwind all index taking:
|
|
// T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
|
|
Objects dimStack;
|
|
Type *t = maybeArray;
|
|
while (true)
|
|
{
|
|
if (t->ty == Tsarray)
|
|
{
|
|
// The index expression is an Expression.
|
|
TypeSArray *a = (TypeSArray *)t;
|
|
dimStack.push(a->dim->syntaxCopy());
|
|
t = a->next->syntaxCopy();
|
|
}
|
|
else if (t->ty == Taarray)
|
|
{
|
|
// The index expression is a Type. It will be interpreted as an expression at semantic time.
|
|
TypeAArray *a = (TypeAArray *)t;
|
|
dimStack.push(a->index->syntaxCopy());
|
|
t = a->next->syntaxCopy();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
assert(dimStack.length > 0);
|
|
// We're good. Replay indices in the reverse order.
|
|
tid = (TypeQualified *)t;
|
|
while (dimStack.length)
|
|
{
|
|
tid->addIndex(dimStack.pop());
|
|
}
|
|
maybeArray = NULL;
|
|
}
|
|
Loc loc = token.loc;
|
|
Identifier *id = token.ident;
|
|
nextToken();
|
|
if (token.value == TOKnot)
|
|
{
|
|
TemplateInstance *tempinst = new TemplateInstance(loc, id);
|
|
tempinst->tiargs = parseTemplateArguments();
|
|
tid->addInst(tempinst);
|
|
}
|
|
else
|
|
tid->addIdent(id);
|
|
continue;
|
|
}
|
|
case TOKlbracket:
|
|
{
|
|
if (dontLookDotIdents) // workaround for Bugzilla 14911
|
|
goto Lend;
|
|
|
|
nextToken();
|
|
Type *t = maybeArray ? maybeArray : (Type *)tid;
|
|
if (token.value == TOKrbracket)
|
|
{
|
|
// It's a dynamic array, and we're done:
|
|
// T[].U does not make sense.
|
|
t = new TypeDArray(t);
|
|
nextToken();
|
|
return t;
|
|
}
|
|
else if (isDeclaration(&token, 0, TOKrbracket, NULL))
|
|
{
|
|
// This can be one of two things:
|
|
// 1 - an associative array declaration, T[type]
|
|
// 2 - an associative array declaration, T[expr]
|
|
// These can only be disambiguated later.
|
|
Type *index = parseType(); // [ type ]
|
|
maybeArray = new TypeAArray(t, index);
|
|
check(TOKrbracket);
|
|
}
|
|
else
|
|
{
|
|
// This can be one of three things:
|
|
// 1 - an static array declaration, T[expr]
|
|
// 2 - a slice, T[expr .. expr]
|
|
// 3 - a template parameter pack index expression, T[expr].U
|
|
// 1 and 3 can only be disambiguated later.
|
|
//printf("it's type[expression]\n");
|
|
inBrackets++;
|
|
Expression *e = parseAssignExp(); // [ expression ]
|
|
if (token.value == TOKslice)
|
|
{
|
|
// It's a slice, and we're done.
|
|
nextToken();
|
|
Expression *e2 = parseAssignExp(); // [ exp .. exp ]
|
|
t = new TypeSlice(t, e, e2);
|
|
inBrackets--;
|
|
check(TOKrbracket);
|
|
return t;
|
|
}
|
|
else
|
|
{
|
|
maybeArray = new TypeSArray(t, e);
|
|
inBrackets--;
|
|
check(TOKrbracket);
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
goto Lend;
|
|
}
|
|
}
|
|
Lend:
|
|
return maybeArray ? maybeArray : (Type *)tid;
|
|
}
|
|
|
|
/******************************************
|
|
* Parse things that follow the initial type t.
|
|
* t *
|
|
* t []
|
|
* t [type]
|
|
* t [expression]
|
|
* t [expression .. expression]
|
|
* t function
|
|
* t delegate
|
|
*/
|
|
|
|
Type *Parser::parseBasicType2(Type *t)
|
|
{
|
|
//printf("parseBasicType2()\n");
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKmul:
|
|
t = new TypePointer(t);
|
|
nextToken();
|
|
continue;
|
|
|
|
case TOKlbracket:
|
|
// Handle []. Make sure things like
|
|
// int[3][1] a;
|
|
// is (array[1] of array[3] of int)
|
|
nextToken();
|
|
if (token.value == TOKrbracket)
|
|
{
|
|
t = new TypeDArray(t); // []
|
|
nextToken();
|
|
}
|
|
else if (isDeclaration(&token, 0, TOKrbracket, NULL))
|
|
{
|
|
// It's an associative array declaration
|
|
//printf("it's an associative array\n");
|
|
Type *index = parseType(); // [ type ]
|
|
t = new TypeAArray(t, index);
|
|
check(TOKrbracket);
|
|
}
|
|
else
|
|
{
|
|
//printf("it's type[expression]\n");
|
|
inBrackets++;
|
|
Expression *e = parseAssignExp(); // [ expression ]
|
|
if (token.value == TOKslice)
|
|
{
|
|
nextToken();
|
|
Expression *e2 = parseAssignExp(); // [ exp .. exp ]
|
|
t = new TypeSlice(t, e, e2);
|
|
}
|
|
else
|
|
{
|
|
t = new TypeSArray(t,e);
|
|
}
|
|
inBrackets--;
|
|
check(TOKrbracket);
|
|
}
|
|
continue;
|
|
|
|
case TOKdelegate:
|
|
case TOKfunction:
|
|
{
|
|
// Handle delegate declaration:
|
|
// t delegate(parameter list) nothrow pure
|
|
// t function(parameter list) nothrow pure
|
|
TOK save = token.value;
|
|
nextToken();
|
|
|
|
VarArg varargs;
|
|
Parameters *parameters = parseParameters(&varargs);
|
|
|
|
StorageClass stc = parsePostfix(STCundefined, NULL);
|
|
TypeFunction *tf = new TypeFunction(ParameterList(parameters, varargs),
|
|
t, linkage, stc);
|
|
if (stc & (STCconst | STCimmutable | STCshared | STCwild | STCreturn))
|
|
{
|
|
if (save == TOKfunction)
|
|
error("const/immutable/shared/inout/return attributes are only valid for non-static member functions");
|
|
else
|
|
tf = (TypeFunction *)tf->addSTC(stc);
|
|
}
|
|
|
|
if (save == TOKdelegate)
|
|
t = new TypeDelegate(tf);
|
|
else
|
|
t = new TypePointer(tf); // pointer to function
|
|
continue;
|
|
}
|
|
|
|
default:
|
|
return t;
|
|
}
|
|
assert(0);
|
|
}
|
|
assert(0);
|
|
return NULL;
|
|
}
|
|
|
|
Type *Parser::parseDeclarator(Type *t, int *palt, Identifier **pident,
|
|
TemplateParameters **tpl, StorageClass storageClass, int *pdisable, Expressions **pudas)
|
|
{
|
|
//printf("parseDeclarator(tpl = %p)\n", tpl);
|
|
t = parseBasicType2(t);
|
|
|
|
Type *ts;
|
|
switch (token.value)
|
|
{
|
|
case TOKidentifier:
|
|
if (pident)
|
|
*pident = token.ident;
|
|
else
|
|
error("unexpected identifier `%s` in declarator", token.ident->toChars());
|
|
ts = t;
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKlparen:
|
|
{
|
|
// like: T (*fp)();
|
|
// like: T ((*fp))();
|
|
if (peekNext() == TOKmul ||
|
|
peekNext() == TOKlparen)
|
|
{
|
|
/* Parse things with parentheses around the identifier, like:
|
|
* int (*ident[3])[]
|
|
* although the D style would be:
|
|
* int[]*[3] ident
|
|
*/
|
|
*palt |= 1;
|
|
nextToken();
|
|
ts = parseDeclarator(t, palt, pident);
|
|
check(TOKrparen);
|
|
break;
|
|
}
|
|
ts = t;
|
|
|
|
Token *peekt = &token;
|
|
/* Completely disallow C-style things like:
|
|
* T (a);
|
|
* Improve error messages for the common bug of a missing return type
|
|
* by looking to see if (a) looks like a parameter list.
|
|
*/
|
|
if (isParameters(&peekt))
|
|
{
|
|
error("function declaration without return type. (Note that constructors are always named `this`)");
|
|
}
|
|
else
|
|
error("unexpected ( in declarator");
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ts = t;
|
|
break;
|
|
}
|
|
|
|
// parse DeclaratorSuffixes
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
#if CARRAYDECL
|
|
/* Support C style array syntax:
|
|
* int ident[]
|
|
* as opposed to D-style:
|
|
* int[] ident
|
|
*/
|
|
case TOKlbracket:
|
|
{
|
|
// This is the old C-style post [] syntax.
|
|
TypeNext *ta;
|
|
nextToken();
|
|
if (token.value == TOKrbracket)
|
|
{
|
|
// It's a dynamic array
|
|
ta = new TypeDArray(t); // []
|
|
nextToken();
|
|
*palt |= 2;
|
|
}
|
|
else if (isDeclaration(&token, 0, TOKrbracket, NULL))
|
|
{
|
|
// It's an associative array
|
|
//printf("it's an associative array\n");
|
|
Type *index = parseType(); // [ type ]
|
|
check(TOKrbracket);
|
|
ta = new TypeAArray(t, index);
|
|
*palt |= 2;
|
|
}
|
|
else
|
|
{
|
|
//printf("It's a static array\n");
|
|
Expression *e = parseAssignExp(); // [ expression ]
|
|
ta = new TypeSArray(t, e);
|
|
check(TOKrbracket);
|
|
*palt |= 2;
|
|
}
|
|
|
|
/* Insert ta into
|
|
* ts -> ... -> t
|
|
* so that
|
|
* ts -> ... -> ta -> t
|
|
*/
|
|
Type **pt;
|
|
for (pt = &ts; *pt != t; pt = &((TypeNext *)*pt)->next)
|
|
;
|
|
*pt = ta;
|
|
continue;
|
|
}
|
|
#endif
|
|
case TOKlparen:
|
|
{
|
|
if (tpl)
|
|
{
|
|
Token *tk = peekPastParen(&token);
|
|
if (tk->value == TOKlparen)
|
|
{
|
|
/* Look ahead to see if this is (...)(...),
|
|
* i.e. a function template declaration
|
|
*/
|
|
//printf("function template declaration\n");
|
|
|
|
// Gather template parameter list
|
|
*tpl = parseTemplateParameterList();
|
|
}
|
|
else if (tk->value == TOKassign)
|
|
{
|
|
/* or (...) =,
|
|
* i.e. a variable template declaration
|
|
*/
|
|
//printf("variable template declaration\n");
|
|
*tpl = parseTemplateParameterList();
|
|
break;
|
|
}
|
|
}
|
|
|
|
VarArg varargs;
|
|
Parameters *parameters = parseParameters(&varargs);
|
|
|
|
/* Parse const/immutable/shared/inout/nothrow/pure/return postfix
|
|
*/
|
|
StorageClass stc = parsePostfix(storageClass, pudas);
|
|
// merge prefix storage classes
|
|
Type *tf = new TypeFunction(ParameterList(parameters, varargs),
|
|
t, linkage, stc);
|
|
tf = tf->addSTC(stc);
|
|
if (pdisable)
|
|
*pdisable = stc & STCdisable ? 1 : 0;
|
|
|
|
/* Insert tf into
|
|
* ts -> ... -> t
|
|
* so that
|
|
* ts -> ... -> tf -> t
|
|
*/
|
|
Type **pt;
|
|
for (pt = &ts; *pt != t; pt = &((TypeNext *)*pt)->next)
|
|
;
|
|
*pt = tf;
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ts;
|
|
}
|
|
|
|
void Parser::parseStorageClasses(StorageClass &storage_class, LINK &link,
|
|
bool &setAlignment, Expression *&ealign, Expressions *&udas)
|
|
{
|
|
StorageClass stc;
|
|
bool sawLinkage = false; // seen a linkage declaration
|
|
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKconst:
|
|
if (peek(&token)->value == TOKlparen)
|
|
break; // const as type constructor
|
|
stc = STCconst; // const as storage class
|
|
goto L1;
|
|
|
|
case TOKimmutable:
|
|
if (peek(&token)->value == TOKlparen)
|
|
break;
|
|
stc = STCimmutable;
|
|
goto L1;
|
|
|
|
case TOKshared:
|
|
if (peek(&token)->value == TOKlparen)
|
|
break;
|
|
stc = STCshared;
|
|
goto L1;
|
|
|
|
case TOKwild:
|
|
if (peek(&token)->value == TOKlparen)
|
|
break;
|
|
stc = STCwild;
|
|
goto L1;
|
|
|
|
case TOKstatic: stc = STCstatic; goto L1;
|
|
case TOKfinal: stc = STCfinal; goto L1;
|
|
case TOKauto: stc = STCauto; goto L1;
|
|
case TOKscope: stc = STCscope; goto L1;
|
|
case TOKoverride: stc = STCoverride; goto L1;
|
|
case TOKabstract: stc = STCabstract; goto L1;
|
|
case TOKsynchronized: stc = STCsynchronized; goto L1;
|
|
case TOKdeprecated: stc = STCdeprecated; goto L1;
|
|
case TOKnothrow: stc = STCnothrow; goto L1;
|
|
case TOKpure: stc = STCpure; goto L1;
|
|
case TOKref: stc = STCref; goto L1;
|
|
case TOKgshared: stc = STCgshared; goto L1;
|
|
case TOKenum: stc = STCmanifest; goto L1;
|
|
case TOKat:
|
|
{
|
|
stc = parseAttribute(&udas);
|
|
if (stc)
|
|
goto L1;
|
|
continue;
|
|
}
|
|
L1:
|
|
storage_class = appendStorageClass(storage_class, stc);
|
|
nextToken();
|
|
continue;
|
|
|
|
case TOKextern:
|
|
{
|
|
if (peek(&token)->value != TOKlparen)
|
|
{
|
|
stc = STCextern;
|
|
goto L1;
|
|
}
|
|
|
|
if (sawLinkage)
|
|
error("redundant linkage declaration");
|
|
sawLinkage = true;
|
|
Identifiers *idents = NULL;
|
|
CPPMANGLE cppmangle = CPPMANGLEdefault;
|
|
bool cppMangleOnly = false;
|
|
link = parseLinkage(&idents, &cppmangle, &cppMangleOnly);
|
|
if (idents)
|
|
{
|
|
error("C++ name spaces not allowed here");
|
|
delete idents;
|
|
}
|
|
if (cppmangle != CPPMANGLEdefault)
|
|
{
|
|
error("C++ mangle declaration not allowed here");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case TOKalign:
|
|
{
|
|
nextToken();
|
|
setAlignment = true;
|
|
if (token.value == TOKlparen)
|
|
{
|
|
nextToken();
|
|
ealign = parseExpression();
|
|
check(TOKrparen);
|
|
}
|
|
continue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void parseAttributes(Parser *p, bool &hasParsedAttributes,
|
|
StorageClass &storage_class, LINK &link, bool &setAlignment,
|
|
Expression *&ealign, Expressions *&udas)
|
|
{
|
|
if (hasParsedAttributes) // only parse once
|
|
return;
|
|
hasParsedAttributes = true;
|
|
udas = NULL;
|
|
storage_class = STCundefined;
|
|
link = p->linkage;
|
|
setAlignment = false;
|
|
ealign = NULL;
|
|
p->parseStorageClasses(storage_class, link, setAlignment, ealign, udas);
|
|
}
|
|
|
|
/**********************************
|
|
* Parse Declarations.
|
|
* These can be:
|
|
* 1. declarations at global/class level
|
|
* 2. declarations at statement level
|
|
* Return array of Declaration *'s.
|
|
*/
|
|
|
|
Dsymbols *Parser::parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, const utf8_t *comment)
|
|
{
|
|
StorageClass storage_class = STCundefined;
|
|
Type *ts;
|
|
Type *t;
|
|
Type *tfirst;
|
|
Identifier *ident;
|
|
TOK tok = TOKreserved;
|
|
LINK link = linkage;
|
|
bool setAlignment = false;
|
|
Expression *ealign = NULL;
|
|
Loc loc = token.loc;
|
|
Expressions *udas = NULL;
|
|
Token *tk;
|
|
|
|
//printf("parseDeclarations() %s\n", token.toChars());
|
|
if (!comment)
|
|
comment = token.blockComment;
|
|
|
|
if (autodecl)
|
|
{
|
|
ts = NULL; // infer type
|
|
goto L2;
|
|
}
|
|
|
|
if (token.value == TOKalias)
|
|
{
|
|
tok = token.value;
|
|
nextToken();
|
|
|
|
/* Look for:
|
|
* alias identifier this;
|
|
*/
|
|
if (token.value == TOKidentifier && peekNext() == TOKthis)
|
|
{
|
|
AliasThis *s = new AliasThis(loc, token.ident);
|
|
nextToken();
|
|
check(TOKthis);
|
|
check(TOKsemicolon);
|
|
Dsymbols *a = new Dsymbols();
|
|
a->push(s);
|
|
addComment(s, comment);
|
|
return a;
|
|
}
|
|
/* Look for:
|
|
* alias identifier = type;
|
|
* alias identifier(...) = type;
|
|
*/
|
|
if (token.value == TOKidentifier &&
|
|
skipParensIf(peek(&token), &tk) &&
|
|
tk->value == TOKassign)
|
|
{
|
|
Dsymbols *a = new Dsymbols();
|
|
while (1)
|
|
{
|
|
ident = token.ident;
|
|
nextToken();
|
|
TemplateParameters *tpl = NULL;
|
|
if (token.value == TOKlparen)
|
|
tpl = parseTemplateParameterList();
|
|
check(TOKassign);
|
|
|
|
bool hasParsedAttributes = false;
|
|
if (token.value == TOKat)
|
|
{
|
|
parseAttributes(this, hasParsedAttributes,
|
|
storage_class, link, setAlignment, ealign, udas);
|
|
}
|
|
|
|
Declaration *v;
|
|
Dsymbol *s;
|
|
|
|
// try to parse function type:
|
|
// TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
|
|
bool attributesAppended = false;
|
|
const StorageClass funcStc = parseTypeCtor();
|
|
Token *tlu = &token;
|
|
if (token.value != TOKfunction &&
|
|
token.value != TOKdelegate &&
|
|
isBasicType(&tlu) && tlu &&
|
|
tlu->value == TOKlparen)
|
|
{
|
|
VarArg vargs;
|
|
Type *tret = parseBasicType();
|
|
Parameters *prms = parseParameters(&vargs);
|
|
ParameterList pl = ParameterList(prms, vargs);
|
|
|
|
parseAttributes(this, hasParsedAttributes,
|
|
storage_class, link, setAlignment, ealign, udas);
|
|
if (udas)
|
|
error("user-defined attributes not allowed for `alias` declarations");
|
|
|
|
attributesAppended = true;
|
|
storage_class = appendStorageClass(storage_class, funcStc);
|
|
Type *tf = new TypeFunction(pl, tret, link, storage_class);
|
|
v = new AliasDeclaration(loc, ident, tf);
|
|
}
|
|
else if (token.value == TOKfunction ||
|
|
token.value == TOKdelegate ||
|
|
(token.value == TOKlparen &&
|
|
skipAttributes(peekPastParen(&token), &tk) &&
|
|
(tk->value == TOKgoesto || tk->value == TOKlcurly)) ||
|
|
token.value == TOKlcurly ||
|
|
(token.value == TOKidentifier && peekNext() == TOKgoesto) ||
|
|
(token.value == TOKref && peekNext() == TOKlparen &&
|
|
skipAttributes(peekPastParen(peek(&token)), &tk) &&
|
|
(tk->value == TOKgoesto || tk->value == TOKlcurly)))
|
|
{
|
|
// function (parameters) { statements... }
|
|
// delegate (parameters) { statements... }
|
|
// (parameters) { statements... }
|
|
// (parameters) => expression
|
|
// { statements... }
|
|
// identifier => expression
|
|
// ref (parameters) { statements... }
|
|
// ref (parameters) => expression
|
|
|
|
s = parseFunctionLiteral();
|
|
|
|
if (udas != NULL)
|
|
{
|
|
if (storage_class != 0)
|
|
error("Cannot put a storage-class in an alias declaration.");
|
|
// shouldn't have set these variables
|
|
assert(link == linkage && !setAlignment && ealign == NULL);
|
|
TemplateDeclaration *tpl_ = (TemplateDeclaration *) s;
|
|
assert(tpl_ != NULL && tpl_->members->length == 1);
|
|
FuncLiteralDeclaration *fd = (FuncLiteralDeclaration *) (*tpl_->members)[0];
|
|
TypeFunction *tf = (TypeFunction *) fd->type;
|
|
assert(tf->parameterList.length() > 0);
|
|
Dsymbols *as = new Dsymbols();
|
|
(*tf->parameterList.parameters)[0]->userAttribDecl = new UserAttributeDeclaration(udas, as);
|
|
}
|
|
v = new AliasDeclaration(loc, ident, s);
|
|
}
|
|
else
|
|
{
|
|
// StorageClasses type
|
|
parseAttributes(this, hasParsedAttributes,
|
|
storage_class, link, setAlignment, ealign, udas);
|
|
if (udas)
|
|
error("user-defined attributes not allowed for %s declarations", Token::toChars(tok));
|
|
|
|
t = parseType();
|
|
v = new AliasDeclaration(loc, ident, t);
|
|
}
|
|
if (!attributesAppended)
|
|
storage_class = appendStorageClass(storage_class, funcStc);
|
|
v->storage_class = storage_class;
|
|
|
|
s = v;
|
|
if (tpl)
|
|
{
|
|
Dsymbols *a2 = new Dsymbols();
|
|
a2->push(s);
|
|
TemplateDeclaration *tempdecl =
|
|
new TemplateDeclaration(loc, ident, tpl, NULL, a2);
|
|
s = tempdecl;
|
|
}
|
|
if (setAlignment)
|
|
{
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(s);
|
|
s = new AlignDeclaration(v->loc, ealign, ax);
|
|
}
|
|
if (link != linkage)
|
|
{
|
|
Dsymbols *a2 = new Dsymbols();
|
|
a2->push(s);
|
|
s = new LinkDeclaration(link, a2);
|
|
}
|
|
a->push(s);
|
|
|
|
switch (token.value)
|
|
{
|
|
case TOKsemicolon:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
break;
|
|
case TOKcomma:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
if (token.value != TOKidentifier)
|
|
{
|
|
error("identifier expected following comma, not %s", token.toChars());
|
|
break;
|
|
}
|
|
if (peekNext() != TOKassign && peekNext() != TOKlparen)
|
|
{
|
|
error("= expected following identifier");
|
|
nextToken();
|
|
break;
|
|
}
|
|
continue;
|
|
default:
|
|
error("semicolon expected to close %s declaration", Token::toChars(tok));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
// alias StorageClasses type ident;
|
|
}
|
|
|
|
parseStorageClasses(storage_class, link, setAlignment, ealign, udas);
|
|
|
|
if (token.value == TOKstruct ||
|
|
token.value == TOKunion ||
|
|
token.value == TOKclass ||
|
|
token.value == TOKinterface)
|
|
{
|
|
Dsymbol *s = parseAggregate();
|
|
Dsymbols *a = new Dsymbols();
|
|
a->push(s);
|
|
|
|
if (storage_class)
|
|
{
|
|
s = new StorageClassDeclaration(storage_class, a);
|
|
a = new Dsymbols();
|
|
a->push(s);
|
|
}
|
|
if (setAlignment)
|
|
{
|
|
s = new AlignDeclaration(s->loc, ealign, a);
|
|
a = new Dsymbols();
|
|
a->push(s);
|
|
}
|
|
if (link != linkage)
|
|
{
|
|
s = new LinkDeclaration(link, a);
|
|
a = new Dsymbols();
|
|
a->push(s);
|
|
}
|
|
if (udas)
|
|
{
|
|
s = new UserAttributeDeclaration(udas, a);
|
|
a = new Dsymbols();
|
|
a->push(s);
|
|
}
|
|
|
|
addComment(s, comment);
|
|
return a;
|
|
}
|
|
|
|
/* Look for auto initializers:
|
|
* storage_class identifier = initializer;
|
|
* storage_class identifier(...) = initializer;
|
|
*/
|
|
if ((storage_class || udas) &&
|
|
token.value == TOKidentifier &&
|
|
skipParensIf(peek(&token), &tk) &&
|
|
tk->value == TOKassign)
|
|
{
|
|
Dsymbols *a = parseAutoDeclarations(storage_class, comment);
|
|
if (udas)
|
|
{
|
|
Dsymbol *s = new UserAttributeDeclaration(udas, a);
|
|
a = new Dsymbols();
|
|
a->push(s);
|
|
}
|
|
return a;
|
|
}
|
|
|
|
/* Look for return type inference for template functions.
|
|
*/
|
|
if ((storage_class || udas) && 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)))
|
|
{
|
|
ts = NULL;
|
|
}
|
|
else
|
|
{
|
|
ts = parseBasicType();
|
|
ts = parseBasicType2(ts);
|
|
}
|
|
|
|
L2:
|
|
tfirst = NULL;
|
|
Dsymbols *a = new Dsymbols();
|
|
|
|
if (pAttrs)
|
|
{
|
|
storage_class |= pAttrs->storageClass;
|
|
//pAttrs->storageClass = STCundefined;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
TemplateParameters *tpl = NULL;
|
|
int disable;
|
|
int alt = 0;
|
|
|
|
loc = token.loc;
|
|
ident = NULL;
|
|
t = parseDeclarator(ts, &alt, &ident, &tpl, storage_class, &disable, &udas);
|
|
assert(t);
|
|
if (!tfirst)
|
|
tfirst = t;
|
|
else if (t != tfirst)
|
|
error("multiple declarations must have the same type, not %s and %s",
|
|
tfirst->toChars(), t->toChars());
|
|
bool isThis = (t->ty == Tident && ((TypeIdentifier *)t)->ident == Id::This && token.value == TOKassign);
|
|
if (ident)
|
|
checkCstyleTypeSyntax(loc, t, alt, ident);
|
|
else if (!isThis)
|
|
error("no identifier for declarator %s", t->toChars());
|
|
|
|
if (tok == TOKalias)
|
|
{
|
|
Declaration *v;
|
|
Initializer *init = NULL;
|
|
|
|
/* Aliases can no longer have multiple declarators, storage classes,
|
|
* linkages, or auto declarations.
|
|
* These never made any sense, anyway.
|
|
* The code below needs to be fixed to reject them.
|
|
* The grammar has already been fixed to preclude them.
|
|
*/
|
|
|
|
if (udas)
|
|
error("user-defined attributes not allowed for %s declarations", Token::toChars(tok));
|
|
|
|
if (token.value == TOKassign)
|
|
{
|
|
nextToken();
|
|
init = parseInitializer();
|
|
}
|
|
if (init)
|
|
{
|
|
if (isThis)
|
|
error("cannot use syntax `alias this = %s`, use `alias %s this` instead",
|
|
init->toChars(), init->toChars());
|
|
else
|
|
error("alias cannot have initializer");
|
|
}
|
|
v = new AliasDeclaration(loc, ident, t);
|
|
|
|
v->storage_class = storage_class;
|
|
if (pAttrs)
|
|
{
|
|
/* AliasDeclaration distinguish @safe, @system, @trusted atttributes
|
|
* on prefix and postfix.
|
|
* @safe alias void function() FP1;
|
|
* alias @safe void function() FP2; // FP2 is not @safe
|
|
* alias void function() @safe FP3;
|
|
*/
|
|
pAttrs->storageClass &= (STCsafe | STCsystem | STCtrusted);
|
|
}
|
|
Dsymbol *s = v;
|
|
|
|
if (link != linkage)
|
|
{
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(v);
|
|
s = new LinkDeclaration(link, ax);
|
|
}
|
|
a->push(s);
|
|
switch (token.value)
|
|
{
|
|
case TOKsemicolon:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
break;
|
|
|
|
case TOKcomma:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
continue;
|
|
|
|
default:
|
|
error("semicolon expected to close %s declaration", Token::toChars(tok));
|
|
break;
|
|
}
|
|
}
|
|
else if (t->ty == Tfunction)
|
|
{
|
|
Expression *constraint = NULL;
|
|
|
|
//printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t->toChars(), storage_class);
|
|
FuncDeclaration *f =
|
|
new FuncDeclaration(loc, Loc(), ident, storage_class | (disable ? STCdisable : 0), t);
|
|
if (pAttrs)
|
|
pAttrs->storageClass = STCundefined;
|
|
if (tpl)
|
|
constraint = parseConstraint();
|
|
Dsymbol *s = parseContracts(f);
|
|
Identifier *tplIdent = s->ident;
|
|
if (link != linkage)
|
|
{
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(s);
|
|
s = new LinkDeclaration(link, ax);
|
|
}
|
|
if (udas)
|
|
{
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(s);
|
|
s = new UserAttributeDeclaration(udas, ax);
|
|
}
|
|
|
|
/* A template parameter list means it's a function template
|
|
*/
|
|
if (tpl)
|
|
{
|
|
// Wrap a template around the function declaration
|
|
Dsymbols *decldefs = new Dsymbols();
|
|
decldefs->push(s);
|
|
TemplateDeclaration *tempdecl =
|
|
new TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
|
|
s = tempdecl;
|
|
|
|
if (storage_class & STCstatic)
|
|
{
|
|
assert(f->storage_class & STCstatic);
|
|
f->storage_class &= ~STCstatic;
|
|
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(s);
|
|
s = new StorageClassDeclaration(STCstatic, ax);
|
|
}
|
|
}
|
|
a->push(s);
|
|
addComment(s, comment);
|
|
}
|
|
else if (ident)
|
|
{
|
|
Initializer *init = NULL;
|
|
if (token.value == TOKassign)
|
|
{
|
|
nextToken();
|
|
init = parseInitializer();
|
|
}
|
|
|
|
VarDeclaration *v = new VarDeclaration(loc, t, ident, init);
|
|
v->storage_class = storage_class;
|
|
if (pAttrs)
|
|
pAttrs->storageClass = STCundefined;
|
|
|
|
Dsymbol *s = v;
|
|
|
|
if (tpl && init)
|
|
{
|
|
Dsymbols *a2 = new Dsymbols();
|
|
a2->push(s);
|
|
TemplateDeclaration *tempdecl =
|
|
new TemplateDeclaration(loc, ident, tpl, NULL, a2, 0);
|
|
s = tempdecl;
|
|
}
|
|
if (link != linkage)
|
|
{
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(s);
|
|
s = new LinkDeclaration(link, ax);
|
|
}
|
|
if (udas)
|
|
{
|
|
Dsymbols *ax = new Dsymbols();
|
|
ax->push(s);
|
|
s = new UserAttributeDeclaration(udas, ax);
|
|
}
|
|
a->push(s);
|
|
switch (token.value)
|
|
{
|
|
case TOKsemicolon:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
break;
|
|
|
|
case TOKcomma:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
continue;
|
|
|
|
default:
|
|
error("semicolon expected, not `%s`", token.toChars());
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
Dsymbol *Parser::parseFunctionLiteral()
|
|
{
|
|
Loc loc = token.loc;
|
|
|
|
TemplateParameters *tpl = NULL;
|
|
Parameters *parameters = NULL;
|
|
VarArg varargs = VARARGnone;
|
|
Type *tret = NULL;
|
|
StorageClass stc = 0;
|
|
TOK save = TOKreserved;
|
|
|
|
switch (token.value)
|
|
{
|
|
case TOKfunction:
|
|
case TOKdelegate:
|
|
save = token.value;
|
|
nextToken();
|
|
if (token.value == TOKref)
|
|
{
|
|
// function ref (parameters) { statements... }
|
|
// delegate ref (parameters) { statements... }
|
|
stc = STCref;
|
|
nextToken();
|
|
}
|
|
if (token.value != TOKlparen && token.value != TOKlcurly)
|
|
{
|
|
// function type (parameters) { statements... }
|
|
// delegate type (parameters) { statements... }
|
|
tret = parseBasicType();
|
|
tret = parseBasicType2(tret); // function return type
|
|
}
|
|
|
|
if (token.value == TOKlparen)
|
|
{
|
|
// function (parameters) { statements... }
|
|
// delegate (parameters) { statements... }
|
|
}
|
|
else
|
|
{
|
|
// function { statements... }
|
|
// delegate { statements... }
|
|
break;
|
|
}
|
|
goto LTOKlparen;
|
|
|
|
case TOKref:
|
|
// ref (parameters) => expression
|
|
// ref (parameters) { statements... }
|
|
stc = STCref;
|
|
nextToken();
|
|
goto LTOKlparen;
|
|
|
|
case TOKlparen:
|
|
LTOKlparen:
|
|
{
|
|
// (parameters) => expression
|
|
// (parameters) { statements... }
|
|
parameters = parseParameters(&varargs, &tpl);
|
|
stc = parsePostfix(stc, NULL);
|
|
if (StorageClass modStc = stc & STC_TYPECTOR)
|
|
{
|
|
if (save == TOKfunction)
|
|
{
|
|
OutBuffer buf;
|
|
stcToBuffer(&buf, modStc);
|
|
error("function literal cannot be %s", buf.peekChars());
|
|
}
|
|
else
|
|
save = TOKdelegate;
|
|
}
|
|
break;
|
|
}
|
|
case TOKlcurly:
|
|
// { statements... }
|
|
break;
|
|
|
|
case TOKidentifier:
|
|
{
|
|
// identifier => expression
|
|
parameters = new Parameters();
|
|
Identifier *id = Identifier::generateId("__T");
|
|
Type *t = new TypeIdentifier(loc, id);
|
|
parameters->push(new Parameter(0, t, token.ident, NULL, NULL));
|
|
|
|
tpl = new TemplateParameters();
|
|
TemplateParameter *tp = new TemplateTypeParameter(loc, id, NULL, NULL);
|
|
tpl->push(tp);
|
|
|
|
nextToken();
|
|
break;
|
|
}
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
if (!parameters)
|
|
parameters = new Parameters();
|
|
TypeFunction *tf = new TypeFunction(ParameterList(parameters, varargs),
|
|
tret, linkage, stc);
|
|
tf = (TypeFunction *)tf->addSTC(stc);
|
|
FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, Loc(), tf, save, NULL);
|
|
|
|
if (token.value == TOKgoesto)
|
|
{
|
|
check(TOKgoesto);
|
|
Loc returnloc = token.loc;
|
|
Expression *ae = parseAssignExp();
|
|
fd->fbody = new ReturnStatement(returnloc, ae);
|
|
fd->endloc = token.loc;
|
|
}
|
|
else
|
|
{
|
|
parseContracts(fd);
|
|
}
|
|
|
|
if (tpl)
|
|
{
|
|
// Wrap a template around function fd
|
|
Dsymbols *decldefs = new Dsymbols();
|
|
decldefs->push(fd);
|
|
return new TemplateDeclaration(fd->loc, fd->ident, tpl, NULL, decldefs, false, true);
|
|
}
|
|
else
|
|
return fd;
|
|
}
|
|
|
|
/*****************************************
|
|
* Parse auto declarations of the form:
|
|
* storageClass ident = init, ident = init, ... ;
|
|
* and return the array of them.
|
|
* Starts with token on the first ident.
|
|
* Ends with scanner past closing ';'
|
|
*/
|
|
|
|
Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, const utf8_t *comment)
|
|
{
|
|
//printf("parseAutoDeclarations\n");
|
|
Token *tk;
|
|
Dsymbols *a = new Dsymbols;
|
|
|
|
while (1)
|
|
{
|
|
Loc loc = token.loc;
|
|
Identifier *ident = token.ident;
|
|
nextToken(); // skip over ident
|
|
|
|
TemplateParameters *tpl = NULL;
|
|
if (token.value == TOKlparen)
|
|
tpl = parseTemplateParameterList();
|
|
|
|
check(TOKassign); // skip over '='
|
|
Initializer *init = parseInitializer();
|
|
VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
|
|
v->storage_class = storageClass;
|
|
|
|
Dsymbol *s = v;
|
|
if (tpl)
|
|
{
|
|
Dsymbols *a2 = new Dsymbols();
|
|
a2->push(v);
|
|
TemplateDeclaration *tempdecl =
|
|
new TemplateDeclaration(loc, ident, tpl, NULL, a2, 0);
|
|
s = tempdecl;
|
|
}
|
|
a->push(s);
|
|
switch (token.value)
|
|
{
|
|
case TOKsemicolon:
|
|
nextToken();
|
|
addComment(s, comment);
|
|
break;
|
|
|
|
case TOKcomma:
|
|
nextToken();
|
|
if (!(token.value == TOKidentifier &&
|
|
skipParensIf(peek(&token), &tk) &&
|
|
tk->value == TOKassign))
|
|
{
|
|
error("identifier expected following comma");
|
|
break;
|
|
}
|
|
addComment(s, comment);
|
|
continue;
|
|
|
|
default:
|
|
error("semicolon expected following auto declaration, not `%s`", token.toChars());
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
/*****************************************
|
|
* Parse contracts following function declaration.
|
|
*/
|
|
|
|
FuncDeclaration *Parser::parseContracts(FuncDeclaration *f)
|
|
{
|
|
LINK linksave = linkage;
|
|
|
|
bool literal = f->isFuncLiteralDeclaration() != NULL;
|
|
|
|
// The following is irrelevant, as it is overridden by sc->linkage in
|
|
// TypeFunction::semantic
|
|
linkage = LINKd; // nested functions have D linkage
|
|
bool requireDo = false;
|
|
L1:
|
|
switch (token.value)
|
|
{
|
|
case TOKlcurly:
|
|
if (requireDo)
|
|
error("missing body { ... } after in or out");
|
|
f->fbody = parseStatement(PSsemi);
|
|
f->endloc = endloc;
|
|
break;
|
|
|
|
case TOKidentifier:
|
|
if (token.ident != Id::_body)
|
|
goto Ldefault;
|
|
/* fall through */
|
|
|
|
case TOKdo:
|
|
nextToken();
|
|
f->fbody = parseStatement(PScurly);
|
|
f->endloc = endloc;
|
|
break;
|
|
|
|
case TOKin:
|
|
{
|
|
// in { statements... }
|
|
// in (expression)
|
|
Loc loc = token.loc;
|
|
nextToken();
|
|
if (!f->frequires)
|
|
{
|
|
f->frequires = new Statements();
|
|
}
|
|
if (token.value == TOKlparen)
|
|
{
|
|
nextToken();
|
|
Expression *e = parseAssignExp();
|
|
Expression *msg = NULL;
|
|
if (token.value == TOKcomma)
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKrparen)
|
|
{
|
|
msg = parseAssignExp();
|
|
if (token.value == TOKcomma)
|
|
nextToken();
|
|
}
|
|
}
|
|
check(TOKrparen);
|
|
e = new AssertExp(loc, e, msg);
|
|
f->frequires->push(new ExpStatement(loc, e));
|
|
requireDo = false;
|
|
}
|
|
else
|
|
{
|
|
f->frequires->push(parseStatement(PScurly | PSscope));
|
|
requireDo = true;
|
|
}
|
|
goto L1;
|
|
}
|
|
|
|
case TOKout:
|
|
{
|
|
// out { statements... }
|
|
// out (; expression)
|
|
// out (identifier) { statements... }
|
|
// out (identifier; expression)
|
|
Loc loc = token.loc;
|
|
nextToken();
|
|
if (!f->fensures)
|
|
{
|
|
f->fensures = new Ensures();
|
|
}
|
|
Identifier *id = NULL;
|
|
if (token.value != TOKlcurly)
|
|
{
|
|
check(TOKlparen);
|
|
if (token.value != TOKidentifier && token.value != TOKsemicolon)
|
|
error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
|
|
if (token.value != TOKsemicolon)
|
|
{
|
|
id = token.ident;
|
|
nextToken();
|
|
}
|
|
if (token.value == TOKsemicolon)
|
|
{
|
|
nextToken();
|
|
Expression *e = parseAssignExp();
|
|
Expression *msg = NULL;
|
|
if (token.value == TOKcomma)
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKrparen)
|
|
{
|
|
msg = parseAssignExp();
|
|
if (token.value == TOKcomma)
|
|
nextToken();
|
|
}
|
|
}
|
|
check(TOKrparen);
|
|
e = new AssertExp(loc, e, msg);
|
|
f->fensures->push(Ensure(id, new ExpStatement(loc, e)));
|
|
requireDo = false;
|
|
goto L1;
|
|
}
|
|
check(TOKrparen);
|
|
}
|
|
f->fensures->push(Ensure(id, parseStatement(PScurly | PSscope)));
|
|
requireDo = true;
|
|
goto L1;
|
|
}
|
|
|
|
case TOKsemicolon:
|
|
if (!literal)
|
|
{
|
|
// Bugzilla 15799: Semicolon becomes a part of function declaration
|
|
// only when 'do' is not required
|
|
if (!requireDo)
|
|
nextToken();
|
|
break;
|
|
}
|
|
/* fall through */
|
|
|
|
default:
|
|
Ldefault:
|
|
if (literal)
|
|
{
|
|
const char *sbody = requireDo ? "do " : "";
|
|
error("missing %s{ ... } for function literal", sbody);
|
|
}
|
|
else if (!requireDo) // allow these even with no body
|
|
{
|
|
error("semicolon expected following function declaration");
|
|
}
|
|
break;
|
|
}
|
|
if (literal && !f->fbody)
|
|
{
|
|
// Set empty function body for error recovery
|
|
f->fbody = new CompoundStatement(Loc(), (Statement *)NULL);
|
|
}
|
|
|
|
linkage = linksave;
|
|
|
|
return f;
|
|
}
|
|
|
|
/*****************************************
|
|
* Parse initializer for variable declaration.
|
|
*/
|
|
|
|
Initializer *Parser::parseInitializer()
|
|
{
|
|
StructInitializer *is;
|
|
ArrayInitializer *ia;
|
|
ExpInitializer *ie;
|
|
Expression *e;
|
|
Identifier *id;
|
|
Initializer *value;
|
|
int comma;
|
|
Loc loc = token.loc;
|
|
Token *t;
|
|
int braces;
|
|
int brackets;
|
|
|
|
switch (token.value)
|
|
{
|
|
case TOKlcurly:
|
|
/* Scan ahead to see if it is a struct initializer or
|
|
* a function literal.
|
|
* If it contains a ';', it is a function literal.
|
|
* Treat { } as a struct initializer.
|
|
*/
|
|
braces = 1;
|
|
for (t = peek(&token); 1; t = peek(t))
|
|
{
|
|
switch (t->value)
|
|
{
|
|
case TOKsemicolon:
|
|
case TOKreturn:
|
|
goto Lexpression;
|
|
|
|
case TOKlcurly:
|
|
braces++;
|
|
continue;
|
|
|
|
case TOKrcurly:
|
|
if (--braces == 0)
|
|
break;
|
|
continue;
|
|
|
|
case TOKeof:
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
is = new StructInitializer(loc);
|
|
nextToken();
|
|
comma = 2;
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKidentifier:
|
|
if (comma == 1)
|
|
error("comma expected separating field initializers");
|
|
t = peek(&token);
|
|
if (t->value == TOKcolon)
|
|
{
|
|
id = token.ident;
|
|
nextToken();
|
|
nextToken(); // skip over ':'
|
|
}
|
|
else
|
|
{ id = NULL;
|
|
}
|
|
value = parseInitializer();
|
|
is->addInit(id, value);
|
|
comma = 1;
|
|
continue;
|
|
|
|
case TOKcomma:
|
|
if (comma == 2)
|
|
error("expression expected, not `,`");
|
|
nextToken();
|
|
comma = 2;
|
|
continue;
|
|
|
|
case TOKrcurly: // allow trailing comma's
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKeof:
|
|
error("found EOF instead of initializer");
|
|
break;
|
|
|
|
default:
|
|
if (comma == 1)
|
|
error("comma expected separating field initializers");
|
|
value = parseInitializer();
|
|
is->addInit(NULL, value);
|
|
comma = 1;
|
|
continue;
|
|
//error("found `%s` instead of field initializer", token.toChars());
|
|
//break;
|
|
}
|
|
break;
|
|
}
|
|
return is;
|
|
|
|
case TOKlbracket:
|
|
/* Scan ahead to see if it is an array initializer or
|
|
* an expression.
|
|
* If it ends with a ';' ',' or '}', it is an array initializer.
|
|
*/
|
|
brackets = 1;
|
|
for (t = peek(&token); 1; t = peek(t))
|
|
{
|
|
switch (t->value)
|
|
{
|
|
case TOKlbracket:
|
|
brackets++;
|
|
continue;
|
|
|
|
case TOKrbracket:
|
|
if (--brackets == 0)
|
|
{ t = peek(t);
|
|
if (t->value != TOKsemicolon &&
|
|
t->value != TOKcomma &&
|
|
t->value != TOKrbracket &&
|
|
t->value != TOKrcurly)
|
|
goto Lexpression;
|
|
break;
|
|
}
|
|
continue;
|
|
|
|
case TOKeof:
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ia = new ArrayInitializer(loc);
|
|
nextToken();
|
|
comma = 2;
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
default:
|
|
if (comma == 1)
|
|
{ error("comma expected separating array initializers, not %s", token.toChars());
|
|
nextToken();
|
|
break;
|
|
}
|
|
e = parseAssignExp();
|
|
if (!e)
|
|
break;
|
|
if (token.value == TOKcolon)
|
|
{
|
|
nextToken();
|
|
value = parseInitializer();
|
|
}
|
|
else
|
|
{ value = new ExpInitializer(e->loc, e);
|
|
e = NULL;
|
|
}
|
|
ia->addInit(e, value);
|
|
comma = 1;
|
|
continue;
|
|
|
|
case TOKlcurly:
|
|
case TOKlbracket:
|
|
if (comma == 1)
|
|
error("comma expected separating array initializers, not %s", token.toChars());
|
|
value = parseInitializer();
|
|
if (token.value == TOKcolon)
|
|
{
|
|
nextToken();
|
|
e = initializerToExpression(value);
|
|
value = parseInitializer();
|
|
}
|
|
else
|
|
e = NULL;
|
|
ia->addInit(e, value);
|
|
comma = 1;
|
|
continue;
|
|
|
|
case TOKcomma:
|
|
if (comma == 2)
|
|
error("expression expected, not `,`");
|
|
nextToken();
|
|
comma = 2;
|
|
continue;
|
|
|
|
case TOKrbracket: // allow trailing comma's
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKeof:
|
|
error("found `%s` instead of array initializer", token.toChars());
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return ia;
|
|
|
|
case TOKvoid:
|
|
t = peek(&token);
|
|
if (t->value == TOKsemicolon || t->value == TOKcomma)
|
|
{
|
|
nextToken();
|
|
return new VoidInitializer(loc);
|
|
}
|
|
goto Lexpression;
|
|
|
|
default:
|
|
Lexpression:
|
|
e = parseAssignExp();
|
|
ie = new ExpInitializer(loc, e);
|
|
return ie;
|
|
}
|
|
}
|
|
|
|
/*****************************************
|
|
* Parses default argument initializer expression that is an assign expression,
|
|
* with special handling for __FILE__, __FILE_FULL_PATH__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
|
|
*/
|
|
|
|
Expression *Parser::parseDefaultInitExp()
|
|
{
|
|
if (token.value == TOKfile ||
|
|
token.value == TOKfilefullpath ||
|
|
token.value == TOKline ||
|
|
token.value == TOKmodulestring ||
|
|
token.value == TOKfuncstring ||
|
|
token.value == TOKprettyfunc)
|
|
{
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKcomma || t->value == TOKrparen)
|
|
{
|
|
Expression *e = NULL;
|
|
if (token.value == TOKfile)
|
|
e = new FileInitExp(token.loc, TOKfile);
|
|
else if (token.value == TOKfilefullpath)
|
|
e = new FileInitExp(token.loc, TOKfilefullpath);
|
|
else if (token.value == TOKline)
|
|
e = new LineInitExp(token.loc);
|
|
else if (token.value == TOKmodulestring)
|
|
e = new ModuleInitExp(token.loc);
|
|
else if (token.value == TOKfuncstring)
|
|
e = new FuncInitExp(token.loc);
|
|
else if (token.value == TOKprettyfunc)
|
|
e = new PrettyFuncInitExp(token.loc);
|
|
else
|
|
assert(0);
|
|
nextToken();
|
|
return e;
|
|
}
|
|
}
|
|
|
|
Expression *e = parseAssignExp();
|
|
return e;
|
|
}
|
|
|
|
/*****************************************
|
|
*/
|
|
|
|
void Parser::checkDanglingElse(Loc elseloc)
|
|
{
|
|
if (token.value != TOKelse &&
|
|
token.value != TOKcatch &&
|
|
token.value != TOKfinally &&
|
|
lookingForElse.linnum != 0)
|
|
{
|
|
warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
|
|
}
|
|
}
|
|
|
|
void Parser::checkCstyleTypeSyntax(Loc loc, Type *t, int alt, Identifier *ident)
|
|
{
|
|
if (!alt)
|
|
return;
|
|
|
|
const char *sp = !ident ? "" : " ";
|
|
const char *s = !ident ? "" : ident->toChars();
|
|
if (alt & 1) // contains C-style function pointer syntax
|
|
error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t->toChars(), sp, s);
|
|
else
|
|
::warning(loc, "instead of C-style syntax, use D-style syntax `%s%s%s`", t->toChars(), sp, s);
|
|
|
|
}
|
|
|
|
/*****************************************
|
|
* Parses `foreach` statements, `static foreach` statements and
|
|
* `static foreach` declarations. The template parameter
|
|
* `isStatic` is true, iff a `static foreach` should be parsed.
|
|
* If `isStatic` is true, `isDecl` can be true to indicate that a
|
|
* `static foreach` declaration should be parsed.
|
|
*/
|
|
Statement *Parser::parseForeach(Loc loc, bool *isRange, bool isDecl)
|
|
{
|
|
TOK op = token.value;
|
|
|
|
nextToken();
|
|
check(TOKlparen);
|
|
|
|
Parameters *parameters = new Parameters();
|
|
|
|
while (1)
|
|
{
|
|
Identifier *ai = NULL;
|
|
Type *at;
|
|
|
|
StorageClass storageClass = 0;
|
|
StorageClass stc = 0;
|
|
Lagain:
|
|
if (stc)
|
|
{
|
|
storageClass = appendStorageClass(storageClass, stc);
|
|
nextToken();
|
|
}
|
|
switch (token.value)
|
|
{
|
|
case TOKref:
|
|
stc = STCref;
|
|
goto Lagain;
|
|
|
|
case TOKenum:
|
|
stc = STCmanifest;
|
|
goto Lagain;
|
|
|
|
case TOKalias:
|
|
storageClass = appendStorageClass(storageClass, STCalias);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKconst:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCconst;
|
|
goto Lagain;
|
|
}
|
|
break;
|
|
|
|
case TOKimmutable:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCimmutable;
|
|
goto Lagain;
|
|
}
|
|
break;
|
|
|
|
case TOKshared:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCshared;
|
|
goto Lagain;
|
|
}
|
|
break;
|
|
|
|
case TOKwild:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCwild;
|
|
goto Lagain;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (token.value == TOKidentifier)
|
|
{
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKcomma || t->value == TOKsemicolon)
|
|
{ ai = token.ident;
|
|
at = NULL; // infer argument type
|
|
nextToken();
|
|
goto Larg;
|
|
}
|
|
}
|
|
at = parseType(&ai);
|
|
if (!ai)
|
|
error("no identifier for declarator %s", at->toChars());
|
|
Larg:
|
|
Parameter *p = new Parameter(storageClass, at, ai, NULL, NULL);
|
|
parameters->push(p);
|
|
if (token.value == TOKcomma)
|
|
{ nextToken();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
check(TOKsemicolon);
|
|
|
|
Expression *aggr = parseExpression();
|
|
if (token.value == TOKslice && parameters->length == 1)
|
|
{
|
|
Parameter *p = (*parameters)[0];
|
|
delete parameters;
|
|
nextToken();
|
|
Expression *upr = parseExpression();
|
|
check(TOKrparen);
|
|
Loc endloc;
|
|
Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL;
|
|
if (isRange)
|
|
*isRange = true;
|
|
return new ForeachRangeStatement(loc, op, p, aggr, upr, body, endloc);
|
|
}
|
|
else
|
|
{
|
|
check(TOKrparen);
|
|
Loc endloc;
|
|
Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL;
|
|
if (isRange)
|
|
*isRange = false;
|
|
return new ForeachStatement(loc, op, parameters, aggr, body, endloc);
|
|
}
|
|
}
|
|
|
|
Dsymbol *Parser::parseForeachStaticDecl(Loc loc, Dsymbol **pLastDecl)
|
|
{
|
|
nextToken();
|
|
|
|
bool isRange = false;
|
|
Statement *s = parseForeach(loc, &isRange, true);
|
|
|
|
return new StaticForeachDeclaration(
|
|
new StaticForeach(loc, isRange ? NULL : (ForeachStatement *)s,
|
|
isRange ? (ForeachRangeStatement *)s : NULL),
|
|
parseBlock(pLastDecl)
|
|
);
|
|
}
|
|
|
|
Statement *Parser::parseForeachStatic(Loc loc)
|
|
{
|
|
nextToken();
|
|
|
|
bool isRange = false;
|
|
Statement *s = parseForeach(loc, &isRange, false);
|
|
|
|
return new StaticForeachStatement(loc,
|
|
new StaticForeach(loc, isRange ? NULL : (ForeachStatement *)s,
|
|
isRange ? (ForeachRangeStatement *)s : NULL)
|
|
);
|
|
}
|
|
|
|
/*****************************************
|
|
* Input:
|
|
* flags PSxxxx
|
|
* Output:
|
|
* pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of first token of next statement
|
|
*/
|
|
|
|
Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc)
|
|
{
|
|
Statement *s = NULL;
|
|
Condition *cond;
|
|
Statement *ifbody;
|
|
Statement *elsebody;
|
|
bool isfinal;
|
|
Loc loc = token.loc;
|
|
|
|
//printf("parseStatement()\n");
|
|
|
|
if (flags & PScurly && token.value != TOKlcurly)
|
|
error("statement expected to be { }, not %s", token.toChars());
|
|
|
|
switch (token.value)
|
|
{
|
|
case TOKidentifier:
|
|
{ /* A leading identifier can be a declaration, label, or expression.
|
|
* The easiest case to check first is label:
|
|
*/
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKcolon)
|
|
{
|
|
Token *nt = peek(t);
|
|
if (nt->value == TOKcolon)
|
|
{
|
|
// skip ident::
|
|
nextToken();
|
|
nextToken();
|
|
nextToken();
|
|
error("use `.` for member lookup, not `::`");
|
|
break;
|
|
}
|
|
// It's a label
|
|
Identifier *ident = token.ident;
|
|
nextToken();
|
|
nextToken();
|
|
if (token.value == TOKrcurly)
|
|
s = NULL;
|
|
else if (token.value == TOKlcurly)
|
|
s = parseStatement(PScurly | PSscope);
|
|
else
|
|
s = parseStatement(PSsemi_ok);
|
|
s = new LabelStatement(loc, ident, s);
|
|
break;
|
|
}
|
|
}
|
|
/* fall through */
|
|
case TOKdot:
|
|
case TOKtypeof:
|
|
case TOKvector:
|
|
case TOKtraits:
|
|
/* Bugzilla 15163: If tokens can be handled as
|
|
* old C-style declaration or D expression, prefer the latter.
|
|
*/
|
|
if (isDeclaration(&token, 3, TOKreserved, NULL))
|
|
goto Ldeclaration;
|
|
else
|
|
goto Lexp;
|
|
break;
|
|
|
|
case TOKassert:
|
|
case TOKthis:
|
|
case TOKsuper:
|
|
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 TOKcharv:
|
|
case TOKwcharv:
|
|
case TOKdcharv:
|
|
case TOKnull:
|
|
case TOKtrue:
|
|
case TOKfalse:
|
|
case TOKstring:
|
|
case TOKxstring:
|
|
case TOKlparen:
|
|
case TOKcast:
|
|
case TOKmul:
|
|
case TOKmin:
|
|
case TOKadd:
|
|
case TOKtilde:
|
|
case TOKnot:
|
|
case TOKplusplus:
|
|
case TOKminusminus:
|
|
case TOKnew:
|
|
case TOKdelete:
|
|
case TOKdelegate:
|
|
case TOKfunction:
|
|
case TOKtypeid:
|
|
case TOKis:
|
|
case TOKlbracket:
|
|
case TOKfile:
|
|
case TOKfilefullpath:
|
|
case TOKline:
|
|
case TOKmodulestring:
|
|
case TOKfuncstring:
|
|
case TOKprettyfunc:
|
|
Lexp:
|
|
{
|
|
Expression *exp = parseExpression();
|
|
check(TOKsemicolon, "statement");
|
|
s = new ExpStatement(loc, exp);
|
|
break;
|
|
}
|
|
|
|
case TOKstatic:
|
|
{ // Look ahead to see if it's static assert() or static if()
|
|
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKassert)
|
|
{
|
|
s = new StaticAssertStatement(parseStaticAssert());
|
|
break;
|
|
}
|
|
if (t->value == TOKif)
|
|
{
|
|
cond = parseStaticIfCondition();
|
|
goto Lcondition;
|
|
}
|
|
else if (t->value == TOKforeach || t->value == TOKforeach_reverse)
|
|
{
|
|
s = parseForeachStatic(loc);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
}
|
|
if (t->value == TOKimport)
|
|
{
|
|
Dsymbols *imports = parseImport();
|
|
s = new ImportStatement(loc, imports);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
}
|
|
goto Ldeclaration;
|
|
}
|
|
|
|
case TOKfinal:
|
|
if (peekNext() == TOKswitch)
|
|
{
|
|
nextToken();
|
|
isfinal = true;
|
|
goto Lswitch;
|
|
}
|
|
goto Ldeclaration;
|
|
|
|
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:
|
|
// bug 7773: int.max is always a part of expression
|
|
if (peekNext() == TOKdot)
|
|
goto Lexp;
|
|
if (peekNext() == TOKlparen)
|
|
goto Lexp;
|
|
/* fall through */
|
|
|
|
case TOKalias:
|
|
case TOKconst:
|
|
case TOKauto:
|
|
case TOKabstract:
|
|
case TOKextern:
|
|
case TOKalign:
|
|
case TOKimmutable:
|
|
case TOKshared:
|
|
case TOKwild:
|
|
case TOKdeprecated:
|
|
case TOKnothrow:
|
|
case TOKpure:
|
|
case TOKref:
|
|
case TOKgshared:
|
|
case TOKat:
|
|
case TOKstruct:
|
|
case TOKunion:
|
|
case TOKclass:
|
|
case TOKinterface:
|
|
Ldeclaration:
|
|
{
|
|
Dsymbols *a = parseDeclarations(false, NULL, NULL);
|
|
if (a->length > 1)
|
|
{
|
|
Statements *as = new Statements();
|
|
as->reserve(a->length);
|
|
for (size_t i = 0; i < a->length; i++)
|
|
{
|
|
Dsymbol *d = (*a)[i];
|
|
s = new ExpStatement(loc, d);
|
|
as->push(s);
|
|
}
|
|
s = new CompoundDeclarationStatement(loc, as);
|
|
}
|
|
else if (a->length == 1)
|
|
{
|
|
Dsymbol *d = (*a)[0];
|
|
s = new ExpStatement(loc, d);
|
|
}
|
|
else
|
|
s = new ExpStatement(loc, (Expression *)NULL);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
}
|
|
|
|
case TOKenum:
|
|
{ /* Determine if this is a manifest constant declaration,
|
|
* or a conventional enum.
|
|
*/
|
|
Dsymbol *d;
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKlcurly || t->value == TOKcolon)
|
|
d = parseEnum();
|
|
else if (t->value != TOKidentifier)
|
|
goto Ldeclaration;
|
|
else
|
|
{
|
|
t = peek(t);
|
|
if (t->value == TOKlcurly || t->value == TOKcolon ||
|
|
t->value == TOKsemicolon)
|
|
d = parseEnum();
|
|
else
|
|
goto Ldeclaration;
|
|
}
|
|
s = new ExpStatement(loc, d);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
}
|
|
|
|
case TOKmixin:
|
|
{
|
|
if (isDeclaration(&token, 3, TOKreserved, NULL))
|
|
goto Ldeclaration;
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKlparen)
|
|
{
|
|
// mixin(string)
|
|
Expression *e = parseAssignExp();
|
|
check(TOKsemicolon);
|
|
if (e->op == TOKmixin)
|
|
{
|
|
CompileExp *cpe = (CompileExp *)e;
|
|
s = new CompileStatement(loc, cpe->exps);
|
|
}
|
|
else
|
|
{
|
|
s = new ExpStatement(loc, e);
|
|
}
|
|
break;
|
|
}
|
|
Dsymbol *d = parseMixin();
|
|
s = new ExpStatement(loc, d);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
}
|
|
|
|
case TOKlcurly:
|
|
{
|
|
Loc lookingForElseSave = lookingForElse;
|
|
lookingForElse = Loc();
|
|
|
|
nextToken();
|
|
//if (token.value == TOKsemicolon)
|
|
//error("use `{ }` for an empty statement, not a `;`");
|
|
Statements *statements = new Statements();
|
|
while (token.value != TOKrcurly && token.value != TOKeof)
|
|
{
|
|
statements->push(parseStatement(PSsemi | PScurlyscope));
|
|
}
|
|
if (endPtr) *endPtr = token.ptr;
|
|
endloc = token.loc;
|
|
if (pEndloc)
|
|
{
|
|
*pEndloc = token.loc;
|
|
pEndloc = NULL; // don't set it again
|
|
}
|
|
s = new CompoundStatement(loc, statements);
|
|
if (flags & (PSscope | PScurlyscope))
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
check(TOKrcurly, "compound statement");
|
|
lookingForElse = lookingForElseSave;
|
|
break;
|
|
}
|
|
|
|
case TOKwhile:
|
|
{
|
|
nextToken();
|
|
check(TOKlparen);
|
|
Expression *condition = parseExpression();
|
|
check(TOKrparen);
|
|
Loc endloc;
|
|
Statement *body = parseStatement(PSscope, NULL, &endloc);
|
|
s = new WhileStatement(loc, condition, body, endloc);
|
|
break;
|
|
}
|
|
|
|
case TOKsemicolon:
|
|
if (!(flags & PSsemi_ok))
|
|
{
|
|
if (flags & PSsemi)
|
|
deprecation("use `{ }` for an empty statement, not a `;`");
|
|
else
|
|
error("use `{ }` for an empty statement, not a `;`");
|
|
}
|
|
nextToken();
|
|
s = new ExpStatement(loc, (Expression *)NULL);
|
|
break;
|
|
|
|
case TOKdo:
|
|
{ Statement *body;
|
|
Expression *condition;
|
|
|
|
nextToken();
|
|
Loc lookingForElseSave = lookingForElse;
|
|
lookingForElse = Loc();
|
|
body = parseStatement(PSscope);
|
|
lookingForElse = lookingForElseSave;
|
|
check(TOKwhile);
|
|
check(TOKlparen);
|
|
condition = parseExpression();
|
|
check(TOKrparen);
|
|
if (token.value == TOKsemicolon)
|
|
nextToken();
|
|
else
|
|
error("terminating `;` required after do-while statement");
|
|
s = new DoStatement(loc, body, condition, token.loc);
|
|
break;
|
|
}
|
|
|
|
case TOKfor:
|
|
{
|
|
Statement *init;
|
|
Expression *condition;
|
|
Expression *increment;
|
|
|
|
nextToken();
|
|
check(TOKlparen);
|
|
if (token.value == TOKsemicolon)
|
|
{ init = NULL;
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
Loc lookingForElseSave = lookingForElse;
|
|
lookingForElse = Loc();
|
|
init = parseStatement(0);
|
|
lookingForElse = lookingForElseSave;
|
|
}
|
|
if (token.value == TOKsemicolon)
|
|
{
|
|
condition = NULL;
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
condition = parseExpression();
|
|
check(TOKsemicolon, "for condition");
|
|
}
|
|
if (token.value == TOKrparen)
|
|
{ increment = NULL;
|
|
nextToken();
|
|
}
|
|
else
|
|
{ increment = parseExpression();
|
|
check(TOKrparen);
|
|
}
|
|
Loc endloc;
|
|
Statement *body = parseStatement(PSscope, NULL, &endloc);
|
|
s = new ForStatement(loc, init, condition, increment, body, endloc);
|
|
break;
|
|
}
|
|
|
|
case TOKforeach:
|
|
case TOKforeach_reverse:
|
|
{
|
|
s = parseForeach(loc, NULL, false);
|
|
break;
|
|
}
|
|
|
|
case TOKif:
|
|
{
|
|
Parameter *param = NULL;
|
|
Expression *condition;
|
|
|
|
nextToken();
|
|
check(TOKlparen);
|
|
|
|
StorageClass storageClass = 0;
|
|
StorageClass stc = 0;
|
|
LagainStc:
|
|
if (stc)
|
|
{
|
|
storageClass = appendStorageClass(storageClass, stc);
|
|
nextToken();
|
|
}
|
|
switch (token.value)
|
|
{
|
|
case TOKref:
|
|
stc = STCref;
|
|
goto LagainStc;
|
|
case TOKauto:
|
|
stc = STCauto;
|
|
goto LagainStc;
|
|
case TOKconst:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCconst;
|
|
goto LagainStc;
|
|
}
|
|
break;
|
|
case TOKimmutable:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCimmutable;
|
|
goto LagainStc;
|
|
}
|
|
break;
|
|
case TOKshared:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCshared;
|
|
goto LagainStc;
|
|
}
|
|
break;
|
|
case TOKwild:
|
|
if (peekNext() != TOKlparen)
|
|
{
|
|
stc = STCwild;
|
|
goto LagainStc;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (storageClass != 0 &&
|
|
token.value == TOKidentifier &&
|
|
peek(&token)->value == TOKassign)
|
|
{
|
|
Identifier *ai = token.ident;
|
|
Type *at = NULL; // infer parameter type
|
|
nextToken();
|
|
check(TOKassign);
|
|
param = new Parameter(storageClass, at, ai, NULL, NULL);
|
|
}
|
|
else if (isDeclaration(&token, 2, TOKassign, NULL))
|
|
{
|
|
Identifier *ai;
|
|
Type *at = parseType(&ai);
|
|
check(TOKassign);
|
|
param = new Parameter(storageClass, at, ai, NULL, NULL);
|
|
}
|
|
|
|
condition = parseExpression();
|
|
check(TOKrparen);
|
|
{
|
|
Loc lookingForElseSave = lookingForElse;
|
|
lookingForElse = loc;
|
|
ifbody = parseStatement(PSscope);
|
|
lookingForElse = lookingForElseSave;
|
|
}
|
|
if (token.value == TOKelse)
|
|
{
|
|
Loc elseloc = token.loc;
|
|
nextToken();
|
|
elsebody = parseStatement(PSscope);
|
|
checkDanglingElse(elseloc);
|
|
}
|
|
else
|
|
elsebody = NULL;
|
|
if (condition && ifbody)
|
|
s = new IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
|
|
else
|
|
s = NULL; // don't propagate parsing errors
|
|
break;
|
|
}
|
|
|
|
case TOKscope:
|
|
if (peek(&token)->value != TOKlparen)
|
|
goto Ldeclaration; // scope used as storage class
|
|
nextToken();
|
|
check(TOKlparen);
|
|
if (token.value != TOKidentifier)
|
|
{ error("scope identifier expected");
|
|
goto Lerror;
|
|
}
|
|
else
|
|
{ TOK t = TOKon_scope_exit;
|
|
Identifier *id = token.ident;
|
|
|
|
if (id == Id::exit)
|
|
t = TOKon_scope_exit;
|
|
else if (id == Id::failure)
|
|
t = TOKon_scope_failure;
|
|
else if (id == Id::success)
|
|
t = TOKon_scope_success;
|
|
else
|
|
error("valid scope identifiers are exit, failure, or success, not %s", id->toChars());
|
|
nextToken();
|
|
check(TOKrparen);
|
|
Statement *st = parseStatement(PSscope);
|
|
s = new ScopeGuardStatement(loc, t, st);
|
|
break;
|
|
}
|
|
|
|
case TOKdebug:
|
|
nextToken();
|
|
if (token.value == TOKassign)
|
|
{
|
|
error("debug conditions can only be declared at module scope");
|
|
nextToken();
|
|
nextToken();
|
|
goto Lerror;
|
|
}
|
|
cond = parseDebugCondition();
|
|
goto Lcondition;
|
|
|
|
case TOKversion:
|
|
nextToken();
|
|
if (token.value == TOKassign)
|
|
{
|
|
error("version conditions can only be declared at module scope");
|
|
nextToken();
|
|
nextToken();
|
|
goto Lerror;
|
|
}
|
|
cond = parseVersionCondition();
|
|
goto Lcondition;
|
|
|
|
Lcondition:
|
|
{
|
|
Loc lookingForElseSave = lookingForElse;
|
|
lookingForElse = loc;
|
|
ifbody = parseStatement(0);
|
|
lookingForElse = lookingForElseSave;
|
|
}
|
|
elsebody = NULL;
|
|
if (token.value == TOKelse)
|
|
{
|
|
Loc elseloc = token.loc;
|
|
nextToken();
|
|
elsebody = parseStatement(0);
|
|
checkDanglingElse(elseloc);
|
|
}
|
|
s = new ConditionalStatement(loc, cond, ifbody, elsebody);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
|
|
case TOKpragma:
|
|
{ Identifier *ident;
|
|
Expressions *args = NULL;
|
|
Statement *body;
|
|
|
|
nextToken();
|
|
check(TOKlparen);
|
|
if (token.value != TOKidentifier)
|
|
{ error("pragma(identifier expected");
|
|
goto Lerror;
|
|
}
|
|
ident = token.ident;
|
|
nextToken();
|
|
if (token.value == TOKcomma && peekNext() != TOKrparen)
|
|
args = parseArguments(); // pragma(identifier, args...);
|
|
else
|
|
check(TOKrparen); // pragma(identifier);
|
|
if (token.value == TOKsemicolon)
|
|
{ nextToken();
|
|
body = NULL;
|
|
}
|
|
else
|
|
body = parseStatement(PSsemi);
|
|
s = new PragmaStatement(loc, ident, args, body);
|
|
break;
|
|
}
|
|
|
|
case TOKswitch:
|
|
isfinal = false;
|
|
goto Lswitch;
|
|
|
|
Lswitch:
|
|
{
|
|
nextToken();
|
|
check(TOKlparen);
|
|
Expression *condition = parseExpression();
|
|
check(TOKrparen);
|
|
Statement *body = parseStatement(PSscope);
|
|
s = new SwitchStatement(loc, condition, body, isfinal);
|
|
break;
|
|
}
|
|
|
|
case TOKcase:
|
|
{ Expression *exp;
|
|
Expressions cases; // array of Expression's
|
|
Expression *last = NULL;
|
|
|
|
while (1)
|
|
{
|
|
nextToken();
|
|
exp = parseAssignExp();
|
|
cases.push(exp);
|
|
if (token.value != TOKcomma)
|
|
break;
|
|
}
|
|
check(TOKcolon);
|
|
|
|
/* case exp: .. case last:
|
|
*/
|
|
if (token.value == TOKslice)
|
|
{
|
|
if (cases.length > 1)
|
|
error("only one case allowed for start of case range");
|
|
nextToken();
|
|
check(TOKcase);
|
|
last = parseAssignExp();
|
|
check(TOKcolon);
|
|
}
|
|
|
|
if (flags & PScurlyscope)
|
|
{
|
|
Statements *statements = new Statements();
|
|
while (token.value != TOKcase &&
|
|
token.value != TOKdefault &&
|
|
token.value != TOKeof &&
|
|
token.value != TOKrcurly)
|
|
{
|
|
statements->push(parseStatement(PSsemi | PScurlyscope));
|
|
}
|
|
s = new CompoundStatement(loc, statements);
|
|
}
|
|
else
|
|
s = parseStatement(PSsemi | PScurlyscope);
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
|
|
if (last)
|
|
{
|
|
s = new CaseRangeStatement(loc, exp, last, s);
|
|
}
|
|
else
|
|
{
|
|
// Keep cases in order by building the case statements backwards
|
|
for (size_t i = cases.length; i; i--)
|
|
{
|
|
exp = cases[i - 1];
|
|
s = new CaseStatement(loc, exp, s);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TOKdefault:
|
|
{
|
|
nextToken();
|
|
check(TOKcolon);
|
|
|
|
if (flags & PScurlyscope)
|
|
{
|
|
Statements *statements = new Statements();
|
|
while (token.value != TOKcase &&
|
|
token.value != TOKdefault &&
|
|
token.value != TOKeof &&
|
|
token.value != TOKrcurly)
|
|
{
|
|
statements->push(parseStatement(PSsemi | PScurlyscope));
|
|
}
|
|
s = new CompoundStatement(loc, statements);
|
|
}
|
|
else
|
|
s = parseStatement(PSsemi | PScurlyscope);
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
s = new DefaultStatement(loc, s);
|
|
break;
|
|
}
|
|
|
|
case TOKreturn:
|
|
{ Expression *exp;
|
|
|
|
nextToken();
|
|
if (token.value == TOKsemicolon)
|
|
exp = NULL;
|
|
else
|
|
exp = parseExpression();
|
|
check(TOKsemicolon, "return statement");
|
|
s = new ReturnStatement(loc, exp);
|
|
break;
|
|
}
|
|
|
|
case TOKbreak:
|
|
{ Identifier *ident;
|
|
|
|
nextToken();
|
|
if (token.value == TOKidentifier)
|
|
{ ident = token.ident;
|
|
nextToken();
|
|
}
|
|
else
|
|
ident = NULL;
|
|
check(TOKsemicolon, "break statement");
|
|
s = new BreakStatement(loc, ident);
|
|
break;
|
|
}
|
|
|
|
case TOKcontinue:
|
|
{ Identifier *ident;
|
|
|
|
nextToken();
|
|
if (token.value == TOKidentifier)
|
|
{ ident = token.ident;
|
|
nextToken();
|
|
}
|
|
else
|
|
ident = NULL;
|
|
check(TOKsemicolon, "continue statement");
|
|
s = new ContinueStatement(loc, ident);
|
|
break;
|
|
}
|
|
|
|
case TOKgoto:
|
|
{ Identifier *ident;
|
|
|
|
nextToken();
|
|
if (token.value == TOKdefault)
|
|
{
|
|
nextToken();
|
|
s = new GotoDefaultStatement(loc);
|
|
}
|
|
else if (token.value == TOKcase)
|
|
{
|
|
Expression *exp = NULL;
|
|
|
|
nextToken();
|
|
if (token.value != TOKsemicolon)
|
|
exp = parseExpression();
|
|
s = new GotoCaseStatement(loc, exp);
|
|
}
|
|
else
|
|
{
|
|
if (token.value != TOKidentifier)
|
|
{
|
|
error("identifier expected following goto");
|
|
ident = NULL;
|
|
}
|
|
else
|
|
{
|
|
ident = token.ident;
|
|
nextToken();
|
|
}
|
|
s = new GotoStatement(loc, ident);
|
|
}
|
|
check(TOKsemicolon, "goto statement");
|
|
break;
|
|
}
|
|
|
|
case TOKsynchronized:
|
|
{ Expression *exp;
|
|
Statement *body;
|
|
|
|
Token *t = peek(&token);
|
|
if (skipAttributes(t, &t) && t->value == TOKclass)
|
|
goto Ldeclaration;
|
|
|
|
nextToken();
|
|
if (token.value == TOKlparen)
|
|
{
|
|
nextToken();
|
|
exp = parseExpression();
|
|
check(TOKrparen);
|
|
}
|
|
else
|
|
exp = NULL;
|
|
body = parseStatement(PSscope);
|
|
s = new SynchronizedStatement(loc, exp, body);
|
|
break;
|
|
}
|
|
|
|
case TOKwith:
|
|
{ Expression *exp;
|
|
Statement *body;
|
|
|
|
nextToken();
|
|
check(TOKlparen);
|
|
exp = parseExpression();
|
|
check(TOKrparen);
|
|
body = parseStatement(PSscope);
|
|
s = new WithStatement(loc, exp, body, token.loc);
|
|
break;
|
|
}
|
|
|
|
case TOKtry:
|
|
{ Statement *body;
|
|
Catches *catches = NULL;
|
|
Statement *finalbody = NULL;
|
|
|
|
nextToken();
|
|
Loc lookingForElseSave = lookingForElse;
|
|
lookingForElse = Loc();
|
|
body = parseStatement(PSscope);
|
|
lookingForElse = lookingForElseSave;
|
|
while (token.value == TOKcatch)
|
|
{
|
|
Statement *handler;
|
|
Catch *c;
|
|
Type *t;
|
|
Identifier *id;
|
|
Loc catchloc = token.loc;
|
|
|
|
nextToken();
|
|
if (token.value == TOKlcurly || token.value != TOKlparen)
|
|
{
|
|
t = NULL;
|
|
id = NULL;
|
|
}
|
|
else
|
|
{
|
|
check(TOKlparen);
|
|
id = NULL;
|
|
t = parseType(&id);
|
|
check(TOKrparen);
|
|
}
|
|
handler = parseStatement(0);
|
|
c = new Catch(catchloc, t, id, handler);
|
|
if (!catches)
|
|
catches = new Catches();
|
|
catches->push(c);
|
|
}
|
|
|
|
if (token.value == TOKfinally)
|
|
{
|
|
nextToken();
|
|
finalbody = parseStatement(PSscope);
|
|
}
|
|
|
|
s = body;
|
|
if (!catches && !finalbody)
|
|
error("catch or finally expected following try");
|
|
else
|
|
{ if (catches)
|
|
s = new TryCatchStatement(loc, body, catches);
|
|
if (finalbody)
|
|
s = new TryFinallyStatement(loc, s, finalbody);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TOKthrow:
|
|
{ Expression *exp;
|
|
|
|
nextToken();
|
|
exp = parseExpression();
|
|
check(TOKsemicolon, "throw statement");
|
|
s = new ThrowStatement(loc, exp);
|
|
break;
|
|
}
|
|
|
|
case TOKasm:
|
|
{
|
|
// Parse the asm block into a sequence of AsmStatements,
|
|
// each AsmStatement is one instruction.
|
|
// Separate out labels.
|
|
// Defer parsing of AsmStatements until semantic processing.
|
|
|
|
Loc labelloc;
|
|
|
|
nextToken();
|
|
StorageClass stc = parsePostfix(STCundefined, NULL);
|
|
if (stc & (STCconst | STCimmutable | STCshared | STCwild))
|
|
error("const/immutable/shared/inout attributes are not allowed on asm blocks");
|
|
|
|
check(TOKlcurly);
|
|
Token *toklist = NULL;
|
|
Token **ptoklist = &toklist;
|
|
Identifier *label = NULL;
|
|
Statements *statements = new Statements();
|
|
size_t nestlevel = 0;
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKidentifier:
|
|
if (!toklist)
|
|
{
|
|
// Look ahead to see if it is a label
|
|
Token *t = peek(&token);
|
|
if (t->value == TOKcolon)
|
|
{ // It's a label
|
|
label = token.ident;
|
|
labelloc = token.loc;
|
|
nextToken();
|
|
nextToken();
|
|
continue;
|
|
}
|
|
}
|
|
goto Ldefault;
|
|
|
|
case TOKlcurly:
|
|
++nestlevel;
|
|
goto Ldefault;
|
|
|
|
case TOKrcurly:
|
|
if (nestlevel > 0)
|
|
{
|
|
--nestlevel;
|
|
goto Ldefault;
|
|
}
|
|
|
|
if (toklist || label)
|
|
{
|
|
error("asm statements must end in `;`");
|
|
}
|
|
break;
|
|
|
|
case TOKsemicolon:
|
|
if (nestlevel != 0)
|
|
error("mismatched number of curly brackets");
|
|
|
|
s = NULL;
|
|
if (toklist || label)
|
|
{
|
|
// Create AsmStatement from list of tokens we've saved
|
|
s = new AsmStatement(token.loc, toklist);
|
|
toklist = NULL;
|
|
ptoklist = &toklist;
|
|
if (label)
|
|
{ s = new LabelStatement(labelloc, label, s);
|
|
label = NULL;
|
|
}
|
|
statements->push(s);
|
|
}
|
|
nextToken();
|
|
continue;
|
|
|
|
case TOKeof:
|
|
/* { */
|
|
error("matching `}` expected, not end of file");
|
|
goto Lerror;
|
|
/* fall through */
|
|
|
|
default:
|
|
Ldefault:
|
|
*ptoklist = Token::alloc();
|
|
memcpy(*ptoklist, &token, sizeof(Token));
|
|
ptoklist = &(*ptoklist)->next;
|
|
*ptoklist = NULL;
|
|
|
|
nextToken();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
s = new CompoundAsmStatement(loc, statements, stc);
|
|
nextToken();
|
|
break;
|
|
}
|
|
|
|
case TOKimport:
|
|
{
|
|
Dsymbols *imports = parseImport();
|
|
s = new ImportStatement(loc, imports);
|
|
if (flags & PSscope)
|
|
s = new ScopeStatement(loc, s, token.loc);
|
|
break;
|
|
}
|
|
|
|
case TOKtemplate:
|
|
{
|
|
Dsymbol *d = parseTemplateDeclaration();
|
|
s = new ExpStatement(loc, d);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
error("found `%s` instead of statement", token.toChars());
|
|
goto Lerror;
|
|
|
|
Lerror:
|
|
while (token.value != TOKrcurly &&
|
|
token.value != TOKsemicolon &&
|
|
token.value != TOKeof)
|
|
nextToken();
|
|
if (token.value == TOKsemicolon)
|
|
nextToken();
|
|
s = NULL;
|
|
break;
|
|
}
|
|
if (pEndloc)
|
|
*pEndloc = token.loc;
|
|
return s;
|
|
}
|
|
|
|
void Parser::check(TOK value)
|
|
{
|
|
check(token.loc, value);
|
|
}
|
|
|
|
void Parser::check(Loc loc, TOK value)
|
|
{
|
|
if (token.value != value)
|
|
error(loc, "found `%s` when expecting `%s`", token.toChars(), Token::toChars(value));
|
|
nextToken();
|
|
}
|
|
|
|
void Parser::check(TOK value, const char *string)
|
|
{
|
|
if (token.value != value)
|
|
error("found `%s` when expecting `%s` following %s",
|
|
token.toChars(), Token::toChars(value), string);
|
|
nextToken();
|
|
}
|
|
|
|
void Parser::checkParens(TOK value, Expression *e)
|
|
{
|
|
if (precedence[e->op] == PREC_rel && !e->parens)
|
|
error(e->loc, "%s must be parenthesized when next to operator %s", e->toChars(), Token::toChars(value));
|
|
}
|
|
|
|
/************************************
|
|
* Determine if the scanner is sitting on the start of a declaration.
|
|
* Input:
|
|
* needId 0 no identifier
|
|
* 1 identifier optional
|
|
* 2 must have identifier
|
|
* 3 must have identifier, but don't recognize old C-style syntax.
|
|
* Output:
|
|
* if *pt is not NULL, it is set to the ending token, which would be endtok
|
|
*/
|
|
|
|
bool Parser::isDeclaration(Token *t, int needId, TOK endtok, Token **pt)
|
|
{
|
|
//printf("isDeclaration(needId = %d)\n", needId);
|
|
int haveId = 0;
|
|
int haveTpl = 0;
|
|
|
|
while (1)
|
|
{
|
|
if ((t->value == TOKconst ||
|
|
t->value == TOKimmutable ||
|
|
t->value == TOKwild ||
|
|
t->value == TOKshared) &&
|
|
peek(t)->value != TOKlparen)
|
|
{
|
|
/* const type
|
|
* immutable type
|
|
* shared type
|
|
* wild type
|
|
*/
|
|
t = peek(t);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!isBasicType(&t))
|
|
{
|
|
goto Lisnot;
|
|
}
|
|
if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != 3))
|
|
goto Lisnot;
|
|
if ((needId == 0 && !haveId) ||
|
|
(needId == 1) ||
|
|
(needId == 2 && haveId) ||
|
|
(needId == 3 && haveId))
|
|
{
|
|
if (pt)
|
|
*pt = t;
|
|
goto Lis;
|
|
}
|
|
else
|
|
goto Lisnot;
|
|
|
|
Lis:
|
|
//printf("\tis declaration, t = %s\n", t->toChars());
|
|
return true;
|
|
|
|
Lisnot:
|
|
//printf("\tis not declaration\n");
|
|
return false;
|
|
}
|
|
|
|
bool Parser::isBasicType(Token **pt)
|
|
{
|
|
// This code parallels parseBasicType()
|
|
Token *t = *pt;
|
|
|
|
switch (t->value)
|
|
{
|
|
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:
|
|
t = peek(t);
|
|
break;
|
|
|
|
case TOKidentifier:
|
|
L5:
|
|
t = peek(t);
|
|
if (t->value == TOKnot)
|
|
{
|
|
goto L4;
|
|
}
|
|
goto L3;
|
|
while (1)
|
|
{
|
|
L2:
|
|
t = peek(t);
|
|
L3:
|
|
if (t->value == TOKdot)
|
|
{
|
|
Ldot:
|
|
t = peek(t);
|
|
if (t->value != TOKidentifier)
|
|
goto Lfalse;
|
|
t = peek(t);
|
|
if (t->value != TOKnot)
|
|
goto L3;
|
|
L4:
|
|
/* Seen a !
|
|
* Look for:
|
|
* !( args ), !identifier, etc.
|
|
*/
|
|
t = peek(t);
|
|
switch (t->value)
|
|
{
|
|
case TOKidentifier:
|
|
goto L5;
|
|
case TOKlparen:
|
|
if (!skipParens(t, &t))
|
|
goto Lfalse;
|
|
goto L3;
|
|
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 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:
|
|
goto L2;
|
|
default:
|
|
goto Lfalse;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TOKdot:
|
|
goto Ldot;
|
|
|
|
case TOKtypeof:
|
|
case TOKvector:
|
|
case TOKmixin:
|
|
/* typeof(exp).identifier...
|
|
*/
|
|
t = peek(t);
|
|
if (!skipParens(t, &t))
|
|
goto Lfalse;
|
|
goto L3;
|
|
|
|
case TOKtraits:
|
|
{
|
|
// __traits(getMember
|
|
t = peek(t);
|
|
if (t->value != TOKlparen)
|
|
goto Lfalse;
|
|
Token *lp = t;
|
|
t = peek(t);
|
|
if (t->value != TOKidentifier || t->ident != Id::getMember)
|
|
goto Lfalse;
|
|
if (!skipParens(lp, &lp))
|
|
goto Lfalse;
|
|
// we are in a lookup for decl VS statement
|
|
// so we expect a declarator following __trait if it's a type.
|
|
// other usages wont be ambiguous (alias, template instance, type qual, etc.)
|
|
if (lp->value != TOKidentifier)
|
|
goto Lfalse;
|
|
|
|
break;
|
|
}
|
|
|
|
case TOKconst:
|
|
case TOKimmutable:
|
|
case TOKshared:
|
|
case TOKwild:
|
|
// const(type) or immutable(type) or shared(type) or wild(type)
|
|
t = peek(t);
|
|
if (t->value != TOKlparen)
|
|
goto Lfalse;
|
|
t = peek(t);
|
|
if (!isDeclaration(t, 0, TOKrparen, &t))
|
|
{
|
|
goto Lfalse;
|
|
}
|
|
t = peek(t);
|
|
break;
|
|
|
|
default:
|
|
goto Lfalse;
|
|
}
|
|
*pt = t;
|
|
//printf("is\n");
|
|
return true;
|
|
|
|
Lfalse:
|
|
//printf("is not\n");
|
|
return false;
|
|
}
|
|
|
|
bool Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok, bool allowAltSyntax)
|
|
{ // This code parallels parseDeclarator()
|
|
Token *t = *pt;
|
|
int parens;
|
|
|
|
//printf("Parser::isDeclarator() %s\n", t->toChars());
|
|
if (t->value == TOKassign)
|
|
return false;
|
|
|
|
while (1)
|
|
{
|
|
parens = false;
|
|
switch (t->value)
|
|
{
|
|
case TOKmul:
|
|
//case TOKand:
|
|
t = peek(t);
|
|
continue;
|
|
|
|
case TOKlbracket:
|
|
t = peek(t);
|
|
if (t->value == TOKrbracket)
|
|
{
|
|
t = peek(t);
|
|
}
|
|
else if (isDeclaration(t, 0, TOKrbracket, &t))
|
|
{
|
|
// It's an associative array declaration
|
|
t = peek(t);
|
|
|
|
// ...[type].ident
|
|
if (t->value == TOKdot && peek(t)->value == TOKidentifier)
|
|
{
|
|
t = peek(t);
|
|
t = peek(t);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// [ expression ]
|
|
// [ expression .. expression ]
|
|
if (!isExpression(&t))
|
|
return false;
|
|
if (t->value == TOKslice)
|
|
{
|
|
t = peek(t);
|
|
if (!isExpression(&t))
|
|
return false;
|
|
if (t->value != TOKrbracket)
|
|
return false;
|
|
t = peek(t);
|
|
}
|
|
else
|
|
{
|
|
if (t->value != TOKrbracket)
|
|
return false;
|
|
t = peek(t);
|
|
|
|
// ...[index].ident
|
|
if (t->value == TOKdot && peek(t)->value == TOKidentifier)
|
|
{
|
|
t = peek(t);
|
|
t = peek(t);
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
|
|
case TOKidentifier:
|
|
if (*haveId)
|
|
return false;
|
|
*haveId = true;
|
|
t = peek(t);
|
|
break;
|
|
|
|
case TOKlparen:
|
|
if (!allowAltSyntax)
|
|
return false; // Do not recognize C-style declarations.
|
|
|
|
t = peek(t);
|
|
|
|
if (t->value == TOKrparen)
|
|
return false; // () is not a declarator
|
|
|
|
/* Regard ( identifier ) as not a declarator
|
|
* BUG: what about ( *identifier ) in
|
|
* f(*p)(x);
|
|
* where f is a class instance with overloaded () ?
|
|
* Should we just disallow C-style function pointer declarations?
|
|
*/
|
|
if (t->value == TOKidentifier)
|
|
{ Token *t2 = peek(t);
|
|
if (t2->value == TOKrparen)
|
|
return false;
|
|
}
|
|
|
|
|
|
if (!isDeclarator(&t, haveId, NULL, TOKrparen))
|
|
return false;
|
|
t = peek(t);
|
|
parens = true;
|
|
break;
|
|
|
|
case TOKdelegate:
|
|
case TOKfunction:
|
|
t = peek(t);
|
|
if (!isParameters(&t))
|
|
return false;
|
|
skipAttributes(t, &t);
|
|
continue;
|
|
default: break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
switch (t->value)
|
|
{
|
|
#if CARRAYDECL
|
|
case TOKlbracket:
|
|
parens = false;
|
|
t = peek(t);
|
|
if (t->value == TOKrbracket)
|
|
{
|
|
t = peek(t);
|
|
}
|
|
else if (isDeclaration(t, 0, TOKrbracket, &t))
|
|
{ // It's an associative array declaration
|
|
t = peek(t);
|
|
}
|
|
else
|
|
{
|
|
// [ expression ]
|
|
if (!isExpression(&t))
|
|
return false;
|
|
if (t->value != TOKrbracket)
|
|
return false;
|
|
t = peek(t);
|
|
}
|
|
continue;
|
|
#endif
|
|
|
|
case TOKlparen:
|
|
parens = false;
|
|
if (Token *tk = peekPastParen(t))
|
|
{
|
|
if (tk->value == TOKlparen)
|
|
{
|
|
if (!haveTpl) return false;
|
|
*haveTpl = 1;
|
|
t = tk;
|
|
}
|
|
else if (tk->value == TOKassign)
|
|
{
|
|
if (!haveTpl) return false;
|
|
*haveTpl = 1;
|
|
*pt = tk;
|
|
return true;
|
|
}
|
|
}
|
|
if (!isParameters(&t))
|
|
return false;
|
|
while (1)
|
|
{
|
|
switch (t->value)
|
|
{
|
|
case TOKconst:
|
|
case TOKimmutable:
|
|
case TOKshared:
|
|
case TOKwild:
|
|
case TOKpure:
|
|
case TOKnothrow:
|
|
case TOKreturn:
|
|
case TOKscope:
|
|
t = peek(t);
|
|
continue;
|
|
case TOKat:
|
|
t = peek(t); // skip '@'
|
|
t = peek(t); // skip identifier
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
continue;
|
|
|
|
case TOKidentifier:
|
|
if (t->ident != Id::_body)
|
|
goto Ldefault;
|
|
/* fall through */
|
|
|
|
// Valid tokens that follow a declaration
|
|
case TOKrparen:
|
|
case TOKrbracket:
|
|
case TOKassign:
|
|
case TOKcomma:
|
|
case TOKdotdotdot:
|
|
case TOKsemicolon:
|
|
case TOKlcurly:
|
|
case TOKin:
|
|
case TOKout:
|
|
case TOKdo:
|
|
LTOKdo:
|
|
// The !parens is to disallow unnecessary parentheses
|
|
if (!parens && (endtok == TOKreserved || endtok == t->value))
|
|
{
|
|
*pt = t;
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case TOKif:
|
|
return haveTpl ? true : false;
|
|
|
|
// Used for mixin type parsing
|
|
case TOKeof:
|
|
if (endtok == TOKeof)
|
|
goto LTOKdo;
|
|
return false;
|
|
|
|
default:
|
|
Ldefault:
|
|
return false;
|
|
}
|
|
}
|
|
assert(0);
|
|
}
|
|
|
|
|
|
bool Parser::isParameters(Token **pt)
|
|
{ // This code parallels parseParameters()
|
|
Token *t = *pt;
|
|
|
|
//printf("isParameters()\n");
|
|
if (t->value != TOKlparen)
|
|
return false;
|
|
|
|
t = peek(t);
|
|
for (;1; t = peek(t))
|
|
{
|
|
L1:
|
|
switch (t->value)
|
|
{
|
|
case TOKrparen:
|
|
break;
|
|
|
|
case TOKdotdotdot:
|
|
t = peek(t);
|
|
break;
|
|
|
|
case TOKin:
|
|
case TOKout:
|
|
case TOKref:
|
|
case TOKlazy:
|
|
case TOKscope:
|
|
case TOKfinal:
|
|
case TOKauto:
|
|
case TOKreturn:
|
|
continue;
|
|
|
|
case TOKconst:
|
|
case TOKimmutable:
|
|
case TOKshared:
|
|
case TOKwild:
|
|
t = peek(t);
|
|
if (t->value == TOKlparen)
|
|
{
|
|
t = peek(t);
|
|
if (!isDeclaration(t, 0, TOKrparen, &t))
|
|
return false;
|
|
t = peek(t); // skip past closing ')'
|
|
goto L2;
|
|
}
|
|
goto L1;
|
|
|
|
default:
|
|
{ if (!isBasicType(&t))
|
|
return false;
|
|
L2:
|
|
int tmp = false;
|
|
if (t->value != TOKdotdotdot &&
|
|
!isDeclarator(&t, &tmp, NULL, TOKreserved))
|
|
return false;
|
|
if (t->value == TOKassign)
|
|
{ t = peek(t);
|
|
if (!isExpression(&t))
|
|
return false;
|
|
}
|
|
if (t->value == TOKdotdotdot)
|
|
{
|
|
t = peek(t);
|
|
break;
|
|
}
|
|
}
|
|
if (t->value == TOKcomma)
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (t->value != TOKrparen)
|
|
return false;
|
|
t = peek(t);
|
|
*pt = t;
|
|
return true;
|
|
}
|
|
|
|
bool Parser::isExpression(Token **pt)
|
|
{
|
|
// This is supposed to determine if something is an expression.
|
|
// What it actually does is scan until a closing right bracket
|
|
// is found.
|
|
|
|
Token *t = *pt;
|
|
int brnest = 0;
|
|
int panest = 0;
|
|
int curlynest = 0;
|
|
|
|
for (;; t = peek(t))
|
|
{
|
|
switch (t->value)
|
|
{
|
|
case TOKlbracket:
|
|
brnest++;
|
|
continue;
|
|
|
|
case TOKrbracket:
|
|
if (--brnest >= 0)
|
|
continue;
|
|
break;
|
|
|
|
case TOKlparen:
|
|
panest++;
|
|
continue;
|
|
|
|
case TOKcomma:
|
|
if (brnest || panest)
|
|
continue;
|
|
break;
|
|
|
|
case TOKrparen:
|
|
if (--panest >= 0)
|
|
continue;
|
|
break;
|
|
|
|
case TOKlcurly:
|
|
curlynest++;
|
|
continue;
|
|
|
|
case TOKrcurly:
|
|
if (--curlynest >= 0)
|
|
continue;
|
|
return false;
|
|
|
|
case TOKslice:
|
|
if (brnest)
|
|
continue;
|
|
break;
|
|
|
|
case TOKsemicolon:
|
|
if (curlynest)
|
|
continue;
|
|
return false;
|
|
|
|
case TOKeof:
|
|
return false;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
*pt = t;
|
|
return true;
|
|
}
|
|
|
|
/*******************************************
|
|
* Skip parens, brackets.
|
|
* Input:
|
|
* t is on opening (
|
|
* Output:
|
|
* *pt is set to closing token, which is ')' on success
|
|
* Returns:
|
|
* true successful
|
|
* false some parsing error
|
|
*/
|
|
|
|
bool Parser::skipParens(Token *t, Token **pt)
|
|
{
|
|
if (t->value != TOKlparen)
|
|
return false;
|
|
|
|
int parens = 0;
|
|
|
|
while (1)
|
|
{
|
|
switch (t->value)
|
|
{
|
|
case TOKlparen:
|
|
parens++;
|
|
break;
|
|
|
|
case TOKrparen:
|
|
parens--;
|
|
if (parens < 0)
|
|
goto Lfalse;
|
|
if (parens == 0)
|
|
goto Ldone;
|
|
break;
|
|
|
|
case TOKeof:
|
|
goto Lfalse;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
t = peek(t);
|
|
}
|
|
|
|
Ldone:
|
|
if (pt)
|
|
*pt = peek(t); // skip found rparen
|
|
return true;
|
|
|
|
Lfalse:
|
|
return false;
|
|
}
|
|
|
|
bool Parser::skipParensIf(Token *t, Token **pt)
|
|
{
|
|
if (t->value != TOKlparen)
|
|
{
|
|
if (pt)
|
|
*pt = t;
|
|
return true;
|
|
}
|
|
return skipParens(t, pt);
|
|
}
|
|
|
|
/*******************************************
|
|
* Skip attributes.
|
|
* Input:
|
|
* t is on a candidate attribute
|
|
* Output:
|
|
* *pt is set to first non-attribute token on success
|
|
* Returns:
|
|
* true successful
|
|
* false some parsing error
|
|
*/
|
|
|
|
bool Parser::skipAttributes(Token *t, Token **pt)
|
|
{
|
|
while (1)
|
|
{
|
|
switch (t->value)
|
|
{
|
|
case TOKconst:
|
|
case TOKimmutable:
|
|
case TOKshared:
|
|
case TOKwild:
|
|
case TOKfinal:
|
|
case TOKauto:
|
|
case TOKscope:
|
|
case TOKoverride:
|
|
case TOKabstract:
|
|
case TOKsynchronized:
|
|
break;
|
|
case TOKdeprecated:
|
|
if (peek(t)->value == TOKlparen)
|
|
{
|
|
t = peek(t);
|
|
if (!skipParens(t, &t))
|
|
goto Lerror;
|
|
// t is on the next of closing parenthesis
|
|
continue;
|
|
}
|
|
break;
|
|
case TOKnothrow:
|
|
case TOKpure:
|
|
case TOKref:
|
|
case TOKgshared:
|
|
case TOKreturn:
|
|
//case TOKmanifest:
|
|
break;
|
|
case TOKat:
|
|
t = peek(t);
|
|
if (t->value == TOKidentifier)
|
|
{
|
|
/* @identifier
|
|
* @identifier!arg
|
|
* @identifier!(arglist)
|
|
* any of the above followed by (arglist)
|
|
* @predefined_attribute
|
|
*/
|
|
if (t->ident == Id::property ||
|
|
t->ident == Id::nogc ||
|
|
t->ident == Id::safe ||
|
|
t->ident == Id::trusted ||
|
|
t->ident == Id::system ||
|
|
t->ident == Id::disable)
|
|
break;
|
|
t = peek(t);
|
|
if (t->value == TOKnot)
|
|
{
|
|
t = peek(t);
|
|
if (t->value == TOKlparen)
|
|
{
|
|
// @identifier!(arglist)
|
|
if (!skipParens(t, &t))
|
|
goto Lerror;
|
|
// t is on the next of closing parenthesis
|
|
}
|
|
else
|
|
{
|
|
// @identifier!arg
|
|
// Do low rent skipTemplateArgument
|
|
if (t->value == TOKvector)
|
|
{
|
|
// identifier!__vector(type)
|
|
t = peek(t);
|
|
if (!skipParens(t, &t))
|
|
goto Lerror;
|
|
}
|
|
else
|
|
t = peek(t);
|
|
}
|
|
}
|
|
if (t->value == TOKlparen)
|
|
{
|
|
if (!skipParens(t, &t))
|
|
goto Lerror;
|
|
// t is on the next of closing parenthesis
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
if (t->value == TOKlparen)
|
|
{
|
|
// @( ArgumentList )
|
|
if (!skipParens(t, &t))
|
|
goto Lerror;
|
|
// t is on the next of closing parenthesis
|
|
continue;
|
|
}
|
|
goto Lerror;
|
|
default:
|
|
goto Ldone;
|
|
}
|
|
t = peek(t);
|
|
}
|
|
|
|
Ldone:
|
|
if (pt)
|
|
*pt = t;
|
|
return true;
|
|
|
|
Lerror:
|
|
return false;
|
|
}
|
|
|
|
/********************************* Expression Parser ***************************/
|
|
|
|
Expression *Parser::parsePrimaryExp()
|
|
{
|
|
Expression *e;
|
|
Type *t;
|
|
Identifier *id;
|
|
Loc loc = token.loc;
|
|
|
|
//printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
|
|
switch (token.value)
|
|
{
|
|
case TOKidentifier:
|
|
{
|
|
Token *t1 = peek(&token);
|
|
Token *t2 = peek(t1);
|
|
if (t1->value == TOKmin && t2->value == TOKgt)
|
|
{
|
|
// skip ident.
|
|
nextToken();
|
|
nextToken();
|
|
nextToken();
|
|
error("use `.` for member lookup, not `->`");
|
|
goto Lerr;
|
|
}
|
|
|
|
if (peekNext() == TOKgoesto)
|
|
goto case_delegate;
|
|
|
|
id = token.ident;
|
|
nextToken();
|
|
TOK save;
|
|
if (token.value == TOKnot && (save = peekNext()) != TOKis && save != TOKin)
|
|
{
|
|
// identifier!(template-argument-list)
|
|
TemplateInstance *tempinst;
|
|
tempinst = new TemplateInstance(loc, id);
|
|
tempinst->tiargs = parseTemplateArguments();
|
|
e = new ScopeExp(loc, tempinst);
|
|
}
|
|
else
|
|
e = new IdentifierExp(loc, id);
|
|
break;
|
|
}
|
|
|
|
case TOKdollar:
|
|
if (!inBrackets)
|
|
error("`$` is valid only inside [] of index or slice");
|
|
e = new DollarExp(loc);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKdot:
|
|
// Signal global scope '.' operator with "" identifier
|
|
e = new IdentifierExp(loc, Id::empty);
|
|
break;
|
|
|
|
case TOKthis:
|
|
e = new ThisExp(loc);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKsuper:
|
|
e = new SuperExp(loc);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKint32v:
|
|
e = new IntegerExp(loc, (d_int32)token.int64value, Type::tint32);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKuns32v:
|
|
e = new IntegerExp(loc, (d_uns32)token.uns64value, Type::tuns32);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKint64v:
|
|
e = new IntegerExp(loc, token.int64value, Type::tint64);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKuns64v:
|
|
e = new IntegerExp(loc, token.uns64value, Type::tuns64);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKfloat32v:
|
|
e = new RealExp(loc, token.floatvalue, Type::tfloat32);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKfloat64v:
|
|
e = new RealExp(loc, token.floatvalue, Type::tfloat64);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKfloat80v:
|
|
e = new RealExp(loc, token.floatvalue, Type::tfloat80);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKimaginary32v:
|
|
e = new RealExp(loc, token.floatvalue, Type::timaginary32);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKimaginary64v:
|
|
e = new RealExp(loc, token.floatvalue, Type::timaginary64);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKimaginary80v:
|
|
e = new RealExp(loc, token.floatvalue, Type::timaginary80);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKnull:
|
|
e = new NullExp(loc);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKfile:
|
|
{
|
|
const char *s = loc.filename ? loc.filename : mod->ident->toChars();
|
|
e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
|
|
nextToken();
|
|
break;
|
|
}
|
|
|
|
case TOKfilefullpath:
|
|
{
|
|
assert(loc.filename); // __FILE_FULL_PATH__ does not work with an invalid location
|
|
const char *s = FileName::toAbsolute(loc.filename);
|
|
e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
|
|
nextToken();
|
|
break;
|
|
}
|
|
|
|
case TOKline:
|
|
e = new IntegerExp(loc, loc.linnum, Type::tint32);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKmodulestring:
|
|
{
|
|
const char *s = md ? md->toChars() : mod->toChars();
|
|
e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
|
|
nextToken();
|
|
break;
|
|
}
|
|
|
|
case TOKfuncstring:
|
|
e = new FuncInitExp(loc);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKprettyfunc:
|
|
e = new PrettyFuncInitExp(loc);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKtrue:
|
|
e = new IntegerExp(loc, 1, Type::tbool);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKfalse:
|
|
e = new IntegerExp(loc, 0, Type::tbool);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKcharv:
|
|
e = new IntegerExp(loc, (d_uns8)token.uns64value, Type::tchar);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKwcharv:
|
|
e = new IntegerExp(loc, (d_uns16)token.uns64value, Type::twchar);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKdcharv:
|
|
e = new IntegerExp(loc, (d_uns32)token.uns64value, Type::tdchar);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKstring:
|
|
case TOKxstring:
|
|
{
|
|
// cat adjacent strings
|
|
utf8_t *s = token.ustring;
|
|
size_t len = token.len;
|
|
unsigned char postfix = token.postfix;
|
|
while (1)
|
|
{
|
|
const Token prev = token;
|
|
nextToken();
|
|
if (token.value == TOKstring ||
|
|
token.value == TOKxstring)
|
|
{
|
|
if (token.postfix)
|
|
{ if (token.postfix != postfix)
|
|
error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
|
|
postfix = token.postfix;
|
|
}
|
|
|
|
deprecation("Implicit string concatenation is deprecated, use %s ~ %s instead",
|
|
prev.toChars(), token.toChars());
|
|
|
|
size_t len1 = len;
|
|
size_t len2 = token.len;
|
|
len = len1 + len2;
|
|
utf8_t *s2 = (utf8_t *)mem.xmalloc((len + 1) * sizeof(utf8_t));
|
|
memcpy(s2, s, len1 * sizeof(utf8_t));
|
|
memcpy(s2 + len1, token.ustring, (len2 + 1) * sizeof(utf8_t));
|
|
s = s2;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
e = new StringExp(loc, s, len, postfix);
|
|
break;
|
|
}
|
|
|
|
case TOKvoid: t = Type::tvoid; goto LabelX;
|
|
case TOKint8: t = Type::tint8; goto LabelX;
|
|
case TOKuns8: t = Type::tuns8; goto LabelX;
|
|
case TOKint16: t = Type::tint16; goto LabelX;
|
|
case TOKuns16: t = Type::tuns16; goto LabelX;
|
|
case TOKint32: t = Type::tint32; goto LabelX;
|
|
case TOKuns32: t = Type::tuns32; goto LabelX;
|
|
case TOKint64: t = Type::tint64; goto LabelX;
|
|
case TOKuns64: t = Type::tuns64; goto LabelX;
|
|
case TOKint128: t = Type::tint128; goto LabelX;
|
|
case TOKuns128: t = Type::tuns128; goto LabelX;
|
|
case TOKfloat32: t = Type::tfloat32; goto LabelX;
|
|
case TOKfloat64: t = Type::tfloat64; goto LabelX;
|
|
case TOKfloat80: t = Type::tfloat80; goto LabelX;
|
|
case TOKimaginary32: t = Type::timaginary32; goto LabelX;
|
|
case TOKimaginary64: t = Type::timaginary64; goto LabelX;
|
|
case TOKimaginary80: t = Type::timaginary80; goto LabelX;
|
|
case TOKcomplex32: t = Type::tcomplex32; goto LabelX;
|
|
case TOKcomplex64: t = Type::tcomplex64; goto LabelX;
|
|
case TOKcomplex80: t = Type::tcomplex80; goto LabelX;
|
|
case TOKbool: t = Type::tbool; goto LabelX;
|
|
case TOKchar: t = Type::tchar; goto LabelX;
|
|
case TOKwchar: t = Type::twchar; goto LabelX;
|
|
case TOKdchar: t = Type::tdchar; goto LabelX;
|
|
LabelX:
|
|
nextToken();
|
|
if (token.value == TOKlparen)
|
|
{
|
|
e = new TypeExp(loc, t);
|
|
e = new CallExp(loc, e, parseArguments());
|
|
break;
|
|
}
|
|
check(TOKdot, t->toChars());
|
|
if (token.value != TOKidentifier)
|
|
{ error("found `%s` when expecting identifier following `%s.`", token.toChars(), t->toChars());
|
|
goto Lerr;
|
|
}
|
|
e = typeDotIdExp(loc, t, token.ident);
|
|
nextToken();
|
|
break;
|
|
|
|
case TOKtypeof:
|
|
{
|
|
t = parseTypeof();
|
|
e = new TypeExp(loc, t);
|
|
break;
|
|
}
|
|
|
|
case TOKvector:
|
|
{
|
|
t = parseVector();
|
|
e = new TypeExp(loc, t);
|
|
break;
|
|
}
|
|
|
|
case TOKtypeid:
|
|
{
|
|
nextToken();
|
|
check(TOKlparen, "typeid");
|
|
RootObject *o = parseTypeOrAssignExp();
|
|
check(TOKrparen);
|
|
e = new TypeidExp(loc, o);
|
|
break;
|
|
}
|
|
|
|
case TOKtraits:
|
|
{ /* __traits(identifier, args...)
|
|
*/
|
|
Identifier *ident;
|
|
Objects *args = NULL;
|
|
|
|
nextToken();
|
|
check(TOKlparen);
|
|
if (token.value != TOKidentifier)
|
|
{ error("__traits(identifier, args...) expected");
|
|
goto Lerr;
|
|
}
|
|
ident = token.ident;
|
|
nextToken();
|
|
if (token.value == TOKcomma)
|
|
args = parseTemplateArgumentList(); // __traits(identifier, args...)
|
|
else
|
|
check(TOKrparen); // __traits(identifier)
|
|
|
|
e = new TraitsExp(loc, ident, args);
|
|
break;
|
|
}
|
|
|
|
case TOKis:
|
|
{
|
|
Type *targ;
|
|
Identifier *ident = NULL;
|
|
Type *tspec = NULL;
|
|
TOK tok = TOKreserved;
|
|
TOK tok2 = TOKreserved;
|
|
TemplateParameters *tpl = NULL;
|
|
|
|
nextToken();
|
|
if (token.value == TOKlparen)
|
|
{
|
|
nextToken();
|
|
if (token.value == TOKidentifier && peekNext() == TOKlparen)
|
|
{
|
|
error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
|
|
nextToken();
|
|
Token *tempTok = peekPastParen(&token);
|
|
memcpy(&token, tempTok, sizeof(Token));
|
|
goto Lerr;
|
|
}
|
|
targ = parseType(&ident);
|
|
if (token.value == TOKcolon || token.value == TOKequal)
|
|
{
|
|
tok = token.value;
|
|
nextToken();
|
|
if (tok == TOKequal &&
|
|
(token.value == TOKstruct ||
|
|
token.value == TOKunion ||
|
|
token.value == TOKclass ||
|
|
token.value == TOKsuper ||
|
|
token.value == TOKenum ||
|
|
token.value == TOKinterface ||
|
|
token.value == TOKmodule ||
|
|
token.value == TOKpackage ||
|
|
token.value == TOKargTypes ||
|
|
token.value == TOKparameters ||
|
|
(token.value == TOKconst && peek(&token)->value == TOKrparen) ||
|
|
(token.value == TOKimmutable && peek(&token)->value == TOKrparen) ||
|
|
(token.value == TOKshared && peek(&token)->value == TOKrparen) ||
|
|
(token.value == TOKwild && peek(&token)->value == TOKrparen) ||
|
|
token.value == TOKfunction ||
|
|
token.value == TOKdelegate ||
|
|
token.value == TOKreturn ||
|
|
(token.value == TOKvector && peek(&token)->value == TOKrparen)))
|
|
{
|
|
tok2 = token.value;
|
|
nextToken();
|
|
}
|
|
else
|
|
{
|
|
tspec = parseType();
|
|
}
|
|
}
|
|
if (tspec)
|
|
{
|
|
if (token.value == TOKcomma)
|
|
tpl = parseTemplateParameterList(1);
|
|
else
|
|
{
|
|
tpl = new TemplateParameters();
|
|
check(TOKrparen);
|
|
}
|
|
}
|
|
else
|
|
check(TOKrparen);
|
|
}
|
|
else
|
|
{
|
|
error("(type identifier : specialization) expected following is");
|
|
goto Lerr;
|
|
}
|
|
e = new IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
|
|
break;
|
|
}
|
|
|
|
case TOKassert:
|
|
{ Expression *msg = NULL;
|
|
|
|
nextToken();
|
|
check(TOKlparen, "assert");
|
|
e = parseAssignExp();
|
|
if (token.value == TOKcomma)
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKrparen)
|
|
{
|
|
msg = parseAssignExp();
|
|
if (token.value == TOKcomma)
|
|
nextToken();
|
|
}
|
|
}
|
|
check(TOKrparen);
|
|
e = new AssertExp(loc, e, msg);
|
|
break;
|
|
}
|
|
|
|
case TOKmixin:
|
|
{
|
|
// https://dlang.org/spec/expression.html#mixin_expressions
|
|
nextToken();
|
|
if (token.value != TOKlparen)
|
|
error("found `%s` when expecting `%s` following %s", token.toChars(), Token::toChars(TOKlparen), "`mixin`");
|
|
e = new CompileExp(loc, parseArguments());
|
|
break;
|
|
}
|
|
|
|
case TOKimport:
|
|
{
|
|
nextToken();
|
|
check(TOKlparen, "import");
|
|
e = parseAssignExp();
|
|
check(TOKrparen);
|
|
e = new ImportExp(loc, e);
|
|
break;
|
|
}
|
|
|
|
case TOKnew:
|
|
e = parseNewExp(NULL);
|
|
break;
|
|
|
|
case TOKref:
|
|
{
|
|
if (peekNext() == TOKlparen)
|
|
{
|
|
Token *tk = peekPastParen(peek(&token));
|
|
if (skipAttributes(tk, &tk) &&
|
|
(tk->value == TOKgoesto || tk->value == TOKlcurly))
|
|
{
|
|
// ref (arguments) => expression
|
|
// ref (arguments) { statements... }
|
|
goto case_delegate;
|
|
}
|
|
}
|
|
nextToken();
|
|
error("found `%s` when expecting function literal following `ref`", token.toChars());
|
|
goto Lerr;
|
|
}
|
|
|
|
case TOKlparen:
|
|
{
|
|
Token *tk = peekPastParen(&token);
|
|
if (skipAttributes(tk, &tk) &&
|
|
(tk->value == TOKgoesto || tk->value == TOKlcurly))
|
|
{
|
|
// (arguments) => expression
|
|
// (arguments) { statements... }
|
|
goto case_delegate;
|
|
}
|
|
|
|
// ( expression )
|
|
nextToken();
|
|
e = parseExpression();
|
|
e->parens = 1;
|
|
check(loc, TOKrparen);
|
|
break;
|
|
}
|
|
|
|
case TOKlbracket:
|
|
{ /* Parse array literals and associative array literals:
|
|
* [ value, value, value ... ]
|
|
* [ key:value, key:value, key:value ... ]
|
|
*/
|
|
Expressions *values = new Expressions();
|
|
Expressions *keys = NULL;
|
|
|
|
nextToken();
|
|
while (token.value != TOKrbracket && token.value != TOKeof)
|
|
{
|
|
e = parseAssignExp();
|
|
if (token.value == TOKcolon && (keys || values->length == 0))
|
|
{ nextToken();
|
|
if (!keys)
|
|
keys = new Expressions();
|
|
keys->push(e);
|
|
e = parseAssignExp();
|
|
}
|
|
else if (keys)
|
|
{ error("`key:value` expected for associative array literal");
|
|
delete keys;
|
|
keys = NULL;
|
|
}
|
|
values->push(e);
|
|
if (token.value == TOKrbracket)
|
|
break;
|
|
check(TOKcomma);
|
|
}
|
|
check(loc, TOKrbracket);
|
|
|
|
if (keys)
|
|
e = new AssocArrayLiteralExp(loc, keys, values);
|
|
else
|
|
e = new ArrayLiteralExp(loc, NULL, values);
|
|
break;
|
|
}
|
|
|
|
case TOKlcurly:
|
|
case TOKfunction:
|
|
case TOKdelegate:
|
|
case_delegate:
|
|
{
|
|
Dsymbol *s = parseFunctionLiteral();
|
|
e = new FuncExp(loc, s);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
error("expression expected, not `%s`", token.toChars());
|
|
Lerr:
|
|
// Anything for e, as long as it's not NULL
|
|
e = new IntegerExp(loc, 0, Type::tint32);
|
|
nextToken();
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parsePostExp(Expression *e)
|
|
{
|
|
Loc loc;
|
|
|
|
while (1)
|
|
{
|
|
loc = token.loc;
|
|
switch (token.value)
|
|
{
|
|
case TOKdot:
|
|
nextToken();
|
|
if (token.value == TOKidentifier)
|
|
{ Identifier *id = token.ident;
|
|
|
|
nextToken();
|
|
if (token.value == TOKnot && peekNext() != TOKis && peekNext() != TOKin)
|
|
{
|
|
Objects *tiargs = parseTemplateArguments();
|
|
e = new DotTemplateInstanceExp(loc, e, id, tiargs);
|
|
}
|
|
else
|
|
e = new DotIdExp(loc, e, id);
|
|
continue;
|
|
}
|
|
else if (token.value == TOKnew)
|
|
{
|
|
e = parseNewExp(e);
|
|
continue;
|
|
}
|
|
else
|
|
error("identifier expected following `.`, not `%s`", token.toChars());
|
|
break;
|
|
|
|
case TOKplusplus:
|
|
e = new PostExp(TOKplusplus, loc, e);
|
|
break;
|
|
|
|
case TOKminusminus:
|
|
e = new PostExp(TOKminusminus, loc, e);
|
|
break;
|
|
|
|
case TOKlparen:
|
|
e = new CallExp(loc, e, parseArguments());
|
|
continue;
|
|
|
|
case TOKlbracket:
|
|
{ // array dereferences:
|
|
// array[index]
|
|
// array[]
|
|
// array[lwr .. upr]
|
|
Expression *index;
|
|
Expression *upr;
|
|
Expressions *arguments = new Expressions();
|
|
|
|
inBrackets++;
|
|
nextToken();
|
|
while (token.value != TOKrbracket && token.value != TOKeof)
|
|
{
|
|
index = parseAssignExp();
|
|
if (token.value == TOKslice)
|
|
{
|
|
// array[..., lwr..upr, ...]
|
|
nextToken();
|
|
upr = parseAssignExp();
|
|
arguments->push(new IntervalExp(loc, index, upr));
|
|
}
|
|
else
|
|
arguments->push(index);
|
|
if (token.value == TOKrbracket)
|
|
break;
|
|
check(TOKcomma);
|
|
}
|
|
check(TOKrbracket);
|
|
inBrackets--;
|
|
e = new ArrayExp(loc, e, arguments);
|
|
continue;
|
|
}
|
|
|
|
default:
|
|
return e;
|
|
}
|
|
nextToken();
|
|
}
|
|
}
|
|
|
|
Expression *Parser::parseUnaryExp()
|
|
{
|
|
Expression *e;
|
|
Loc loc = token.loc;
|
|
|
|
switch (token.value)
|
|
{
|
|
case TOKand:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new AddrExp(loc, e);
|
|
break;
|
|
|
|
case TOKplusplus:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
//e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
|
|
e = new PreExp(TOKpreplusplus, loc, e);
|
|
break;
|
|
|
|
case TOKminusminus:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
//e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
|
|
e = new PreExp(TOKpreminusminus, loc, e);
|
|
break;
|
|
|
|
case TOKmul:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new PtrExp(loc, e);
|
|
break;
|
|
|
|
case TOKmin:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new NegExp(loc, e);
|
|
break;
|
|
|
|
case TOKadd:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new UAddExp(loc, e);
|
|
break;
|
|
|
|
case TOKnot:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new NotExp(loc, e);
|
|
break;
|
|
|
|
case TOKtilde:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new ComExp(loc, e);
|
|
break;
|
|
|
|
case TOKdelete:
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new DeleteExp(loc, e, false);
|
|
break;
|
|
|
|
case TOKcast: // cast(type) expression
|
|
{
|
|
nextToken();
|
|
check(TOKlparen);
|
|
/* Look for cast(), cast(const), cast(immutable),
|
|
* cast(shared), cast(shared const), cast(wild), cast(shared wild)
|
|
*/
|
|
unsigned char m = 0;
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKconst:
|
|
if (peekNext() == TOKlparen)
|
|
break; // const as type constructor
|
|
m |= MODconst; // const as storage class
|
|
nextToken();
|
|
continue;
|
|
|
|
case TOKimmutable:
|
|
if (peekNext() == TOKlparen)
|
|
break;
|
|
m |= MODimmutable;
|
|
nextToken();
|
|
continue;
|
|
|
|
case TOKshared:
|
|
if (peekNext() == TOKlparen)
|
|
break;
|
|
m |= MODshared;
|
|
nextToken();
|
|
continue;
|
|
|
|
case TOKwild:
|
|
if (peekNext() == TOKlparen)
|
|
break;
|
|
m |= MODwild;
|
|
nextToken();
|
|
continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (token.value == TOKrparen)
|
|
{
|
|
nextToken();
|
|
e = parseUnaryExp();
|
|
e = new CastExp(loc, e, m);
|
|
}
|
|
else
|
|
{
|
|
Type *t = parseType(); // cast( type )
|
|
t = t->addMod(m); // cast( const type )
|
|
check(TOKrparen);
|
|
e = parseUnaryExp();
|
|
e = new CastExp(loc, e, t);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TOKwild:
|
|
case TOKshared:
|
|
case TOKconst:
|
|
case TOKimmutable: // immutable(type)(arguments) / immutable(type).init
|
|
{
|
|
StorageClass stc = parseTypeCtor();
|
|
Type *t = parseBasicType();
|
|
t = t->addSTC(stc);
|
|
e = new TypeExp(loc, t);
|
|
if (stc == 0 && token.value == TOKdot)
|
|
{
|
|
nextToken();
|
|
if (token.value != TOKidentifier)
|
|
{
|
|
error("identifier expected following (type).");
|
|
return NULL;
|
|
}
|
|
e = typeDotIdExp(loc, t, token.ident);
|
|
nextToken();
|
|
e = parsePostExp(e);
|
|
break;
|
|
}
|
|
else if (token.value != TOKlparen)
|
|
{
|
|
error("(arguments) expected following %s", t->toChars());
|
|
return e;
|
|
}
|
|
e = new CallExp(loc, e, parseArguments());
|
|
break;
|
|
}
|
|
|
|
|
|
case TOKlparen:
|
|
{ Token *tk;
|
|
|
|
tk = peek(&token);
|
|
#if CCASTSYNTAX
|
|
// If cast
|
|
if (isDeclaration(tk, 0, TOKrparen, &tk))
|
|
{
|
|
tk = peek(tk); // skip over right parenthesis
|
|
switch (tk->value)
|
|
{
|
|
case TOKnot:
|
|
tk = peek(tk);
|
|
if (tk->value == TOKis || tk->value == TOKin) // !is or !in
|
|
break;
|
|
/* fall through */
|
|
|
|
case TOKdot:
|
|
case TOKplusplus:
|
|
case TOKminusminus:
|
|
case TOKdelete:
|
|
case TOKnew:
|
|
case TOKlparen:
|
|
case TOKidentifier:
|
|
case TOKthis:
|
|
case TOKsuper:
|
|
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 TOKfunction:
|
|
case TOKdelegate:
|
|
case TOKtypeof:
|
|
case TOKtraits:
|
|
case TOKvector:
|
|
case TOKfile:
|
|
case TOKfilefullpath:
|
|
case TOKline:
|
|
case TOKmodulestring:
|
|
case TOKfuncstring:
|
|
case TOKprettyfunc:
|
|
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:
|
|
{ // (type) una_exp
|
|
Type *t;
|
|
|
|
nextToken();
|
|
t = parseType();
|
|
check(TOKrparen);
|
|
|
|
// if .identifier
|
|
// or .identifier!( ... )
|
|
if (token.value == TOKdot)
|
|
{
|
|
if (peekNext() != TOKidentifier && peekNext() != TOKnew)
|
|
{
|
|
error("identifier or new keyword expected following (...).");
|
|
return NULL;
|
|
}
|
|
e = new TypeExp(loc, t);
|
|
e->parens = 1;
|
|
e = parsePostExp(e);
|
|
}
|
|
else
|
|
{
|
|
e = parseUnaryExp();
|
|
e = new CastExp(loc, e, t);
|
|
error("C style cast illegal, use %s", e->toChars());
|
|
}
|
|
return e;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
e = parsePrimaryExp();
|
|
e = parsePostExp(e);
|
|
break;
|
|
}
|
|
default:
|
|
e = parsePrimaryExp();
|
|
e = parsePostExp(e);
|
|
break;
|
|
}
|
|
assert(e);
|
|
|
|
// ^^ is right associative and has higher precedence than the unary operators
|
|
while (token.value == TOKpow)
|
|
{
|
|
nextToken();
|
|
Expression *e2 = parseUnaryExp();
|
|
e = new PowExp(loc, e, e2);
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseMulExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseUnaryExp();
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKmul: nextToken(); e2 = parseUnaryExp(); e = new MulExp(loc,e,e2); continue;
|
|
case TOKdiv: nextToken(); e2 = parseUnaryExp(); e = new DivExp(loc,e,e2); continue;
|
|
case TOKmod: nextToken(); e2 = parseUnaryExp(); e = new ModExp(loc,e,e2); continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseAddExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseMulExp();
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKadd: nextToken(); e2 = parseMulExp(); e = new AddExp(loc,e,e2); continue;
|
|
case TOKmin: nextToken(); e2 = parseMulExp(); e = new MinExp(loc,e,e2); continue;
|
|
case TOKtilde: nextToken(); e2 = parseMulExp(); e = new CatExp(loc,e,e2); continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseShiftExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseAddExp();
|
|
while (1)
|
|
{
|
|
switch (token.value)
|
|
{
|
|
case TOKshl: nextToken(); e2 = parseAddExp(); e = new ShlExp(loc,e,e2); continue;
|
|
case TOKshr: nextToken(); e2 = parseAddExp(); e = new ShrExp(loc,e,e2); continue;
|
|
case TOKushr: nextToken(); e2 = parseAddExp(); e = new UshrExp(loc,e,e2); continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseCmpExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Token *t;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseShiftExp();
|
|
TOK op = token.value;
|
|
|
|
switch (op)
|
|
{
|
|
case TOKequal:
|
|
case TOKnotequal:
|
|
nextToken();
|
|
e2 = parseShiftExp();
|
|
e = new EqualExp(op, loc, e, e2);
|
|
break;
|
|
|
|
case TOKis:
|
|
op = TOKidentity;
|
|
goto L1;
|
|
|
|
case TOKnot:
|
|
// Attempt to identify '!is'
|
|
t = peek(&token);
|
|
if (t->value == TOKin)
|
|
{
|
|
nextToken();
|
|
nextToken();
|
|
e2 = parseShiftExp();
|
|
e = new InExp(loc, e, e2);
|
|
e = new NotExp(loc, e);
|
|
break;
|
|
}
|
|
if (t->value != TOKis)
|
|
break;
|
|
nextToken();
|
|
op = TOKnotidentity;
|
|
goto L1;
|
|
|
|
L1:
|
|
nextToken();
|
|
e2 = parseShiftExp();
|
|
e = new IdentityExp(op, loc, e, e2);
|
|
break;
|
|
|
|
case TOKlt:
|
|
case TOKle:
|
|
case TOKgt:
|
|
case TOKge:
|
|
case TOKunord:
|
|
case TOKlg:
|
|
case TOKleg:
|
|
case TOKule:
|
|
case TOKul:
|
|
case TOKuge:
|
|
case TOKug:
|
|
case TOKue:
|
|
nextToken();
|
|
e2 = parseShiftExp();
|
|
e = new CmpExp(op, loc, e, e2);
|
|
break;
|
|
|
|
case TOKin:
|
|
nextToken();
|
|
e2 = parseShiftExp();
|
|
e = new InExp(loc, e, e2);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseAndExp()
|
|
{
|
|
Loc loc = token.loc;
|
|
|
|
Expression *e = parseCmpExp();
|
|
while (token.value == TOKand)
|
|
{
|
|
checkParens(TOKand, e);
|
|
nextToken();
|
|
Expression *e2 = parseCmpExp();
|
|
checkParens(TOKand, e2);
|
|
e = new AndExp(loc,e,e2);
|
|
loc = token.loc;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseXorExp()
|
|
{
|
|
Loc loc = token.loc;
|
|
|
|
Expression *e = parseAndExp();
|
|
while (token.value == TOKxor)
|
|
{
|
|
checkParens(TOKxor, e);
|
|
nextToken();
|
|
Expression *e2 = parseAndExp();
|
|
checkParens(TOKxor, e2);
|
|
e = new XorExp(loc, e, e2);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseOrExp()
|
|
{
|
|
Loc loc = token.loc;
|
|
|
|
Expression *e = parseXorExp();
|
|
while (token.value == TOKor)
|
|
{
|
|
checkParens(TOKor, e);
|
|
nextToken();
|
|
Expression *e2 = parseXorExp();
|
|
checkParens(TOKor, e2);
|
|
e = new OrExp(loc, e, e2);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseAndAndExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseOrExp();
|
|
while (token.value == TOKandand)
|
|
{
|
|
nextToken();
|
|
e2 = parseOrExp();
|
|
e = new LogicalExp(loc, TOKandand, e, e2);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseOrOrExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseAndAndExp();
|
|
while (token.value == TOKoror)
|
|
{
|
|
nextToken();
|
|
e2 = parseAndAndExp();
|
|
e = new LogicalExp(loc, TOKoror, e, e2);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseCondExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e1;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
e = parseOrOrExp();
|
|
if (token.value == TOKquestion)
|
|
{
|
|
nextToken();
|
|
e1 = parseExpression();
|
|
check(TOKcolon);
|
|
e2 = parseCondExp();
|
|
e = new CondExp(loc, e, e1, e2);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseAssignExp()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc;
|
|
|
|
e = parseCondExp();
|
|
while (1)
|
|
{
|
|
loc = token.loc;
|
|
switch (token.value)
|
|
{
|
|
case TOKassign: nextToken(); e2 = parseAssignExp(); e = new AssignExp(loc,e,e2); continue;
|
|
case TOKaddass: nextToken(); e2 = parseAssignExp(); e = new AddAssignExp(loc,e,e2); continue;
|
|
case TOKminass: nextToken(); e2 = parseAssignExp(); e = new MinAssignExp(loc,e,e2); continue;
|
|
case TOKmulass: nextToken(); e2 = parseAssignExp(); e = new MulAssignExp(loc,e,e2); continue;
|
|
case TOKdivass: nextToken(); e2 = parseAssignExp(); e = new DivAssignExp(loc,e,e2); continue;
|
|
case TOKmodass: nextToken(); e2 = parseAssignExp(); e = new ModAssignExp(loc,e,e2); continue;
|
|
case TOKpowass: nextToken(); e2 = parseAssignExp(); e = new PowAssignExp(loc,e,e2); continue;
|
|
case TOKandass: nextToken(); e2 = parseAssignExp(); e = new AndAssignExp(loc,e,e2); continue;
|
|
case TOKorass: nextToken(); e2 = parseAssignExp(); e = new OrAssignExp(loc,e,e2); continue;
|
|
case TOKxorass: nextToken(); e2 = parseAssignExp(); e = new XorAssignExp(loc,e,e2); continue;
|
|
case TOKshlass: nextToken(); e2 = parseAssignExp(); e = new ShlAssignExp(loc,e,e2); continue;
|
|
case TOKshrass: nextToken(); e2 = parseAssignExp(); e = new ShrAssignExp(loc,e,e2); continue;
|
|
case TOKushrass: nextToken(); e2 = parseAssignExp(); e = new UshrAssignExp(loc,e,e2); continue;
|
|
case TOKcatass: nextToken(); e2 = parseAssignExp(); e = new CatAssignExp(loc,e,e2); continue;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *Parser::parseExpression()
|
|
{
|
|
Expression *e;
|
|
Expression *e2;
|
|
Loc loc = token.loc;
|
|
|
|
//printf("Parser::parseExpression() loc = %d\n", loc.linnum);
|
|
e = parseAssignExp();
|
|
while (token.value == TOKcomma)
|
|
{
|
|
nextToken();
|
|
e2 = parseAssignExp();
|
|
e = new CommaExp(loc, e, e2, false);
|
|
loc = token.loc;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
|
|
/*************************
|
|
* Collect argument list.
|
|
* Assume current token is ',', '(' or '['.
|
|
*/
|
|
|
|
Expressions *Parser::parseArguments()
|
|
{ // function call
|
|
Expressions *arguments;
|
|
Expression *arg;
|
|
TOK endtok;
|
|
|
|
arguments = new Expressions();
|
|
if (token.value == TOKlbracket)
|
|
endtok = TOKrbracket;
|
|
else
|
|
endtok = TOKrparen;
|
|
|
|
{
|
|
nextToken();
|
|
while (token.value != endtok && token.value != TOKeof)
|
|
{
|
|
arg = parseAssignExp();
|
|
arguments->push(arg);
|
|
if (token.value == endtok)
|
|
break;
|
|
check(TOKcomma);
|
|
}
|
|
check(endtok);
|
|
}
|
|
return arguments;
|
|
}
|
|
|
|
/*******************************************
|
|
*/
|
|
|
|
Expression *Parser::parseNewExp(Expression *thisexp)
|
|
{
|
|
Type *t;
|
|
Expressions *newargs;
|
|
Expressions *arguments = NULL;
|
|
Loc loc = token.loc;
|
|
|
|
nextToken();
|
|
newargs = NULL;
|
|
if (token.value == TOKlparen)
|
|
{
|
|
newargs = parseArguments();
|
|
}
|
|
|
|
// An anonymous nested class starts with "class"
|
|
if (token.value == TOKclass)
|
|
{
|
|
nextToken();
|
|
if (token.value == TOKlparen)
|
|
arguments = parseArguments();
|
|
|
|
BaseClasses *baseclasses = NULL;
|
|
if (token.value != TOKlcurly)
|
|
baseclasses = parseBaseClasses();
|
|
|
|
Identifier *id = NULL;
|
|
Dsymbols *members = NULL;
|
|
|
|
if (token.value != TOKlcurly)
|
|
{
|
|
error("{ members } expected for anonymous class");
|
|
}
|
|
else
|
|
{
|
|
nextToken();
|
|
members = parseDeclDefs(0);
|
|
if (token.value != TOKrcurly)
|
|
error("class member expected");
|
|
nextToken();
|
|
}
|
|
|
|
ClassDeclaration *cd = new ClassDeclaration(loc, id, baseclasses, members, false);
|
|
Expression *e = new NewAnonClassExp(loc, thisexp, newargs, cd, arguments);
|
|
|
|
return e;
|
|
}
|
|
|
|
StorageClass stc = parseTypeCtor();
|
|
t = parseBasicType(true);
|
|
t = parseBasicType2(t);
|
|
t = t->addSTC(stc);
|
|
if (t->ty == Taarray)
|
|
{
|
|
TypeAArray *taa = (TypeAArray *)t;
|
|
Type *index = taa->index;
|
|
|
|
Expression *edim = typeToExpression(index);
|
|
if (!edim)
|
|
{
|
|
error("need size of rightmost array, not type %s", index->toChars());
|
|
return new NullExp(loc);
|
|
}
|
|
t = new TypeSArray(taa->next, edim);
|
|
}
|
|
else if (t->ty == Tsarray)
|
|
{
|
|
}
|
|
else if (token.value == TOKlparen)
|
|
{
|
|
arguments = parseArguments();
|
|
}
|
|
Expression *e = new NewExp(loc, thisexp, newargs, t, arguments);
|
|
return e;
|
|
}
|
|
|
|
/**********************************************
|
|
*/
|
|
|
|
void Parser::addComment(Dsymbol *s, const utf8_t *blockComment)
|
|
{
|
|
s->addComment(combineComments(blockComment, token.lineComment));
|
|
token.lineComment = NULL;
|
|
}
|
|
|
|
|
|
/**********************************
|
|
* Set operator precedence for each operator.
|
|
*/
|
|
|
|
PREC precedence[TOKMAX];
|
|
|
|
struct PrecedenceInitializer
|
|
{
|
|
PrecedenceInitializer();
|
|
};
|
|
|
|
static PrecedenceInitializer precedenceinitializer;
|
|
|
|
PrecedenceInitializer::PrecedenceInitializer()
|
|
{
|
|
for (size_t i = 0; i < TOKMAX; i++)
|
|
precedence[i] = PREC_zero;
|
|
|
|
precedence[TOKtype] = PREC_expr;
|
|
precedence[TOKerror] = PREC_expr;
|
|
|
|
precedence[TOKtypeof] = PREC_primary;
|
|
precedence[TOKmixin] = PREC_primary;
|
|
precedence[TOKimport] = PREC_primary;
|
|
|
|
precedence[TOKdotvar] = PREC_primary;
|
|
precedence[TOKscope] = PREC_primary;
|
|
precedence[TOKidentifier] = PREC_primary;
|
|
precedence[TOKthis] = PREC_primary;
|
|
precedence[TOKsuper] = PREC_primary;
|
|
precedence[TOKint64] = PREC_primary;
|
|
precedence[TOKfloat64] = PREC_primary;
|
|
precedence[TOKcomplex80] = PREC_primary;
|
|
precedence[TOKnull] = PREC_primary;
|
|
precedence[TOKstring] = PREC_primary;
|
|
precedence[TOKarrayliteral] = PREC_primary;
|
|
precedence[TOKassocarrayliteral] = PREC_primary;
|
|
precedence[TOKclassreference] = PREC_primary;
|
|
precedence[TOKfile] = PREC_primary;
|
|
precedence[TOKfilefullpath] = PREC_primary;
|
|
precedence[TOKline] = PREC_primary;
|
|
precedence[TOKmodulestring] = PREC_primary;
|
|
precedence[TOKfuncstring] = PREC_primary;
|
|
precedence[TOKprettyfunc] = PREC_primary;
|
|
precedence[TOKtypeid] = PREC_primary;
|
|
precedence[TOKis] = PREC_primary;
|
|
precedence[TOKassert] = PREC_primary;
|
|
precedence[TOKhalt] = PREC_primary;
|
|
precedence[TOKtemplate] = PREC_primary;
|
|
precedence[TOKdsymbol] = PREC_primary;
|
|
precedence[TOKfunction] = PREC_primary;
|
|
precedence[TOKvar] = PREC_primary;
|
|
precedence[TOKsymoff] = PREC_primary;
|
|
precedence[TOKstructliteral] = PREC_primary;
|
|
precedence[TOKarraylength] = PREC_primary;
|
|
precedence[TOKdelegateptr] = PREC_primary;
|
|
precedence[TOKdelegatefuncptr] = PREC_primary;
|
|
precedence[TOKremove] = PREC_primary;
|
|
precedence[TOKtuple] = PREC_primary;
|
|
precedence[TOKtraits] = PREC_primary;
|
|
precedence[TOKdefault] = PREC_primary;
|
|
precedence[TOKoverloadset] = PREC_primary;
|
|
precedence[TOKvoid] = PREC_primary;
|
|
precedence[TOKvectorarray] = PREC_primary;
|
|
|
|
// post
|
|
precedence[TOKdotti] = PREC_primary;
|
|
precedence[TOKdotid] = PREC_primary;
|
|
precedence[TOKdottd] = PREC_primary;
|
|
precedence[TOKdot] = PREC_primary;
|
|
precedence[TOKdottype] = PREC_primary;
|
|
// precedence[TOKarrow] = PREC_primary;
|
|
precedence[TOKplusplus] = PREC_primary;
|
|
precedence[TOKminusminus] = PREC_primary;
|
|
precedence[TOKpreplusplus] = PREC_primary;
|
|
precedence[TOKpreminusminus] = PREC_primary;
|
|
precedence[TOKcall] = PREC_primary;
|
|
precedence[TOKslice] = PREC_primary;
|
|
precedence[TOKarray] = PREC_primary;
|
|
precedence[TOKindex] = PREC_primary;
|
|
|
|
precedence[TOKdelegate] = PREC_unary;
|
|
precedence[TOKaddress] = PREC_unary;
|
|
precedence[TOKstar] = PREC_unary;
|
|
precedence[TOKneg] = PREC_unary;
|
|
precedence[TOKuadd] = PREC_unary;
|
|
precedence[TOKnot] = PREC_unary;
|
|
precedence[TOKtilde] = PREC_unary;
|
|
precedence[TOKdelete] = PREC_unary;
|
|
precedence[TOKnew] = PREC_unary;
|
|
precedence[TOKnewanonclass] = PREC_unary;
|
|
precedence[TOKcast] = PREC_unary;
|
|
|
|
precedence[TOKvector] = PREC_unary;
|
|
precedence[TOKpow] = PREC_pow;
|
|
|
|
precedence[TOKmul] = PREC_mul;
|
|
precedence[TOKdiv] = PREC_mul;
|
|
precedence[TOKmod] = PREC_mul;
|
|
|
|
precedence[TOKadd] = PREC_add;
|
|
precedence[TOKmin] = PREC_add;
|
|
precedence[TOKcat] = PREC_add;
|
|
|
|
precedence[TOKshl] = PREC_shift;
|
|
precedence[TOKshr] = PREC_shift;
|
|
precedence[TOKushr] = PREC_shift;
|
|
|
|
precedence[TOKlt] = PREC_rel;
|
|
precedence[TOKle] = PREC_rel;
|
|
precedence[TOKgt] = PREC_rel;
|
|
precedence[TOKge] = PREC_rel;
|
|
precedence[TOKunord] = PREC_rel;
|
|
precedence[TOKlg] = PREC_rel;
|
|
precedence[TOKleg] = PREC_rel;
|
|
precedence[TOKule] = PREC_rel;
|
|
precedence[TOKul] = PREC_rel;
|
|
precedence[TOKuge] = PREC_rel;
|
|
precedence[TOKug] = PREC_rel;
|
|
precedence[TOKue] = PREC_rel;
|
|
precedence[TOKin] = PREC_rel;
|
|
|
|
/* Note that we changed precedence, so that < and != have the same
|
|
* precedence. This change is in the parser, too.
|
|
*/
|
|
precedence[TOKequal] = PREC_rel;
|
|
precedence[TOKnotequal] = PREC_rel;
|
|
precedence[TOKidentity] = PREC_rel;
|
|
precedence[TOKnotidentity] = PREC_rel;
|
|
|
|
precedence[TOKand] = PREC_and;
|
|
|
|
precedence[TOKxor] = PREC_xor;
|
|
|
|
precedence[TOKor] = PREC_or;
|
|
|
|
precedence[TOKandand] = PREC_andand;
|
|
|
|
precedence[TOKoror] = PREC_oror;
|
|
|
|
precedence[TOKquestion] = PREC_cond;
|
|
|
|
precedence[TOKassign] = PREC_assign;
|
|
precedence[TOKconstruct] = PREC_assign;
|
|
precedence[TOKblit] = PREC_assign;
|
|
precedence[TOKaddass] = PREC_assign;
|
|
precedence[TOKminass] = PREC_assign;
|
|
precedence[TOKcatass] = PREC_assign;
|
|
precedence[TOKmulass] = PREC_assign;
|
|
precedence[TOKdivass] = PREC_assign;
|
|
precedence[TOKmodass] = PREC_assign;
|
|
precedence[TOKpowass] = PREC_assign;
|
|
precedence[TOKshlass] = PREC_assign;
|
|
precedence[TOKshrass] = PREC_assign;
|
|
precedence[TOKushrass] = PREC_assign;
|
|
precedence[TOKandass] = PREC_assign;
|
|
precedence[TOKorass] = PREC_assign;
|
|
precedence[TOKxorass] = PREC_assign;
|
|
|
|
precedence[TOKcomma] = PREC_expr;
|
|
precedence[TOKdeclaration] = PREC_expr;
|
|
|
|
precedence[TOKinterval] = PREC_assign;
|
|
}
|