| |
| /* 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; |
| } |