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