// Compiler implementation of the D programming language // Copyright (c) 1999-2008 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. // This is the D parser #include #include #include "mem.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" // How multiple declarations are parsed. // If 1, treat as C. // If 0, treat: // int *p, i; // as: // int* p; // int* i; #define CDECLSYNTAX 0 // 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, unsigned char *base, unsigned length, int doDocComment) : Lexer(module, base, 0, length, doDocComment, 0) { //printf("Parser::Parser()\n"); md = NULL; linkage = LINKd; endloc = 0; inBrackets = 0; //nextToken(); // start up the scanner } Array *Parser::parseModule() { Array *decldefs; // ModuleDeclation leads off if (token.value == TOKmodule) { unsigned char *comment = token.blockComment; bool safe = FALSE; nextToken(); if (token.value == TOKlparen) { nextToken(); if (token.value != TOKidentifier) { error("module (safe) identifier expected"); goto Lerr; } Identifier *id = token.ident; if (id == Id::system) safe = TRUE; else error("(safe) expected, not %s", id->toChars()); nextToken(); check(TOKrparen); } if (token.value != TOKidentifier) { error("Identifier expected following module"); goto Lerr; } else { Array *a = NULL; Identifier *id; id = token.ident; while (nextToken() == TOKdot) { if (!a) a = new Array(); a->push(id); nextToken(); if (token.value != TOKidentifier) { error("Identifier expected following package"); goto Lerr; } id = token.ident; } md = new ModuleDeclaration(a, id, safe); 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("unrecognized declaration"); goto Lerr; } return decldefs; Lerr: while (token.value != TOKsemicolon && token.value != TOKeof) nextToken(); nextToken(); return new Array(); } Array *Parser::parseDeclDefs(int once) { Dsymbol *s; Array *decldefs; Array *a; Array *aelse; enum PROT prot; enum STC stc; unsigned storageClass; Condition *condition; unsigned char *comment; //printf("Parser::parseDeclDefs()\n"); decldefs = new Array(); do { comment = token.blockComment; storageClass = STCundefined; 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 TOKstruct: case TOKunion: case TOKclass: case TOKinterface: s = parseAggregate(); break; case TOKimport: s = parseImport(decldefs, 0); break; case TOKtemplate: s = (Dsymbol *)parseTemplateDeclaration(); break; case TOKmixin: { Loc loc = this->loc; if (peek(&token)->value == TOKlparen) { // mixin(string) nextToken(); check(TOKlparen, "mixin"); Expression *e = parseAssignExp(); check(TOKrparen); check(TOKsemicolon); s = new CompileDeclaration(loc, e); break; } s = parseMixin(); break; } CASE_BASIC_TYPES: case TOKalias: case TOKtypedef: case TOKidentifier: case TOKtypeof: case TOKdot: Ldeclaration: a = parseDeclarations(STCundefined); decldefs->append(a); continue; case TOKthis: s = parseCtor(); break; case TOKassign: s = parsePostBlit(); break; case TOKtilde: s = parseDtor(); break; case TOKinvariant: { Token *t; t = peek(&token); if (t->value == TOKlparen) { if (peek(t)->value == TOKrparen) // invariant() forms start of class invariant s = parseInvariant(); else // invariant(type) goto Ldeclaration; } else { stc = STCinvariant; goto Lstc; } break; } case TOKunittest: s = parseUnitTest(); break; case TOKnew: s = parseNew(); break; case TOKdelete: s = parseDelete(); break; case TOKeof: case TOKrcurly: return decldefs; case TOKstatic: nextToken(); if (token.value == TOKthis) s = parseStaticCtor(); else if (token.value == TOKtilde) s = parseStaticDtor(); else if (token.value == TOKassert) s = parseStaticAssert(); else if (token.value == TOKif) { condition = parseStaticIfCondition(); a = parseBlock(); aelse = NULL; if (token.value == TOKelse) { nextToken(); aelse = parseBlock(); } s = new StaticIfDeclaration(condition, a, aelse); break; } else if (token.value == TOKimport) { s = parseImport(decldefs, 1); } else { stc = STCstatic; goto Lstc2; } break; case TOKconst: if (peek(&token)->value == TOKlparen) goto Ldeclaration; stc = STCconst; goto Lstc; case TOKimmutable: if (peek(&token)->value == TOKlparen) goto Ldeclaration; stc = STCinvariant; 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 TOKdeprecated: stc = STCdeprecated; goto Lstc; case TOKnothrow: stc = STCnothrow; goto Lstc; case TOKpure: stc = STCpure; goto Lstc; case TOKref: stc = STCref; goto Lstc; case TOKtls: stc = STCtls; goto Lstc; //case TOKmanifest: stc = STCmanifest; goto Lstc; Lstc: if (storageClass & stc) error("redundant storage class %s", Token::toChars(token.value)); { unsigned u = storageClass | stc; u &= STCconst | STCinvariant | STCmanifest; if (u & (u - 1)) error("conflicting storage class %s", Token::toChars(token.value)); } nextToken(); Lstc2: storageClass |= stc; switch (token.value) { case TOKconst: case TOKinvariant: case TOKimmutable: // If followed by a (, it is not a storage class if (peek(&token)->value == TOKlparen) break; if (token.value == TOKconst) stc = STCconst; else stc = STCinvariant; 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 TOKdeprecated: stc = STCdeprecated; goto Lstc; case TOKnothrow: stc = STCnothrow; goto Lstc; case TOKpure: stc = STCpure; goto Lstc; case TOKref: stc = STCref; goto Lstc; case TOKtls: stc = STCtls; goto Lstc; //case TOKmanifest: stc = STCmanifest; goto Lstc; default: break; } /* Look for auto initializers: * storage_class identifier = initializer; */ if (token.value == TOKidentifier && peek(&token)->value == TOKassign) { a = parseAutoDeclarations(storageClass, comment); decldefs->append(a); continue; } /* Look for return type inference for template functions. */ Token *tk; if (token.value == TOKidentifier && (tk = peek(&token))->value == TOKlparen && skipParens(tk, &tk) && peek(tk)->value == TOKlparen) { a = parseDeclarations(storageClass); decldefs->append(a); continue; } a = parseBlock(); s = new StorageClassDeclaration(storageClass, a); break; case TOKextern: if (peek(&token)->value != TOKlparen) { stc = STCextern; goto Lstc; } { enum LINK linksave = linkage; linkage = parseLinkage(); a = parseBlock(); s = new LinkDeclaration(linkage, a); linkage = linksave; break; } case TOKprivate: prot = PROTprivate; goto Lprot; case TOKpackage: prot = PROTpackage; goto Lprot; case TOKprotected: prot = PROTprotected; goto Lprot; case TOKpublic: prot = PROTpublic; goto Lprot; case TOKexport: prot = PROTexport; goto Lprot; Lprot: nextToken(); switch (token.value) { case TOKprivate: case TOKpackage: case TOKprotected: case TOKpublic: case TOKexport: error("redundant protection attribute"); break; } a = parseBlock(); s = new ProtDeclaration(prot, a); break; case TOKalign: { unsigned n; // LDC better align code locations Loc alignloc = loc; s = NULL; nextToken(); if (token.value == TOKlparen) { nextToken(); if (token.value == TOKint32v) n = (unsigned)token.uns64value; else { error("integer expected, not %s", token.toChars()); n = 1; } nextToken(); check(TOKrparen); } else n = global.structalign; // default a = parseBlock(); s = new AlignDeclaration(alignloc, n, a); break; } case TOKpragma: { Identifier *ident; Expressions *args = NULL; 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) a = NULL; else a = parseBlock(); s = new PragmaDeclaration(loc, ident, args, a); break; } case TOKdebug: nextToken(); if (token.value == TOKassign) { nextToken(); if (token.value == TOKidentifier) s = new DebugSymbol(loc, token.ident); else if (token.value == TOKint32v) s = new DebugSymbol(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(loc, token.ident); else if (token.value == TOKint32v) s = new VersionSymbol(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: a = parseBlock(); aelse = NULL; if (token.value == TOKelse) { nextToken(); aelse = parseBlock(); } s = new ConditionalDeclaration(condition, a, aelse); break; case TOKsemicolon: // 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) { decldefs->push(s); addComment(s, comment); } } while (!once); return decldefs; } /******************************************** * Parse declarations after an align, protection, or extern decl. */ Array *Parser::parseBlock() { Array *a = NULL; Dsymbol *s; //printf("parseBlock()\n"); switch (token.value) { case TOKsemicolon: error("declaration expected following attribute, not ';'"); nextToken(); break; case TOKlcurly: nextToken(); a = parseDeclDefs(0); if (token.value != TOKrcurly) { /* { */ error("matching '}' expected, not %s", token.toChars()); } else nextToken(); break; case TOKcolon: nextToken(); #if 0 a = NULL; #else a = parseDeclDefs(0); // grab declarations up to closing curly bracket #endif break; default: a = parseDeclDefs(1); break; } return a; } /********************************** * Parse a static assertion. */ StaticAssert *Parser::parseStaticAssert() { Loc loc = this->loc; Expression *exp; Expression *msg = NULL; //printf("parseStaticAssert()\n"); nextToken(); check(TOKlparen); exp = parseAssignExp(); if (token.value == TOKcomma) { nextToken(); msg = parseAssignExp(); } check(TOKrparen); check(TOKsemicolon); return new StaticAssert(loc, exp, msg); } /*********************************** * Parse typeof(expression). * Current token is on the 'typeof'. */ #if DMDV2 TypeQualified *Parser::parseTypeof() { TypeQualified *t; Loc loc = this->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; } #endif /*********************************** * Parse extern (linkage) * The parser is on the 'extern' token. */ enum LINK Parser::parseLinkage() { enum LINK link = LINKdefault; nextToken(); assert(token.value == TOKlparen); nextToken(); if (token.value == TOKidentifier) { Identifier *id = token.ident; nextToken(); if (id == Id::Windows) link = LINKwindows; else if (id == Id::Pascal) link = LINKpascal; else if (id == Id::D) link = LINKd; else if (id == Id::C) { link = LINKc; if (token.value == TOKplusplus) { link = LINKcpp; nextToken(); } } else if (id == Id::System) { // LDC we configure target at runtime if (global.params.os == OSWindows) link = LINKwindows; else link = LINKc; } else { error("valid linkage identifiers are D, C, C++, Pascal, Windows, System"); link = LINKd; } } else { link = LINKd; // default } check(TOKrparen); return link; } /************************************** * 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) 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(); if (token.value == TOKidentifier) id = token.ident; else if (token.value == TOKint32v) level = (unsigned)token.uns64value; #if DMDV2 /* Allow: * version (unittest) * even though unittest is a keyword */ else if (token.value == TOKunittest) id = Lexer::idPool(Token::toChars(TOKunittest)); #endif 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 */ Condition *Parser::parseStaticIfCondition() { Expression *exp; Condition *condition; Array *aif; Array *aelse; Loc loc = this->loc; 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(arguments) { body } * or postblit: * this(this) { body } * Current token is 'this'. */ FuncDeclaration *Parser::parseCtor() { Loc loc = this->loc; nextToken(); if (token.value == TOKlparen && peek(&token)->value == TOKthis) { // this(this) { ... } nextToken(); nextToken(); check(TOKrparen); PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0); parseContracts(f); return f; } int varargs; Arguments *arguments = parseParameters(&varargs); CtorDeclaration *f = new CtorDeclaration(loc, 0, arguments, varargs); parseContracts(f); return f; } /***************************************** * Parse a postblit definition: * =this() { body } * Current token is '='. */ PostBlitDeclaration *Parser::parsePostBlit() { Loc loc = this->loc; nextToken(); check(TOKthis); check(TOKlparen); check(TOKrparen); PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0); parseContracts(f); return f; } /***************************************** * Parse a destructor definition: * ~this() { body } * Current token is '~'. */ DtorDeclaration *Parser::parseDtor() { DtorDeclaration *f; Loc loc = this->loc; nextToken(); check(TOKthis); check(TOKlparen); check(TOKrparen); f = new DtorDeclaration(loc, 0); parseContracts(f); return f; } /***************************************** * Parse a static constructor definition: * static this() { body } * Current token is 'this'. */ StaticCtorDeclaration *Parser::parseStaticCtor() { StaticCtorDeclaration *f; Loc loc = this->loc; nextToken(); check(TOKlparen); check(TOKrparen); f = new StaticCtorDeclaration(loc, 0); parseContracts(f); return f; } /***************************************** * Parse a static destructor definition: * static ~this() { body } * Current token is '~'. */ StaticDtorDeclaration *Parser::parseStaticDtor() { StaticDtorDeclaration *f; Loc loc = this->loc; nextToken(); check(TOKthis); check(TOKlparen); check(TOKrparen); f = new StaticDtorDeclaration(loc, 0); parseContracts(f); return f; } /***************************************** * Parse an invariant definition: * invariant() { body } * Current token is 'invariant'. */ InvariantDeclaration *Parser::parseInvariant() { InvariantDeclaration *f; Loc loc = this->loc; nextToken(); if (token.value == TOKlparen) // optional () { nextToken(); check(TOKrparen); } f = new InvariantDeclaration(loc, 0); f->fbody = parseStatement(PScurly); return f; } /***************************************** * Parse a unittest definition: * unittest { body } * Current token is 'unittest'. */ UnitTestDeclaration *Parser::parseUnitTest() { UnitTestDeclaration *f; Statement *body; Loc loc = this->loc; nextToken(); body = parseStatement(PScurly); f = new UnitTestDeclaration(loc, this->loc); f->fbody = body; return f; } /***************************************** * Parse a new definition: * new(arguments) { body } * Current token is 'new'. */ NewDeclaration *Parser::parseNew() { NewDeclaration *f; Arguments *arguments; int varargs; Loc loc = this->loc; nextToken(); arguments = parseParameters(&varargs); f = new NewDeclaration(loc, 0, arguments, varargs); parseContracts(f); return f; } /***************************************** * Parse a delete definition: * delete(arguments) { body } * Current token is 'delete'. */ DeleteDeclaration *Parser::parseDelete() { DeleteDeclaration *f; Arguments *arguments; int varargs; Loc loc = this->loc; nextToken(); arguments = parseParameters(&varargs); if (varargs) error("... not allowed in delete function parameter list"); f = new DeleteDeclaration(loc, 0, arguments); parseContracts(f); return f; } /********************************************** * Parse parameter list. */ Arguments *Parser::parseParameters(int *pvarargs) { Arguments *arguments = new Arguments(); int varargs = 0; int hasdefault = 0; check(TOKlparen); while (1) { Type *tb; Identifier *ai = NULL; Type *at; Argument *a; unsigned storageClass = 0; unsigned stc; Expression *ae; for (;1; nextToken()) { switch (token.value) { case TOKrparen: break; case TOKdotdotdot: varargs = 1; nextToken(); break; case TOKconst: if (peek(&token)->value == TOKlparen) goto Ldefault; stc = STCconst; goto L2; case TOKinvariant: case TOKimmutable: if (peek(&token)->value == TOKlparen) goto Ldefault; stc = STCinvariant; goto L2; case TOKin: stc = STCin; goto L2; case TOKout: stc = STCout; goto L2; case TOKinout: case TOKref: stc = STCref; goto L2; case TOKlazy: stc = STClazy; goto L2; case TOKscope: stc = STCscope; goto L2; case TOKfinal: stc = STCfinal; goto L2; L2: if (storageClass & stc || (storageClass & STCin && stc & (STCconst | STCscope)) || (stc & STCin && storageClass & (STCconst | STCscope)) ) error("redundant storage class %s", Token::toChars(token.value)); storageClass |= stc; { unsigned u = storageClass & (STCconst | STCinvariant); if (u & (u - 1)) error("conflicting storage class %s", Token::toChars(token.value)); } continue; #if 0 case TOKstatic: stc = STCstatic; goto L2; case TOKauto: storageClass = STCauto; goto L4; case TOKalias: storageClass = STCalias; goto L4; L4: nextToken(); if (token.value == TOKidentifier) { ai = token.ident; nextToken(); } else ai = NULL; at = NULL; // no type ae = NULL; // no default argument if (token.value == TOKassign) // = defaultArg { nextToken(); ae = parseDefaultInitExp(); hasdefault = 1; } else { if (hasdefault) error("default argument expected for alias %s", ai ? ai->toChars() : ""); } goto L3; #endif default: Ldefault: stc = storageClass & (STCin | STCout | STCref | STClazy); if (stc & (stc - 1)) // if stc is not a power of 2 error("incompatible parameter storage classes"); if ((storageClass & (STCconst | STCout)) == (STCconst | STCout)) error("out cannot be const"); if ((storageClass & (STCinvariant | STCout)) == (STCinvariant | STCout)) error("out cannot be invariant"); if ((storageClass & STCscope) && (storageClass & (STCref | STCout))) error("scope cannot be ref or out"); at = parseType(&ai); ae = NULL; if (token.value == TOKassign) // = defaultArg { nextToken(); ae = parseDefaultInitExp(); hasdefault = 1; } else { if (hasdefault) error("default argument expected for %s", ai ? ai->toChars() : at->toChars()); } if (token.value == TOKdotdotdot) { /* This is: * at ai ... */ if (storageClass & (STCout | STCref)) error("variadic argument cannot be out or ref"); varargs = 2; a = new Argument(storageClass, at, ai, ae); arguments->push(a); nextToken(); break; } L3: a = new Argument(storageClass, at, ai, ae); arguments->push(a); if (token.value == TOKcomma) { nextToken(); goto L1; } break; } break; } break; L1: ; } check(TOKrparen); *pvarargs = varargs; return arguments; } /************************************* */ EnumDeclaration *Parser::parseEnum() { EnumDeclaration *e; Identifier *id; Type *memtype; Loc loc = this->loc; //printf("Parser::parseEnum()\n"); nextToken(); if (token.value == TOKidentifier) { id = token.ident; nextToken(); } else id = NULL; if (token.value == TOKcolon) { nextToken(); memtype = parseBasicType(); memtype = parseDeclarator(memtype, NULL, NULL); } else memtype = NULL; e = new EnumDeclaration(loc, id, memtype); if (token.value == TOKsemicolon && id) nextToken(); else if (token.value == TOKlcurly) { //printf("enum definition\n"); e->members = new Array(); nextToken(); unsigned char *comment = token.blockComment; while (token.value != TOKrcurly) { /* Can take the following forms: * 1. ident * 2. ident = value * 3. type ident = value */ loc = this->loc; Type *type = NULL; Identifier *ident; Token *tp = peek(&token); if (token.value == TOKidentifier && (tp->value == TOKassign || tp->value == TOKcomma || tp->value == TOKrcurly)) { ident = token.ident; type = NULL; nextToken(); } else { type = parseType(&ident, NULL); if (id || memtype) error("type only allowed if anonymous enum and no enum type"); } Expression *value; if (token.value == TOKassign) { nextToken(); value = parseAssignExp(); } else { value = NULL; if (type) error("if type, there must be an initializer"); } EnumMember *em = new EnumMember(loc, ident, value, type); e->members->push(em); if (token.value == TOKrcurly) ; else { addComment(em, comment); comment = NULL; check(TOKcomma); } addComment(em, comment); comment = token.blockComment; } 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; enum TOK tok; Identifier *id; TemplateParameters *tpl = NULL; Expression *constraint = NULL; //printf("Parser::parseAggregate()\n"); tok = token.value; 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(); } } Loc loc = this->loc; switch (tok) { case TOKclass: case TOKinterface: { if (!id) error("anonymous classes not allowed"); // Collect base class(es) BaseClasses *baseclasses = NULL; if (token.value == TOKcolon) { nextToken(); baseclasses = parseBaseClasses(); if (token.value != TOKlcurly) error("members expected"); } if (tok == TOKclass) a = new ClassDeclaration(loc, id, baseclasses); else a = new InterfaceDeclaration(loc, id, baseclasses); break; } case TOKstruct: if (id) a = new StructDeclaration(loc, id); 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) { //printf("aggregate definition\n"); nextToken(); Array *decl = parseDeclDefs(0); if (token.value != TOKrcurly) error("} expected following member declarations in aggregate"); nextToken(); if (anon) { /* Anonymous structs/unions are more like attributes. */ return new AnonDeclaration(loc, anon - 1, decl); } else a->members = decl; } else { error("{ } expected following aggregate declaration"); a = new StructDeclaration(loc, NULL); } if (tpl) { // Wrap a template around the aggregate declaration Array *decldefs = new Array(); 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()) { enum PROT protection = PROTpublic; switch (token.value) { case TOKprivate: protection = PROTprivate; nextToken(); break; case TOKpackage: protection = PROTpackage; nextToken(); break; case TOKprotected: protection = PROTprotected; nextToken(); break; case TOKpublic: protection = PROTpublic; nextToken(); break; } if (token.value == TOKidentifier) { BaseClass *b = new BaseClass(parseBasicType(), protection); baseclasses->push(b); if (token.value != TOKcomma) break; } else { error("base classes expected instead of %s", token.toChars()); return NULL; } } return baseclasses; } /************************************** * Parse constraint. * Constraint is of the form: * if ( ConstraintExpression ) */ #if DMDV2 Expression *Parser::parseConstraint() { Expression *e = NULL; if (token.value == TOKif) { nextToken(); // skip over 'if' check(TOKlparen); e = parseExpression(); check(TOKrparen); } return e; } #endif /************************************** * Parse a TemplateDeclaration. */ TemplateDeclaration *Parser::parseTemplateDeclaration() { TemplateDeclaration *tempdecl; Identifier *id; TemplateParameters *tpl; Array *decldefs; Expression *constraint = NULL; Loc loc = this->loc; nextToken(); if (token.value != TOKidentifier) { error("TemplateIdentifier 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 { nextToken(); decldefs = parseDeclDefs(0); if (token.value != TOKrcurly) { error("template member expected"); goto Lerr; } nextToken(); } tempdecl = new TemplateDeclaration(loc, id, tpl, constraint, decldefs); 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 (1) { TemplateParameter *tp; 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(); 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(); } Object *spec = NULL; if (token.value == TOKcolon) // : Type { nextToken(); if (isDeclaration(&token, 0, TOKreserved, NULL)) spec = parseType(); else spec = parseCondExp(); } Object *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; } 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; tp_ident = token.ident; nextToken(); nextToken(); tp = new TemplateTupleParameter(loc, tp_ident); } #if DMDV2 else if (token.value == TOKthis) { // ThisParameter nextToken(); if (token.value != TOKidentifier) { error("identifier expected for template this parameter"); goto Lerr; } 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); } #endif else { // ValueParameter tp_valtype = parseType(&tp_ident); if (!tp_ident) { error("identifier expected for template value parameter"); tp_ident = new Identifier("error", TOKidentifier); } 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; Type *tqual; Objects *tiargs; Array *idents; //printf("parseMixin()\n"); nextToken(); 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(); } idents = new Array(); while (1) { tiargs = NULL; if (token.value == TOKnot) { nextToken(); if (token.value == TOKlparen) tiargs = parseTemplateArgumentList(); else tiargs = parseTemplateArgument(); } if (token.value != TOKdot) break; if (tiargs) { TemplateInstance *tempinst = new TemplateInstance(loc, id); tempinst->tiargs = tiargs; id = (Identifier *)tempinst; tiargs = NULL; } idents->push(id); nextToken(); if (token.value != TOKidentifier) { error("identifier expected following '.' instead of '%s'", token.toChars()); break; } id = token.ident; nextToken(); } idents->push(id); if (token.value == TOKidentifier) { id = token.ident; nextToken(); } else id = NULL; tm = new TemplateMixin(loc, id, tqual, idents, tiargs); if (token.value != TOKsemicolon) error("';' expected after mixin"); nextToken(); return tm; } /****************************************** * Parse template argument list. * Input: * current token is opening '(' * Output: * current token is one after closing ')' */ Objects *Parser::parseTemplateArgumentList() { //printf("Parser::parseTemplateArgumentList()\n"); if (token.value != TOKlparen && token.value != TOKlcurly) { error("!(TemplateArgumentList) expected following TemplateIdentifier"); return new Objects(); } return parseTemplateArgumentList2(); } Objects *Parser::parseTemplateArgumentList2() { Objects *tiargs = new Objects(); enum TOK endtok = TOKrparen; nextToken(); // Get TemplateArgumentList if (token.value != endtok) { while (1) { // See if it is an Expression or a Type if (isDeclaration(&token, 0, TOKreserved, NULL)) { // Template argument is a type Type *ta = parseType(); tiargs->push(ta); } else { // Template argument is an expression Expression *ea = parseAssignExp(); tiargs->push(ea); } if (token.value != TOKcomma) break; nextToken(); } } check(endtok, "template argument list"); return tiargs; } /***************************** * Parse single template argument, to support the syntax: * foo!arg * Input: * current token is the arg */ Objects *Parser::parseTemplateArgument() { //printf("parseTemplateArgument()\n"); Objects *tiargs = new Objects(); Type *ta; switch (token.value) { case TOKidentifier: ta = new TypeIdentifier(loc, token.ident); goto LabelX; CASE_BASIC_TYPES_X(ta): tiargs->push(ta); nextToken(); break; case TOKint32v: case TOKuns32v: case TOKint64v: case TOKuns64v: 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 TOKfile: case TOKline: { // Template argument is an expression Expression *ea = parsePrimaryExp(); tiargs->push(ea); break; } default: error("template argument expected following !"); break; } if (token.value == TOKnot) error("multiple ! arguments are not allowed"); return tiargs; } Import *Parser::parseImport(Array *decldefs, int isstatic) { Import *s; Identifier *id; Identifier *aliasid = NULL; Array *a; Loc loc; //printf("Parser::parseImport()\n"); do { L1: nextToken(); if (token.value != TOKidentifier) { error("Identifier expected following import"); break; } loc = this->loc; a = NULL; id = token.ident; nextToken(); if (!aliasid && token.value == TOKassign) { aliasid = id; goto L1; } while (token.value == TOKdot) { if (!a) a = new Array(); a->push(id); nextToken(); if (token.value != TOKidentifier) { error("Identifier expected following package"); break; } id = token.ident; nextToken(); } s = new Import(loc, a, token.ident, aliasid, isstatic); decldefs->push(s); /* Look for * : alias=name, alias=name; * syntax. */ if (token.value == TOKcolon) { do { Identifier *name; Identifier *alias; nextToken(); if (token.value != TOKidentifier) { error("Identifier expected following :"); break; } alias = token.ident; 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 NULL; } Type *Parser::parseType(Identifier **pident, TemplateParameters **tpl) { Type *t; if (token.value == TOKconst && peek(&token)->value != TOKlparen) { nextToken(); /* const type */ t = parseType(pident, tpl); t = t->makeConst(); return t; } else if ((token.value == TOKinvariant || token.value == TOKimmutable) && peek(&token)->value != TOKlparen) { nextToken(); /* invariant type */ t = parseType(pident, tpl); t = t->makeInvariant(); return t; } else t = parseBasicType(); t = parseDeclarator(t, pident, tpl); return t; } Type *Parser::parseBasicType() { Type *t; Identifier *id; TypeQualified *tid; //printf("parseBasicType()\n"); switch (token.value) { CASE_BASIC_TYPES_X(t): nextToken(); break; case TOKidentifier: id = token.ident; nextToken(); if (token.value == TOKnot) { // ident!(template_arguments) TemplateInstance *tempinst = new TemplateInstance(loc, id); nextToken(); if (token.value == TOKlparen) // ident!(template_arguments) tempinst->tiargs = parseTemplateArgumentList(); else // ident!template_argument tempinst->tiargs = parseTemplateArgument(); tid = new TypeInstance(loc, tempinst); goto Lident2; } Lident: tid = new TypeIdentifier(loc, id); Lident2: while (token.value == TOKdot) { nextToken(); if (token.value != TOKidentifier) { error("identifier expected following '.' instead of '%s'", token.toChars()); break; } id = token.ident; nextToken(); if (token.value == TOKnot) { TemplateInstance *tempinst = new TemplateInstance(loc, id); nextToken(); if (token.value == TOKlparen) // ident!(template_arguments) tempinst->tiargs = parseTemplateArgumentList(); else // ident!template_argument tempinst->tiargs = parseTemplateArgument(); tid->addIdent((Identifier *)tempinst); } else tid->addIdent(id); } t = tid; break; case TOKdot: // Leading . as in .foo id = Id::empty; goto Lident; case TOKtypeof: // typeof(expression) tid = parseTypeof(); goto Lident2; case TOKconst: // const(type) nextToken(); check(TOKlparen); t = parseType(); check(TOKrparen); t = t->makeConst(); break; case TOKinvariant: case TOKimmutable: // invariant(type) nextToken(); check(TOKlparen); t = parseType(); check(TOKrparen); t = t->makeInvariant(); break; default: error("basic type expected, not %s", token.toChars()); t = Type::tint32; break; } return t; } /****************************************** * 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 = parseExpression(); // [ expression ] if (token.value == TOKslice) { nextToken(); Expression *e2 = parseExpression(); // [ 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 Arguments *arguments; int varargs; bool ispure = false; bool isnothrow = false; enum TOK save = token.value; nextToken(); arguments = parseParameters(&varargs); while (1) { // Postfixes of 'pure' or 'nothrow' if (token.value == TOKpure) ispure = true; else if (token.value == TOKnothrow) isnothrow = true; else break; nextToken(); } TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage); tf->ispure = ispure; tf->isnothrow = isnothrow; 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, Identifier **pident, TemplateParameters **tpl) { Type *ts; //printf("parseDeclarator(tpl = %p)\n", tpl); t = parseBasicType2(t); switch (token.value) { case TOKidentifier: if (pident) *pident = token.ident; else error("unexpected identifer '%s' in declarator", token.ident->toChars()); ts = t; nextToken(); break; case TOKlparen: /* Parse things with parentheses around the identifier, like: * int (*ident[3])[] * although the D style would be: * int[]*[3] ident */ nextToken(); ts = parseDeclarator(t, pident); check(TOKrparen); 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(); } 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); } else { //printf("It's a static array\n"); Expression *e = parseExpression(); // [ expression ] ta = new TypeSArray(t, e); check(TOKrbracket); } /* 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) { /* Look ahead to see if this is (...)(...), * i.e. a function template declaration */ if (peekPastParen(&token)->value == TOKlparen) { //printf("function template declaration\n"); // Gather template parameter list *tpl = parseTemplateParameterList(); } } int varargs; Arguments *arguments = parseParameters(&varargs); Type *tf = new TypeFunction(arguments, t, varargs, linkage); /* Parse const/invariant/nothrow/pure postfix */ while (1) { switch (token.value) { case TOKconst: tf = tf->makeConst(); nextToken(); continue; case TOKinvariant: case TOKimmutable: tf = tf->makeInvariant(); nextToken(); continue; case TOKnothrow: ((TypeFunction *)tf)->isnothrow = 1; nextToken(); continue; case TOKpure: ((TypeFunction *)tf)->ispure = 1; nextToken(); continue; } break; } /* Insert tf into * ts -> ... -> t * so that * ts -> ... -> tf -> t */ Type **pt; for (pt = &ts; *pt != t; pt = &((TypeNext*)*pt)->next) ; *pt = tf; break; } } break; } return ts; } /********************************** * Parse Declarations. * These can be: * 1. declarations at global/class level * 2. declarations at statement level * Return array of Declaration *'s. */ Array *Parser::parseDeclarations(unsigned storage_class) { enum STC stc; Type *ts; Type *t; Type *tfirst; Identifier *ident; Array *a; enum TOK tok = TOKreserved; unsigned char *comment = token.blockComment; enum LINK link = linkage; //printf("parseDeclarations() %s\n", token.toChars()); if (storage_class) { ts = NULL; // infer type goto L2; } switch (token.value) { case TOKtypedef: case TOKalias: tok = token.value; nextToken(); break; } storage_class = STCundefined; 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 TOKinvariant: case TOKimmutable: if (peek(&token)->value == TOKlparen) break; stc = STCinvariant; goto L1; case TOKshared: if (peek(&token)->value == TOKlparen) break; stc = STCshared; 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 TOKtls: stc = STCtls; goto L1; case TOKenum: stc = STCmanifest; goto L1; L1: if (storage_class & stc) error("redundant storage class '%s'", token.toChars()); storage_class = (STC) (storage_class | stc); { unsigned u = storage_class; u &= STCconst | STCinvariant | STCmanifest; if (u & (u - 1)) error("conflicting storage class %s", Token::toChars(token.value)); } nextToken(); continue; case TOKextern: if (peek(&token)->value != TOKlparen) { stc = STCextern; goto L1; } link = parseLinkage(); continue; default: break; } break; } /* Look for auto initializers: * storage_class identifier = initializer; */ if (storage_class && token.value == TOKidentifier && peek(&token)->value == TOKassign) { return parseAutoDeclarations(storage_class, comment); } if (token.value == TOKclass) { AggregateDeclaration *s = (AggregateDeclaration *)parseAggregate(); s->storage_class |= storage_class; a = new Array(); a->push(s); addComment(s, comment); return a; } /* Look for return type inference for template functions. */ { Token *tk; if (storage_class && token.value == TOKidentifier && (tk = peek(&token))->value == TOKlparen && skipParens(tk, &tk) && peek(tk)->value == TOKlparen) { ts = NULL; } else { ts = parseBasicType(); ts = parseBasicType2(ts); } } L2: tfirst = NULL; a = new Array(); while (1) { Loc loc = this->loc; TemplateParameters *tpl = NULL; ident = NULL; t = parseDeclarator(ts, &ident, &tpl); 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()); if (!ident) error("no identifier for declarator %s", t->toChars()); if (tok == TOKtypedef || tok == TOKalias) { Declaration *v; Initializer *init = NULL; if (token.value == TOKassign) { nextToken(); init = parseInitializer(); } if (tok == TOKtypedef) v = new TypedefDeclaration(loc, ident, t, init); else { if (init) error("alias cannot have initializer"); v = new AliasDeclaration(loc, ident, t); } v->storage_class = storage_class; if (link == linkage) a->push(v); else { Array *ax = new Array(); ax->push(v); Dsymbol *s = new LinkDeclaration(link, ax); a->push(s); } switch (token.value) { case TOKsemicolon: nextToken(); addComment(v, comment); break; case TOKcomma: nextToken(); addComment(v, comment); continue; default: error("semicolon expected to close %s declaration", Token::toChars(tok)); break; } } else if (t->ty == Tfunction) { TypeFunction *tf = (TypeFunction *)t; Expression *constraint = NULL; #if 0 if (Argument::isTPL(tf->parameters)) { if (!tpl) tpl = new TemplateParameters(); } #endif FuncDeclaration *f = new FuncDeclaration(loc, 0, ident, (enum STC)storage_class, t); addComment(f, comment); if (tpl) constraint = parseConstraint(); parseContracts(f); addComment(f, NULL); Dsymbol *s; if (link == linkage) { s = f; } else { Array *ax = new Array(); ax->push(f); s = new LinkDeclaration(link, ax); } /* A template parameter list means it's a function template */ if (tpl) { // Wrap a template around the function declaration Array *decldefs = new Array(); decldefs->push(s); TemplateDeclaration *tempdecl = new TemplateDeclaration(loc, s->ident, tpl, constraint, decldefs); s = tempdecl; } addComment(s, comment); a->push(s); } else { Initializer *init = NULL; if (token.value == TOKassign) { nextToken(); init = parseInitializer(); } VarDeclaration *v = new VarDeclaration(loc, t, ident, init); v->storage_class = storage_class; if (link == linkage) a->push(v); else { Array *ax = new Array(); ax->push(v); Dsymbol *s = new LinkDeclaration(link, ax); a->push(s); } switch (token.value) { case TOKsemicolon: nextToken(); addComment(v, comment); break; case TOKcomma: nextToken(); addComment(v, comment); continue; default: error("semicolon expected, not '%s'", token.toChars()); break; } } break; } return a; } /***************************************** * 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 ';' */ #if DMDV2 Array *Parser::parseAutoDeclarations(unsigned storageClass, unsigned char *comment) { Array *a = new Array; while (1) { Identifier *ident = token.ident; nextToken(); // skip over ident assert(token.value == TOKassign); nextToken(); // skip over '=' Initializer *init = parseInitializer(); VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init); v->storage_class = storageClass; a->push(v); if (token.value == TOKsemicolon) { nextToken(); addComment(v, comment); } else if (token.value == TOKcomma) { nextToken(); if (token.value == TOKidentifier && peek(&token)->value == TOKassign) { addComment(v, comment); continue; } else error("Identifier expected following comma"); } else error("semicolon expected following auto declaration, not '%s'", token.toChars()); break; } return a; } #endif /***************************************** * Parse contracts following function declaration. */ void Parser::parseContracts(FuncDeclaration *f) { Type *tb; enum LINK linksave = linkage; // The following is irrelevant, as it is overridden by sc->linkage in // TypeFunction::semantic linkage = LINKd; // nested functions have D linkage L1: switch (token.value) { case TOKlcurly: if (f->frequire || f->fensure) error("missing body { ... } after in or out"); f->fbody = parseStatement(PSsemi); f->endloc = endloc; break; case TOKbody: nextToken(); f->fbody = parseStatement(PScurly); f->endloc = endloc; break; case TOKsemicolon: if (f->frequire || f->fensure) error("missing body { ... } after in or out"); nextToken(); break; #if 0 // Do we want this for function declarations, so we can do: // int x, y, foo(), z; case TOKcomma: nextToken(); continue; #endif #if 0 // Dumped feature case TOKthrow: if (!f->fthrows) f->fthrows = new Array(); nextToken(); check(TOKlparen); while (1) { tb = parseBasicType(); f->fthrows->push(tb); if (token.value == TOKcomma) { nextToken(); continue; } break; } check(TOKrparen); goto L1; #endif case TOKin: nextToken(); if (f->frequire) error("redundant 'in' statement"); f->frequire = parseStatement(PScurly | PSscope); goto L1; case TOKout: // parse: out (identifier) { statement } nextToken(); if (token.value != TOKlcurly) { check(TOKlparen); if (token.value != TOKidentifier) error("(identifier) following 'out' expected, not %s", token.toChars()); f->outId = token.ident; nextToken(); check(TOKrparen); } if (f->fensure) error("redundant 'out' statement"); f->fensure = parseStatement(PScurly | PSscope); goto L1; default: error("semicolon expected following function declaration"); break; } linkage = linksave; } /***************************************** * Parse initializer for variable declaration. */ Initializer *Parser::parseInitializer() { StructInitializer *is; ArrayInitializer *ia; ExpInitializer *ie; Expression *e; Identifier *id; Initializer *value; int comma; Loc loc = this->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 = 0; 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: nextToken(); comma = 2; continue; case TOKrcurly: // allow trailing comma's nextToken(); break; case TOKeof: error("found EOF instead of initializer"); break; default: 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 != TOKrcurly) goto Lexpression; break; } continue; case TOKeof: break; default: continue; } break; } ia = new ArrayInitializer(loc); nextToken(); comma = 0; 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(); ia->addInit(NULL, value); comma = 1; continue; case TOKcomma: 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__ and __LINE__. */ #if DMDV2 Expression *Parser::parseDefaultInitExp() { if (token.value == TOKfile || token.value == TOKline) { Token *t = peek(&token); if (t->value == TOKcomma || t->value == TOKrparen) { Expression *e; if (token.value == TOKfile) e = new FileInitExp(loc); else e = new LineInitExp(loc); nextToken(); return e; } } Expression *e = parseAssignExp(); return e; } #endif /***************************************** * Input: * flags PSxxxx */ Statement *Parser::parseStatement(int flags) { Statement *s; Token *t; Condition *condition; Statement *ifbody; Statement *elsebody; Loc loc = this->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: */ t = peek(&token); if (t->value == TOKcolon) { // It's a label Identifier *ident = token.ident; nextToken(); nextToken(); s = parseStatement(PSsemi); s = new LabelStatement(loc, ident, s); break; } // fallthrough to TOKdot case TOKdot: case TOKtypeof: if (isDeclaration(&token, 2, TOKreserved, NULL)) goto Ldeclaration; else goto Lexp; break; case TOKassert: case TOKthis: case TOKsuper: case TOKint32v: case TOKuns32v: case TOKint64v: case TOKuns64v: 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 TOKlparen: case TOKcast: case TOKmul: case TOKmin: case TOKadd: case TOKplusplus: case TOKminusminus: case TOKnew: case TOKdelete: case TOKdelegate: case TOKfunction: case TOKtypeid: case TOKis: case TOKlbracket: #if DMDV2 case TOKtraits: case TOKfile: case TOKline: #endif Lexp: { Expression *exp; 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; t = peek(&token); if (t->value == TOKassert) { nextToken(); s = new StaticAssertStatement(parseStaticAssert()); break; } if (t->value == TOKif) { nextToken(); condition = parseStaticIfCondition(); goto Lcondition; } goto Ldeclaration; } CASE_BASIC_TYPES: case TOKtypedef: case TOKalias: case TOKconst: case TOKauto: case TOKextern: case TOKfinal: case TOKinvariant: case TOKimmutable: // case TOKtypeof: Ldeclaration: { Array *a; a = parseDeclarations(STCundefined); if (a->dim > 1) { Statements *as = new Statements(); as->reserve(a->dim); for (int i = 0; i < a->dim; i++) { Dsymbol *d = (Dsymbol *)a->data[i]; s = new DeclarationStatement(loc, d); as->push(s); } s = new CompoundStatement(loc, as); } else if (a->dim == 1) { Dsymbol *d = (Dsymbol *)a->data[0]; s = new DeclarationStatement(loc, d); } else assert(0); if (flags & PSscope) s = new ScopeStatement(loc, s); break; } case TOKstruct: case TOKunion: case TOKclass: case TOKinterface: { Dsymbol *d; d = parseAggregate(); s = new DeclarationStatement(loc, d); 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 DeclarationStatement(loc, d); break; } case TOKmixin: { t = peek(&token); if (t->value == TOKlparen) { // mixin(string) nextToken(); check(TOKlparen, "mixin"); Expression *e = parseAssignExp(); check(TOKrparen); check(TOKsemicolon); s = new CompileStatement(loc, e); break; } Dsymbol *d = parseMixin(); s = new DeclarationStatement(loc, d); break; } case TOKlcurly: { Statements *statements; nextToken(); statements = new Statements(); while (token.value != TOKrcurly) { statements->push(parseStatement(PSsemi | PScurlyscope)); } endloc = this->loc; s = new CompoundStatement(loc, statements); if (flags & (PSscope | PScurlyscope)) s = new ScopeStatement(loc, s); nextToken(); break; } case TOKwhile: { Expression *condition; Statement *body; nextToken(); check(TOKlparen); condition = parseExpression(); check(TOKrparen); body = parseStatement(PSscope); s = new WhileStatement(loc, condition, body); break; } case TOKsemicolon: if (!(flags & PSsemi)) error("use '{ }' for an empty statement, not a ';'"); nextToken(); s = new ExpStatement(loc, NULL); break; case TOKdo: { Statement *body; Expression *condition; nextToken(); body = parseStatement(PSscope); check(TOKwhile); check(TOKlparen); condition = parseExpression(); check(TOKrparen); s = new DoStatement(loc, body, condition); break; } case TOKfor: { Statement *init; Expression *condition; Expression *increment; Statement *body; nextToken(); check(TOKlparen); if (token.value == TOKsemicolon) { init = NULL; nextToken(); } else { init = parseStatement(0); } 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); } body = parseStatement(PSscope); s = new ForStatement(loc, init, condition, increment, body); if (init) s = new ScopeStatement(loc, s); break; } case TOKforeach: case TOKforeach_reverse: { enum TOK op = token.value; Arguments *arguments; Statement *d; Statement *body; Expression *aggr; nextToken(); check(TOKlparen); arguments = new Arguments(); while (1) { Type *tb; Identifier *ai = NULL; Type *at; unsigned storageClass; Argument *a; storageClass = 0; if (token.value == TOKinout || token.value == TOKref) { storageClass = STCref; nextToken(); } 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: a = new Argument(storageClass, at, ai, NULL); arguments->push(a); if (token.value == TOKcomma) { nextToken(); continue; } break; } check(TOKsemicolon); aggr = parseExpression(); if (token.value == TOKslice && arguments->dim == 1) { Argument *a = (Argument *)arguments->data[0]; delete arguments; nextToken(); Expression *upr = parseExpression(); check(TOKrparen); body = parseStatement(0); s = new ForeachRangeStatement(loc, op, a, aggr, upr, body); } else { check(TOKrparen); body = parseStatement(0); s = new ForeachStatement(loc, op, arguments, aggr, body); } break; } case TOKif: { Argument *arg = NULL; Expression *condition; Statement *ifbody; Statement *elsebody; nextToken(); check(TOKlparen); if (token.value == TOKauto) { nextToken(); if (token.value == TOKidentifier) { Token *t = peek(&token); if (t->value == TOKassign) { arg = new Argument(0, NULL, token.ident, NULL); nextToken(); nextToken(); } else { error("= expected following auto identifier"); goto Lerror; } } else { error("identifier expected following auto"); goto Lerror; } } else if (isDeclaration(&token, 2, TOKassign, NULL)) { Type *at; Identifier *ai; at = parseType(&ai); check(TOKassign); arg = new Argument(0, at, ai, NULL); } // Check for " ident;" else if (token.value == TOKidentifier) { Token *t = peek(&token); if (t->value == TOKcomma || t->value == TOKsemicolon) { arg = new Argument(0, NULL, token.ident, NULL); nextToken(); nextToken(); if (1 || !global.params.useDeprecated) error("if (v; e) is deprecated, use if (auto v = e)"); } } condition = parseExpression(); check(TOKrparen); ifbody = parseStatement(PSscope); if (token.value == TOKelse) { nextToken(); elsebody = parseStatement(PSscope); } else elsebody = NULL; s = new IfStatement(loc, arg, condition, ifbody, elsebody); 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(PScurlyscope); s = new OnScopeStatement(loc, t, st); break; } case TOKdebug: nextToken(); condition = parseDebugCondition(); goto Lcondition; case TOKversion: nextToken(); condition = parseVersionCondition(); goto Lcondition; Lcondition: ifbody = parseStatement(0 /*PSsemi*/); elsebody = NULL; if (token.value == TOKelse) { nextToken(); elsebody = parseStatement(0 /*PSsemi*/); } s = new ConditionalStatement(loc, condition, ifbody, elsebody); 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: { Expression *condition; Statement *body; nextToken(); check(TOKlparen); condition = parseExpression(); check(TOKrparen); body = parseStatement(PSscope); s = new SwitchStatement(loc, condition, body); break; } case TOKcase: { Expression *exp; Statements *statements; Array cases; // array of Expression's while (1) { nextToken(); exp = parseAssignExp(); cases.push(exp); if (token.value != TOKcomma) break; } check(TOKcolon); statements = new Statements(); while (token.value != TOKcase && token.value != TOKdefault && token.value != TOKrcurly) { statements->push(parseStatement(PSsemi | PScurlyscope)); } s = new CompoundStatement(loc, statements); s = new ScopeStatement(loc, s); // Keep cases in order by building the case statements backwards for (int i = cases.dim; i; i--) { exp = (Expression *)cases.data[i - 1]; s = new CaseStatement(loc, exp, s); } break; } case TOKdefault: { Statements *statements; nextToken(); check(TOKcolon); statements = new Statements(); while (token.value != TOKcase && token.value != TOKdefault && token.value != TOKrcurly) { statements->push(parseStatement(PSsemi | PScurlyscope)); } s = new CompoundStatement(loc, statements); s = new ScopeStatement(loc, s); 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; 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); break; } case TOKtry: { Statement *body; Array *catches = NULL; Statement *finalbody = NULL; nextToken(); body = parseStatement(PSscope); while (token.value == TOKcatch) { Statement *handler; Catch *c; Type *t; Identifier *id; Loc loc = this->loc; nextToken(); if (token.value == TOKlcurly) { t = NULL; id = NULL; } else { check(TOKlparen); id = NULL; t = parseType(&id); check(TOKrparen); } handler = parseStatement(0); c = new Catch(loc, t, id, handler); if (!catches) catches = new Array(); catches->push(c); } if (token.value == TOKfinally) { nextToken(); finalbody = parseStatement(0); } 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 TOKvolatile: nextToken(); s = parseStatement(PSsemi | PScurlyscope); #if DMDV2 if (!global.params.useDeprecated) error("volatile statements deprecated; used synchronized statements instead"); #endif s = new VolatileStatement(loc, s); break; case TOKasm: { Statements *statements; Identifier *label; Loc labelloc; Token *toklist; Token **ptoklist; // Parse the asm block into a sequence of AsmStatements, // each AsmStatement is one instruction. // Separate out labels. // Defer parsing of AsmStatements until semantic processing. nextToken(); check(TOKlcurly); toklist = NULL; ptoklist = &toklist; label = NULL; statements = new Statements(); while (1) { switch (token.value) { case TOKidentifier: if (!toklist) { // Look ahead to see if it is a label t = peek(&token); if (t->value == TOKcolon) { // It's a label label = token.ident; labelloc = this->loc; nextToken(); nextToken(); continue; } } goto Ldefault; case TOKrcurly: if (toklist || label) { error("asm statements must end in ';'"); } break; case TOKsemicolon: s = NULL; if (toklist || label) { // Create AsmStatement from list of tokens we've saved s = new AsmStatement(this->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"); break; default: Ldefault: *ptoklist = new Token(); memcpy(*ptoklist, &token, sizeof(Token)); ptoklist = &(*ptoklist)->next; *ptoklist = NULL; nextToken(); continue; } break; } s = new AsmBlockStatement(loc, statements); nextToken(); 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; } return s; } void Parser::check(enum TOK value) { check(loc, value); } void Parser::check(Loc loc, enum TOK value) { if (token.value != value) error(loc, "found '%s' when expecting '%s'", token.toChars(), Token::toChars(value)); nextToken(); } void Parser::check(enum TOK value, const char *string) { if (token.value != value) error("found '%s' when expecting '%s' following '%s'", token.toChars(), Token::toChars(value), string); nextToken(); } /************************************ * Determine if the scanner is sitting on the start of a declaration. * Input: * needId 0 no identifier * 1 identifier optional * 2 must have identifier */ int Parser::isDeclaration(Token *t, int needId, enum TOK endtok, Token **pt) { //printf("isDeclaration(needId = %d)\n", needId); int haveId = 0; #if DMDV2 if ((t->value == TOKconst || t->value == TOKinvariant || token.value == TOKimmutable) && peek(t)->value != TOKlparen) { /* const type * invariant type */ t = peek(t); } #endif if (!isBasicType(&t)) goto Lisnot; if (!isDeclarator(&t, &haveId, endtok)) goto Lisnot; if ( needId == 1 || (needId == 0 && !haveId) || (needId == 2 && haveId)) { if (pt) *pt = t; goto Lis; } else goto Lisnot; Lis: //printf("\tis declaration\n"); return TRUE; Lisnot: //printf("\tis not declaration\n"); return FALSE; } int Parser::isBasicType(Token **pt) { // This code parallels parseBasicType() Token *t = *pt; Token *t2; int parens; int haveId = 0; switch (t->value) { CASE_BASIC_TYPES: 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; break; CASE_BASIC_TYPES: case TOKint32v: case TOKuns32v: case TOKint64v: case TOKuns64v: 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 TOKfile: case TOKline: goto L2; default: goto Lfalse; } } else break; } break; case TOKdot: goto Ldot; case TOKtypeof: /* typeof(exp).identifier... */ t = peek(t); if (t->value != TOKlparen) goto Lfalse; if (!skipParens(t, &t)) goto Lfalse; goto L2; case TOKconst: case TOKinvariant: case TOKimmutable: // const(type) or invariant(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; } int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok) { // This code parallels parseDeclarator() Token *t = *pt; int parens; //printf("Parser::isDeclarator()\n"); //t->print(); 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); } 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); } continue; case TOKidentifier: if (*haveId) return FALSE; *haveId = TRUE; t = peek(t); break; case TOKlparen: 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, TOKrparen)) return FALSE; t = peek(t); parens = TRUE; break; case TOKdelegate: case TOKfunction: t = peek(t); if (!isParameters(&t)) return FALSE; continue; } 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 (!isParameters(&t)) return FALSE; while (1) { switch (t->value) { case TOKconst: case TOKinvariant: case TOKimmutable: case TOKpure: case TOKnothrow: t = peek(t); continue; default: break; } break; } continue; // Valid tokens that follow a declaration case TOKrparen: case TOKrbracket: case TOKassign: case TOKcomma: case TOKsemicolon: case TOKlcurly: case TOKin: // The !parens is to disallow unnecessary parentheses if (!parens && (endtok == TOKreserved || endtok == t->value)) { *pt = t; return TRUE; } return FALSE; default: return FALSE; } } } int Parser::isParameters(Token **pt) { // This code parallels parseParameters() Token *t = *pt; int tmp; //printf("isParameters()\n"); if (t->value != TOKlparen) return FALSE; t = peek(t); for (;1; t = peek(t)) { switch (t->value) { case TOKrparen: break; case TOKdotdotdot: t = peek(t); break; case TOKin: case TOKout: case TOKinout: case TOKref: case TOKlazy: case TOKconst: case TOKinvariant: case TOKimmutable: case TOKfinal: continue; #if 0 case TOKstatic: continue; case TOKauto: case TOKalias: t = peek(t); if (t->value == TOKidentifier) t = peek(t); if (t->value == TOKassign) { t = peek(t); if (!isExpression(&t)) return FALSE; } goto L3; #endif default: if (!isBasicType(&t)) return FALSE; tmp = FALSE; if (t->value != TOKdotdotdot && !isDeclarator(&t, &tmp, TOKreserved)) return FALSE; if (t->value == TOKassign) { t = peek(t); if (!isExpression(&t)) return FALSE; } if (t->value == TOKdotdotdot) { t = peek(t); break; } L3: if (t->value == TOKcomma) { continue; } break; } break; } if (t->value != TOKrparen) return FALSE; t = peek(t); *pt = t; return TRUE; } int 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 over * instance foo.bar(parameters...) * Output: * if (pt), *pt is set to the token following the closing ) * Returns: * 1 it's valid instance syntax * 0 invalid instance syntax */ int Parser::isTemplateInstance(Token *t, Token **pt) { t = peek(t); if (t->value != TOKdot) { if (t->value != TOKidentifier) goto Lfalse; t = peek(t); } while (t->value == TOKdot) { t = peek(t); if (t->value != TOKidentifier) goto Lfalse; t = peek(t); } if (t->value != TOKlparen) goto Lfalse; // Skip over the template arguments while (1) { while (1) { t = peek(t); switch (t->value) { case TOKlparen: if (!skipParens(t, &t)) goto Lfalse; continue; case TOKrparen: break; case TOKcomma: break; case TOKeof: case TOKsemicolon: goto Lfalse; default: continue; } break; } if (t->value != TOKcomma) break; } if (t->value != TOKrparen) goto Lfalse; t = peek(t); if (pt) *pt = t; return 1; Lfalse: return 0; } /******************************************* * Skip parens, brackets. * Input: * t is on opening ( * Output: * *pt is set to closing token, which is ')' on success * Returns: * !=0 successful * 0 some parsing error */ int Parser::skipParens(Token *t, Token **pt) { 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: case TOKsemicolon: goto Lfalse; default: break; } t = peek(t); } Ldone: if (*pt) *pt = t; return 1; Lfalse: return 0; } /********************************* Expression Parser ***************************/ Expression *Parser::parsePrimaryExp() { Expression *e; Type *t; Identifier *id; enum TOK save; Loc loc = this->loc; //printf("parsePrimaryExp(): loc = %d\n", loc.linnum); switch (token.value) { case TOKidentifier: id = token.ident; nextToken(); if (token.value == TOKnot && peekNext() != TOKis) { // identifier!(template-argument-list) TemplateInstance *tempinst; tempinst = new TemplateInstance(loc, id); nextToken(); if (token.value == TOKlparen) // ident!(template_arguments) tempinst->tiargs = parseTemplateArgumentList(); else // ident!template_argument tempinst->tiargs = parseTemplateArgument(); 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, token.int32value, Type::tint32); nextToken(); break; case TOKuns32v: e = new IntegerExp(loc, token.uns32value, 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.float80value, Type::tfloat32); nextToken(); break; case TOKfloat64v: e = new RealExp(loc, token.float80value, Type::tfloat64); nextToken(); break; case TOKfloat80v: e = new RealExp(loc, token.float80value, Type::tfloat80); nextToken(); break; case TOKimaginary32v: e = new RealExp(loc, token.float80value, Type::timaginary32); nextToken(); break; case TOKimaginary64v: e = new RealExp(loc, token.float80value, Type::timaginary64); nextToken(); break; case TOKimaginary80v: e = new RealExp(loc, token.float80value, Type::timaginary80); nextToken(); break; case TOKnull: e = new NullExp(loc); nextToken(); break; #if DMDV2 case TOKfile: { char *s = loc.filename ? loc.filename : mod->ident->toChars(); e = new StringExp(loc, s, strlen(s), 0); nextToken(); break; } case TOKline: e = new IntegerExp(loc, loc.linnum, Type::tint32); nextToken(); break; #endif 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, token.uns32value, Type::tchar); nextToken(); break; case TOKwcharv: e = new IntegerExp(loc, token.uns32value, Type::twchar); nextToken(); break; case TOKdcharv: e = new IntegerExp(loc, token.uns32value, Type::tdchar); nextToken(); break; case TOKstring: { unsigned char *s; unsigned len; unsigned char postfix; // cat adjacent strings s = token.ustring; len = token.len; postfix = token.postfix; while (1) { nextToken(); if (token.value == TOKstring) { unsigned len1; unsigned len2; unsigned char *s2; if (token.postfix) { if (token.postfix != postfix) error("mismatched string literal postfixes '%c' and '%c'", postfix, token.postfix); postfix = token.postfix; } len1 = len; len2 = token.len; len = len1 + len2; s2 = (unsigned char *)mem.malloc((len + 1) * sizeof(unsigned char)); memcpy(s2, s, len1 * sizeof(unsigned char)); memcpy(s2 + len1, token.ustring, (len2 + 1) * sizeof(unsigned char)); s = s2; } else break; } e = new StringExp(loc, s, len, postfix); break; } CASE_BASIC_TYPES_X(t): nextToken(); L1: check(TOKdot, t->toChars()); if (token.value != TOKidentifier) { error("found '%s' when expecting identifier following '%s.'", token.toChars(), t->toChars()); goto Lerr; } e = new TypeDotIdExp(loc, t, token.ident); nextToken(); break; case TOKtypeof: { t = parseTypeof(); e = new TypeExp(loc, t); break; } case TOKtypeid: { Type *t; nextToken(); check(TOKlparen, "typeid"); t = parseType(); // ( type ) check(TOKrparen); e = new TypeidExp(loc, t); break; } #if DMDV2 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 = parseTemplateArgumentList2(); // __traits(identifier, args...) else check(TOKrparen); // __traits(identifier) e = new TraitsExp(loc, ident, args); break; } #endif case TOKis: { Type *targ; Identifier *ident = NULL; Type *tspec = NULL; enum TOK tok = TOKreserved; enum TOK tok2 = TOKreserved; TemplateParameters *tpl = NULL; Loc loc = this->loc; nextToken(); if (token.value == TOKlparen) { nextToken(); targ = parseType(&ident); if (token.value == TOKcolon || token.value == TOKequal) { tok = token.value; nextToken(); if (tok == TOKequal && (token.value == TOKtypedef || token.value == TOKstruct || token.value == TOKunion || token.value == TOKclass || token.value == TOKsuper || token.value == TOKenum || token.value == TOKinterface || token.value == TOKconst && peek(&token)->value == TOKrparen || token.value == TOKinvariant && peek(&token)->value == TOKrparen || token.value == TOKimmutable && peek(&token)->value == TOKrparen || token.value == TOKfunction || token.value == TOKdelegate || token.value == TOKreturn)) { tok2 = token.value; nextToken(); } else { tspec = parseType(); } } if (ident && tspec) { if (token.value == TOKcomma) tpl = parseTemplateParameterList(1); else { tpl = new TemplateParameters(); check(TOKrparen); } TemplateParameter *tp = new TemplateTypeParameter(loc, ident, NULL, NULL); tpl->insert(0, tp); } 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(); msg = parseAssignExp(); } check(TOKrparen); e = new AssertExp(loc, e, msg); break; } case TOKmixin: { nextToken(); check(TOKlparen, "mixin"); e = parseAssignExp(); check(TOKrparen); e = new CompileExp(loc, e); break; } case TOKimport: { nextToken(); check(TOKlparen, "import"); e = parseAssignExp(); check(TOKrparen); e = new FileExp(loc, e); break; } case TOKlparen: if (peekPastParen(&token)->value == TOKlcurly) { // (arguments) { statements... } save = TOKdelegate; goto case_delegate; } // ( expression ) nextToken(); e = parseExpression(); 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(); if (token.value != TOKrbracket) { while (token.value != TOKeof) { Expression *e = parseAssignExp(); if (token.value == TOKcolon && (keys || values->dim == 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(TOKrbracket); if (keys) e = new AssocArrayLiteralExp(loc, keys, values); else e = new ArrayLiteralExp(loc, values); break; } case TOKlcurly: // { statements... } save = TOKdelegate; goto case_delegate; case TOKfunction: case TOKdelegate: save = token.value; nextToken(); case_delegate: { /* function type(parameters) { body } pure nothrow * delegate type(parameters) { body } pure nothrow * (parameters) { body } * { body } */ Arguments *arguments; int varargs; FuncLiteralDeclaration *fd; Type *t; bool isnothrow = false; bool ispure = false; if (token.value == TOKlcurly) { t = NULL; varargs = 0; arguments = new Arguments(); } else { if (token.value == TOKlparen) t = NULL; else { t = parseBasicType(); t = parseBasicType2(t); // function return type } arguments = parseParameters(&varargs); while (1) { if (token.value == TOKpure) ispure = true; else if (token.value == TOKnothrow) isnothrow = true; else break; nextToken(); } } TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage); tf->ispure = ispure; tf->isnothrow = isnothrow; fd = new FuncLiteralDeclaration(loc, 0, tf, save, NULL); parseContracts(fd); e = new FuncExp(loc, fd); 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 = this->loc; switch (token.value) { case TOKdot: nextToken(); if (token.value == TOKidentifier) { Identifier *id = token.ident; nextToken(); if (token.value == TOKnot && peekNext() != TOKis) { // identifier!(template-argument-list) TemplateInstance *tempinst = new TemplateInstance(loc, id); nextToken(); if (token.value == TOKlparen) // ident!(template_arguments) tempinst->tiargs = parseTemplateArgumentList(); else // ident!template_argument tempinst->tiargs = parseTemplateArgument(); e = new DotTemplateInstanceExp(loc, e, tempinst); } 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; inBrackets++; nextToken(); if (token.value == TOKrbracket) { // array[] e = new SliceExp(loc, e, NULL, NULL); nextToken(); } else { index = parseAssignExp(); if (token.value == TOKslice) { // array[lwr .. upr] nextToken(); upr = parseAssignExp(); e = new SliceExp(loc, e, index, upr); } else { // array[index, i2, i3, i4, ...] Expressions *arguments = new Expressions(); arguments->push(index); if (token.value == TOKcomma) { nextToken(); while (1) { Expression *arg; arg = parseAssignExp(); arguments->push(arg); if (token.value == TOKrbracket) break; check(TOKcomma); } } e = new ArrayExp(loc, e, arguments); } check(TOKrbracket); inBrackets--; } continue; } default: return e; } nextToken(); } } Expression *Parser::parseUnaryExp() { Expression *e; Loc loc = this->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)); break; case TOKminusminus: nextToken(); e = parseUnaryExp(); e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32)); 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); break; case TOKnew: e = parseNewExp(NULL); break; case TOKcast: // cast(type) expression { Type *t; nextToken(); check(TOKlparen); /* Look for cast(const) and cast(invariant) */ if ((token.value == TOKconst || token.value == TOKinvariant || token.value == TOKimmutable) && peek(&token)->value == TOKrparen) { enum TOK tok = token.value; nextToken(); nextToken(); e = parseUnaryExp(); e = new CastExp(loc, e, tok); } else { t = parseType(); // ( type ) check(TOKrparen); e = parseUnaryExp(); e = new CastExp(loc, e, t); } 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) // !is break; 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 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: #if 0 case TOKtilde: case TOKand: case TOKmul: case TOKmin: case TOKadd: #endif case TOKfunction: case TOKdelegate: case TOKtypeof: #if DMDV2 case TOKfile: case TOKline: #endif CASE_BASIC_TYPES: // (type)int.size { // (type) una_exp Type *t; nextToken(); t = parseType(); check(TOKrparen); // if .identifier if (token.value == TOKdot) { nextToken(); if (token.value != TOKidentifier) { error("Identifier expected following (type)."); return NULL; } e = new TypeDotIdExp(loc, t, token.ident); nextToken(); e = parsePostExp(e); } else { e = parseUnaryExp(); e = new CastExp(loc, e, t); error("C style cast illegal, use %s", e->toChars()); } return e; } } } #endif e = parsePrimaryExp(); e = parsePostExp(e); break; } default: e = parsePrimaryExp(); e = parsePostExp(e); break; } assert(e); return e; } Expression *Parser::parseMulExp() { Expression *e; Expression *e2; Loc loc = this->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 = this->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 = this->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::parseRelExp() { Expression *e; Expression *e2; enum TOK op; Loc loc = this->loc; e = parseShiftExp(); while (1) { switch (token.value) { case TOKlt: case TOKle: case TOKgt: case TOKge: case TOKunord: case TOKlg: case TOKleg: case TOKule: case TOKul: case TOKuge: case TOKug: case TOKue: op = token.value; nextToken(); e2 = parseShiftExp(); e = new CmpExp(op, loc, e, e2); continue; case TOKin: nextToken(); e2 = parseShiftExp(); e = new InExp(loc, e, e2); continue; default: break; } break; } return e; } Expression *Parser::parseEqualExp() { Expression *e; Expression *e2; Token *t; Loc loc = this->loc; e = parseRelExp(); while (1) { enum TOK value = token.value; switch (value) { case TOKequal: case TOKnotequal: nextToken(); e2 = parseRelExp(); e = new EqualExp(value, loc, e, e2); continue; case TOKidentity: error("'===' is no longer legal, use 'is' instead"); goto L1; case TOKnotidentity: error("'!==' is no longer legal, use '!is' instead"); goto L1; case TOKis: value = TOKidentity; goto L1; case TOKnot: // Attempt to identify '!is' t = peek(&token); if (t->value != TOKis) break; nextToken(); value = TOKnotidentity; goto L1; L1: nextToken(); e2 = parseRelExp(); e = new IdentityExp(value, loc, e, e2); continue; default: break; } break; } return e; } Expression *Parser::parseCmpExp() { Expression *e; Expression *e2; Token *t; Loc loc = this->loc; e = parseShiftExp(); enum 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 != 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() { Expression *e; Expression *e2; Loc loc = this->loc; if (global.params.Dversion == 1) { e = parseEqualExp(); while (token.value == TOKand) { nextToken(); e2 = parseEqualExp(); e = new AndExp(loc,e,e2); loc = this->loc; } } else { e = parseCmpExp(); while (token.value == TOKand) { nextToken(); e2 = parseCmpExp(); e = new AndExp(loc,e,e2); loc = this->loc; } } return e; } Expression *Parser::parseXorExp() { Expression *e; Expression *e2; Loc loc = this->loc; e = parseAndExp(); while (token.value == TOKxor) { nextToken(); e2 = parseAndExp(); e = new XorExp(loc, e, e2); } return e; } Expression *Parser::parseOrExp() { Expression *e; Expression *e2; Loc loc = this->loc; e = parseXorExp(); while (token.value == TOKor) { nextToken(); e2 = parseXorExp(); e = new OrExp(loc, e, e2); } return e; } Expression *Parser::parseAndAndExp() { Expression *e; Expression *e2; Loc loc = this->loc; e = parseOrExp(); while (token.value == TOKandand) { nextToken(); e2 = parseOrExp(); e = new AndAndExp(loc, e, e2); } return e; } Expression *Parser::parseOrOrExp() { Expression *e; Expression *e2; Loc loc = this->loc; e = parseAndAndExp(); while (token.value == TOKoror) { nextToken(); e2 = parseAndAndExp(); e = new OrOrExp(loc, e, e2); } return e; } Expression *Parser::parseCondExp() { Expression *e; Expression *e1; Expression *e2; Loc loc = this->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 = this->loc; switch (token.value) { #define X(tok,ector) \ case tok: nextToken(); e2 = parseAssignExp(); e = new ector(loc,e,e2); continue; X(TOKassign, AssignExp); X(TOKaddass, AddAssignExp); X(TOKminass, MinAssignExp); X(TOKmulass, MulAssignExp); X(TOKdivass, DivAssignExp); X(TOKmodass, ModAssignExp); X(TOKandass, AndAssignExp); X(TOKorass, OrAssignExp); X(TOKxorass, XorAssignExp); X(TOKshlass, ShlAssignExp); X(TOKshrass, ShrAssignExp); X(TOKushrass, UshrAssignExp); X(TOKcatass, CatAssignExp); #undef X default: break; } break; } return e; } Expression *Parser::parseExpression() { Expression *e; Expression *e2; Loc loc = this->loc; //printf("Parser::parseExpression() loc = %d\n", loc.linnum); e = parseAssignExp(); while (token.value == TOKcomma) { nextToken(); e2 = parseAssignExp(); e = new CommaExp(loc, e, e2); loc = this->loc; } return e; } /************************* * Collect argument list. * Assume current token is ',', '(' or '['. */ Expressions *Parser::parseArguments() { // function call Expressions *arguments; Expression *arg; enum TOK endtok; arguments = new Expressions(); if (token.value == TOKlbracket) endtok = TOKrbracket; else endtok = TOKrparen; { nextToken(); if (token.value != endtok) { while (1) { 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; Expression *e; Loc loc = this->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; ClassDeclaration *cd = new ClassDeclaration(loc, id, baseclasses); if (token.value != TOKlcurly) { error("{ members } expected for anonymous class"); cd->members = NULL; } else { nextToken(); Array *decl = parseDeclDefs(0); if (token.value != TOKrcurly) error("class member expected"); nextToken(); cd->members = decl; } e = new NewAnonClassExp(loc, thisexp, newargs, cd, arguments); return e; } t = parseBasicType(); t = parseBasicType2(t); if (t->ty == Taarray) { TypeAArray *taa = (TypeAArray *)t; Type *index = taa->index; Expression *e = index->toExpression(); if (e) { arguments = new Expressions(); arguments->push(e); t = new TypeDArray(taa->next); } else { error("need size of rightmost array, not type %s", index->toChars()); return new NullExp(loc); } } else if (t->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)t; Expression *e = tsa->dim; arguments = new Expressions(); arguments->push(e); t = new TypeDArray(tsa->next); } else if (token.value == TOKlparen) { arguments = parseArguments(); } e = new NewExp(loc, thisexp, newargs, t, arguments); return e; } /********************************************** */ void Parser::addComment(Dsymbol *s, unsigned char *blockComment) { s->addComment(combineComments(blockComment, token.lineComment)); token.lineComment = NULL; } /********************************* ***************************/