view dmd/Parser.d @ 178:e3afd1303184

Many small bugs fixed Made all classes derive from TObject to detect memory leaks (functionality is disabled for now) Began work on overriding backend memory allocations (to avoid memory leaks)
author korDen
date Sun, 17 Oct 2010 07:42:00 +0400
parents af1bebfd96a4
children cd48cb899aee
line wrap: on
line source

module dmd.Parser;

import dmd.common;
import dmd.Lexer;
import dmd.PostBlitDeclaration;
import dmd.FileInitExp;
import dmd.LineInitExp;
import dmd.EnumMember;
import dmd.CtorDeclaration;
import dmd.ShlAssignExp;
import dmd.ShrAssignExp;
import dmd.UshrAssignExp;
import dmd.CatAssignExp;
import dmd.StaticIfCondition;
import dmd.TraitsExp;
import dmd.TemplateMixin;
import dmd.BaseClass;
import dmd.AssignExp;
import dmd.TemplateInstance;
import dmd.NewExp;
import dmd.ArrayExp;
import dmd.DotTemplateInstanceExp;
import dmd.ClassDeclaration;
import dmd.NewAnonClassExp;
import dmd.InterfaceDeclaration;
import dmd.StructDeclaration;
import dmd.UnionDeclaration;
import dmd.AnonDeclaration;
import dmd.StructInitializer;
import dmd.ArrayInitializer;
import dmd.ExpInitializer;
import dmd.TemplateAliasParameter;
import dmd.TemplateTupleParameter;
import dmd.TemplateThisParameter;
import dmd.TemplateValueParameter;
import dmd.VoidInitializer;
import dmd.VersionCondition;
import dmd.DotIdExp;
import dmd.DebugCondition;
import dmd.PostExp;
import dmd.CallExp;
import dmd.SliceExp;
import dmd.FuncExp;
import dmd.AssocArrayLiteralExp;
import dmd.ArrayLiteralExp;
import dmd.IsExp;
import dmd.FuncLiteralDeclaration;
import dmd.AssertExp;
import dmd.CompileExp;
import dmd.FileExp;
import dmd.TemplateMixin;
import dmd.TemplateParameter;
import dmd.TemplateTypeParameter;
import dmd.TypeidExp;
import dmd.StringExp;
import dmd.ScopeExp;
import dmd.IdentifierExp;
import dmd.DollarExp;
import dmd.ThisExp;
import dmd.SuperExp;
import dmd.NullExp;
import dmd.RealExp;
import dmd.TypeExp;
import dmd.AddrExp;
import dmd.MOD;
import dmd.IntegerExp;
import dmd.CastExp;
import dmd.PtrExp;
import dmd.NegExp;
import dmd.XorAssignExp;
import dmd.OrAssignExp;
import dmd.UAddExp;
import dmd.NotExp;
import dmd.ComExp;
import dmd.DeleteExp;
import dmd.MulAssignExp;
import dmd.ModAssignExp;
import dmd.MinAssignExp;
import dmd.DivAssignExp;
import dmd.AndAssignExp;
import dmd.AddAssignExp;
import dmd.PowAssignExp;
import dmd.ModuleDeclaration;
import dmd.CaseRangeStatement;
import dmd.CommaExp;
import dmd.XorExp;
import dmd.CondExp;
import dmd.CmpExp;
import dmd.InExp;
import dmd.OrOrExp;
import dmd.OrExp;
import dmd.AddExp;
import dmd.MinExp;
import dmd.CatExp;
import dmd.AndAndExp;
import dmd.EqualExp;
import dmd.ShlExp;
import dmd.ShrExp;
import dmd.DivExp;
import dmd.MulExp;
import dmd.ModExp;
import dmd.UshrExp;
import dmd.IdentityExp;
import dmd.AndExp;
import dmd.Id;
import dmd.LabelStatement;
import dmd.ExpStatement;
import dmd.StaticAssertStatement;
import dmd.DeclarationStatement;
import dmd.ScopeStatement;
import dmd.PragmaStatement;
import dmd.WhileStatement;
import dmd.DoStatement;
import dmd.ForStatement;
import dmd.OnScopeStatement;
import dmd.IfStatement;
import dmd.SwitchStatement;
import dmd.CaseStatement;
import dmd.DefaultStatement;
import dmd.GotoDefaultStatement;
import dmd.GotoCaseStatement;
import dmd.GotoStatement;
import dmd.SynchronizedStatement;
import dmd.WithStatement;
import dmd.Catch;
import dmd.TryCatchStatement;
import dmd.TryFinallyStatement;
import dmd.ThrowStatement;
import dmd.VolatileStatement;
import dmd.ReturnStatement;
import dmd.BreakStatement;
import dmd.ContinueStatement;
import dmd.AsmStatement;
import dmd.TypeReturn;
import dmd.TypeTypeof;
import dmd.ForeachRangeStatement;
import dmd.ForeachStatement;
import dmd.CompileStatement;
import dmd.CompoundStatement;
import dmd.ConditionalStatement;
import dmd.CompoundDeclarationStatement;
import dmd.Parameter;
import dmd.ParseStatementFlags;
import dmd.TypeNewArray;
import dmd.TypeNext;
import dmd.TypeInstance;
import dmd.TypePointer;
import dmd.TypeDArray;
import dmd.TypeAArray;
import dmd.TypeSlice;
import dmd.TypeSArray;
import dmd.TemplateInstance;
import dmd.TypeIdentifier;
import dmd.VarDeclaration;
import dmd.TypeFunction;
import dmd.TypeDelegate;
import dmd.TY;
import dmd.LinkDeclaration;
import dmd.Declaration;
import dmd.AggregateDeclaration;
import dmd.TypedefDeclaration;
import dmd.AliasDeclaration;
import dmd.LINK;
import dmd.Loc;
import dmd.Module;
import dmd.Array;
import dmd.Expression;
import dmd.TemplateDeclaration;
import dmd.ArrayTypes;
import dmd.Dsymbol;
import dmd.StaticAssert;
import dmd.TypeQualified;
import dmd.Condition;
import dmd.PostBlitDeclaration;
import dmd.DtorDeclaration;
import dmd.ConditionalDeclaration;
import dmd.StaticCtorDeclaration;
import dmd.StaticDtorDeclaration;
import dmd.InvariantDeclaration;
import dmd.UnitTestDeclaration;
import dmd.NewDeclaration;
import dmd.DeleteDeclaration;
import dmd.EnumDeclaration;
import dmd.Import;
import dmd.Type;
import dmd.Identifier;
import dmd.FuncDeclaration;
import dmd.Statement;
import dmd.Initializer;
import dmd.Token;
import dmd.TOK;
import dmd.ParseStatementFlags;
import dmd.PROT;
import dmd.STC;
import dmd.Util;
import dmd.CompileDeclaration;
import dmd.StaticIfDeclaration;
import dmd.StorageClassDeclaration;
import dmd.LinkDeclaration;
import dmd.ProtDeclaration;
import dmd.AlignDeclaration;
import dmd.PragmaDeclaration;
import dmd.DebugSymbol;
import dmd.VersionSymbol;
import dmd.AliasThis;
import dmd.Global;
import dmd.TRUST;
import dmd.PowExp;

import core.stdc.string : memcpy;

import std.exception;
import core.memory;

class Parser : Lexer
{
    ModuleDeclaration md;
    LINK linkage;
    Loc endloc;			// set to location of last right curly
    int inBrackets;		// inside [] of array index or slice

    this(Module module_, ubyte* base, uint length, int doDocComment)
	{
		register();
		super(module_, base, 0, length, doDocComment, 0);
		//printf("Parser.Parser()\n");
		linkage = LINK.LINKd;
		//nextToken();		// start up the scanner
	}
	
    Dsymbols parseModule()
	{
		typeof(return) decldefs;

		// ModuleDeclation leads off
		if (token.value == TOK.TOKmodule)
		{
			string comment = token.blockComment;
			bool safe = false;

			nextToken();
static if(false) {
version (DMDV2) {
			if (token.value == TOK.TOKlparen)
			{
				nextToken();
				if (token.value != TOK.TOKidentifier)
				{
					error("module (system) identifier expected");
					goto Lerr;
				}
				Identifier id = token.ident;

				if (id is Id.system)
					safe = true;
				else
					error("(safe) expected, not %s", id.toChars());
				nextToken();
				check(TOK.TOKrparen);
			}
}
}

			if (token.value != TOK.TOKidentifier)
			{
				error("Identifier expected following module");
				goto Lerr;
			}
			else
			{
				Identifiers a = null;
				Identifier id = token.ident;
				while (nextToken() == TOK.TOKdot)
				{
					if (!a)
						a = new Identifiers();
					a.push(id);
					nextToken();
					if (token.value != TOK.TOKidentifier)
					{   error("Identifier expected following package");
						goto Lerr;
					}
					id = token.ident;
				}

				md = new ModuleDeclaration(a, id, safe);

				if (token.value != TOK.TOKsemicolon)
					error("';' expected following module declaration instead of %s", token.toChars());

				nextToken();
				addComment(mod, comment);
			}
		}

		decldefs = parseDeclDefs(0);
		if (token.value != TOK.TOKeof)
		{
			error("unrecognized declaration");
			goto Lerr;
		}

		return decldefs;

	Lerr:
		while (token.value != TOK.TOKsemicolon && token.value != TOK.TOKeof)
			nextToken();

		nextToken();
		return new Dsymbols();
	}
	
    Dsymbols parseDeclDefs(int once)
	{
		Dsymbol s;
		Dsymbols decldefs;
		Dsymbols a;
		Dsymbols aelse;
		PROT prot;
		StorageClass stc;
		StorageClass storageClass;
		Condition  condition;
		string comment;

		//printf("Parser.parseDeclDefs()\n");
		decldefs = new Dsymbols();
		do
		{
		comment = token.blockComment;
		storageClass = STC.STCundefined;
		switch (token.value)
		{
			case TOK.TOKenum:
			{	/* Determine if this is a manifest constant declaration,
			 * or a conventional enum.
			 */
			Token *t = peek(&token);
			if (t.value == TOK.TOKlcurly || t.value == TOK.TOKcolon)
				s = parseEnum();
			else if (t.value != TOK.TOKidentifier)
				goto Ldeclaration;
			else
			{
				t = peek(t);
				if (t.value == TOK.TOKlcurly || t.value == TOK.TOKcolon ||
				t.value == TOK.TOKsemicolon)
				s = parseEnum();
				else
				goto Ldeclaration;
			}
			break;
			}

			case TOK.TOKstruct:
			case TOK.TOKunion:
			case TOK.TOKclass:
			case TOK.TOKinterface:
			s = parseAggregate();
			break;

			case TOK.TOKimport:
			s = parseImport(decldefs, 0);
			break;

			case TOK.TOKtemplate:
			s = cast(Dsymbol)parseTemplateDeclaration();
			break;

			case TOK.TOKmixin:
			{	Loc loc = this.loc;
			if (peek(&token).value == TOK.TOKlparen)
			{   // mixin(string)
				nextToken();
				check(TOK.TOKlparen, "mixin");
				Expression e = parseAssignExp();
				check(TOK.TOKrparen);
				check(TOK.TOKsemicolon);
				s = new CompileDeclaration(loc, e);
				break;
			}
			s = parseMixin();
			break;
			}

			case TOK.TOKwchar: case TOK.TOKdchar:
			case TOK.TOKbit: case TOK.TOKbool: case TOK.TOKchar:
			case TOK.TOKint8: case TOK.TOKuns8:
			case TOK.TOKint16: case TOK.TOKuns16:
			case TOK.TOKint32: case TOK.TOKuns32:
			case TOK.TOKint64: case TOK.TOKuns64:
			case TOK.TOKfloat32: case TOK.TOKfloat64: case TOK.TOKfloat80:
			case TOK.TOKimaginary32: case TOK.TOKimaginary64: case TOK.TOKimaginary80:
			case TOK.TOKcomplex32: case TOK.TOKcomplex64: case TOK.TOKcomplex80:
			case TOK.TOKvoid:
			case TOK.TOKalias:
			case TOK.TOKtypedef:
			case TOK.TOKidentifier:
			case TOK.TOKtypeof:
			case TOK.TOKdot:
			Ldeclaration:
			a = parseDeclarations(STC.STCundefined);
			decldefs.append(a);
			continue;

			case TOK.TOKthis:
			s = parseCtor();
			break;

static if (false) { // dead end, use this(this){} instead
			case TOK.TOKassign:
			s = parsePostBlit();
			break;
}
			case TOK.TOKtilde:
			s = parseDtor();
			break;

			case TOK.TOKinvariant:
			{	Token *t;
			t = peek(&token);
			if (t.value == TOK.TOKlparen)
			{
				if (peek(t).value == TOK.TOKrparen)
				// invariant() forms start of class invariant
				s = parseInvariant();
				else
				// invariant(type)
				goto Ldeclaration;
			}
			else
			{
				stc = STC.STCimmutable;
				goto Lstc;
			}
			break;
			}

			case TOK.TOKunittest:
			s = parseUnitTest();
			break;

			case TOK.TOKnew:
			s = parseNew();
			break;

			case TOK.TOKdelete:
			s = parseDelete();
			break;

			case TOK.TOKeof:
			case TOK.TOKrcurly:
			return decldefs;

			case TOK.TOKstatic:
			nextToken();
			if (token.value == TOK.TOKthis)
				s = parseStaticCtor();
			else if (token.value == TOK.TOKtilde)
				s = parseStaticDtor();
			else if (token.value == TOK.TOKassert)
				s = parseStaticAssert();
			else if (token.value == TOK.TOKif)
			{   condition = parseStaticIfCondition();
				a = parseBlock();
				aelse = null;
				if (token.value == TOK.TOKelse)
				{   nextToken();
				aelse = parseBlock();
				}
				s = new StaticIfDeclaration(condition, a, aelse);
				break;
			}
			else if (token.value == TOK.TOKimport)
			{
				s = parseImport(decldefs, 1);
			}
			else
			{   stc = STC.STCstatic;
				goto Lstc2;
			}
			break;

			case TOK.TOKconst:
			if (peek(&token).value == TOK.TOKlparen)
				goto Ldeclaration;
			stc = STC.STCconst;
			goto Lstc;

			case TOK.TOKimmutable:
			if (peek(&token).value == TOK.TOKlparen)
				goto Ldeclaration;
			stc = STC.STCimmutable;
			goto Lstc;

			case TOK.TOKshared:
			if (peek(&token).value == TOK.TOKlparen)
				goto Ldeclaration;
			stc = STC.STCshared;
			goto Lstc;

	        case TOKwild:
		    if (peek(&token).value == TOK.TOKlparen)
		        goto Ldeclaration;
		    stc = STCwild;
		    goto Lstc;

			case TOK.TOKfinal:	  stc = STC.STCfinal;	 goto Lstc;
			case TOK.TOKauto:	  stc = STC.STCauto;	 goto Lstc;
			case TOK.TOKscope:	  stc = STC.STCscope;	 goto Lstc;
			case TOK.TOKoverride:	  stc = STC.STCoverride;	 goto Lstc;
			case TOK.TOKabstract:	  stc = STC.STCabstract;	 goto Lstc;
			case TOK.TOKsynchronized: stc = STC.STCsynchronized; goto Lstc;
			case TOK.TOKdeprecated:   stc = STC.STCdeprecated;	 goto Lstc;
version (DMDV2) {
			case TOK.TOKnothrow:      stc = STC.STCnothrow;	 goto Lstc;
			case TOK.TOKpure:         stc = STC.STCpure;	 goto Lstc;
			case TOK.TOKref:          stc = STC.STCref;          goto Lstc;
			case TOK.TOKtls:          stc = STC.STCtls;		 goto Lstc;
			case TOK.TOKgshared:      
				stc = STC.STCgshared;	 goto Lstc;
			//case TOK.TOKmanifest:	  stc = STC.STCmanifest;	 goto Lstc;
	        case TOK.TOKat:           stc = parseAttribute(); goto Lstc;
}

			Lstc:
			if (storageClass & stc)
				error("redundant storage class %s", Token.toChars(token.value));
			composeStorageClass(storageClass | stc);
			nextToken();
			Lstc2:
			storageClass |= stc;
			switch (token.value)
			{
				case TOK.TOKconst:
				case TOK.TOKinvariant:
				case TOK.TOKimmutable:
				case TOK.TOKshared:
		        case TOKwild:
				// If followed by a (, it is not a storage class
				if (peek(&token).value == TOK.TOKlparen)
					break;
				if (token.value == TOK.TOKconst)
					stc = STC.STCconst;
				else if (token.value == TOK.TOKshared)
					stc = STC.STCshared;
			    else if (token.value == TOKwild)
			        stc = STC.STCwild;
				else
					stc = STC.STCimmutable;
				goto Lstc;
				case TOK.TOKfinal:	  stc = STC.STCfinal;	 goto Lstc;
				case TOK.TOKauto:	  stc = STC.STCauto;	 goto Lstc;
				case TOK.TOKscope:	  stc = STC.STCscope;	 goto Lstc;
				case TOK.TOKoverride:	  stc = STC.STCoverride;	 goto Lstc;
				case TOK.TOKabstract:	  stc = STC.STCabstract;	 goto Lstc;
				case TOK.TOKsynchronized: stc = STC.STCsynchronized; goto Lstc;
				case TOK.TOKdeprecated:   stc = STC.STCdeprecated;	 goto Lstc;
				case TOK.TOKnothrow:      stc = STC.STCnothrow;	 goto Lstc;
				case TOK.TOKpure:         stc = STC.STCpure;	 goto Lstc;
				case TOK.TOKref:          stc = STC.STCref;          goto Lstc;
				case TOK.TOKtls:          stc = STC.STCtls;		 goto Lstc;
				case TOK.TOKgshared:      stc = STC.STCgshared;	 goto Lstc;
				//case TOK.TOKmanifest:	  stc = STC.STCmanifest;	 goto Lstc;
		        case TOK.TOKat:           stc = parseAttribute(); goto Lstc;
				default:
				break;
			}

			/* Look for auto initializers:
			 *	storage_class identifier = initializer;
			 */
			if (token.value == TOK.TOKidentifier &&
				peek(&token).value == TOK.TOKassign)
			{
				a = parseAutoDeclarations(storageClass, comment);
				decldefs.append(a);
				continue;
			}

			/* Look for return type inference for template functions.
			 */
			Token *tk;
			if (token.value == TOK.TOKidentifier &&
				(tk = peek(&token)).value == TOK.TOKlparen &&
				skipParens(tk, &tk) &&
				(peek(tk).value == TOK.TOKlparen ||
				 peek(tk).value == TOK.TOKlcurly)
			   )
			{
				a = parseDeclarations(storageClass);
				decldefs.append(a);
				continue;
			}
			a = parseBlock();
			s = new StorageClassDeclaration(storageClass, a);
			break;

			case TOK.TOKextern:
				if (peek(&token).value != TOK.TOKlparen)
				{   
					stc = STC.STCextern;
					goto Lstc;
				}
				{
					LINK linksave = linkage;
					linkage = parseLinkage();
					a = parseBlock();
					s = new LinkDeclaration(linkage, a);
					linkage = linksave;
					break;
				}

			case TOK.TOKprivate:	prot = PROT.PROTprivate;	goto Lprot;
			case TOK.TOKpackage:	prot = PROT.PROTpackage;	goto Lprot;
			case TOK.TOKprotected:	prot = PROT.PROTprotected;	goto Lprot;
			case TOK.TOKpublic:		prot = PROT.PROTpublic;		goto Lprot;
			case TOK.TOKexport:		prot = PROT.PROTexport;		goto Lprot;
				Lprot:
				nextToken();
				switch (token.value)
				{
					case TOK.TOKprivate:
					case TOK.TOKpackage:
					case TOK.TOKprotected:
					case TOK.TOKpublic:
					case TOK.TOKexport:
						error("redundant protection attribute");
						break;
					default:
						break;
				}
				a = parseBlock();
				s = new ProtDeclaration(prot, a);
				break;

			case TOK.TOKalign:
			{	uint n;

			s = null;
			nextToken();
			if (token.value == TOK.TOKlparen)
			{
				nextToken();
				if (token.value == TOK.TOKint32v)
				n = cast(uint)token.uns64value;
				else
				{	error("integer expected, not %s", token.toChars());
				n = 1;
				}
				nextToken();
				check(TOK.TOKrparen);
			}
			else
				n = global.structalign;		// default

			a = parseBlock();
			s = new AlignDeclaration(n, a);
			break;
			}

			case TOK.TOKpragma:
			{	Identifier ident;
			Expressions args = null;

			nextToken();
			check(TOK.TOKlparen);
			if (token.value != TOK.TOKidentifier)
			{   error("pragma(identifier expected");
				goto Lerror;
			}
			ident = token.ident;
			nextToken();
			if (token.value == TOK.TOKcomma && peekNext() != TOK.TOKrparen)
				args = parseArguments();	// pragma(identifier, args...)
			else
				check(TOK.TOKrparen);		// pragma(identifier)

			if (token.value == TOK.TOKsemicolon)
				a = null;
			else
				a = parseBlock();
			s = new PragmaDeclaration(loc, ident, args, a);
			break;
			}

			case TOK.TOKdebug:
			nextToken();
			if (token.value == TOK.TOKassign)
			{
				nextToken();
				if (token.value == TOK.TOKidentifier)
				s = new DebugSymbol(loc, token.ident);
				else if (token.value == TOK.TOKint32v)
				s = new DebugSymbol(loc, cast(uint)token.uns64value);
				else
				{	error("identifier or integer expected, not %s", token.toChars());
				s = null;
				}
				nextToken();
				if (token.value != TOK.TOKsemicolon)
				error("semicolon expected");
				nextToken();
				break;
			}

			condition = parseDebugCondition();
			goto Lcondition;

			case TOK.TOKversion:
			nextToken();
			if (token.value == TOK.TOKassign)
			{
				nextToken();
				if (token.value == TOK.TOKidentifier)
				s = new VersionSymbol(loc, token.ident);
				else if (token.value == TOK.TOKint32v)
				s = new VersionSymbol(loc, cast(uint)token.uns64value);
				else
				{	error("identifier or integer expected, not %s", token.toChars());
				s = null;
				}
				nextToken();
				if (token.value != TOK.TOKsemicolon)
				error("semicolon expected");
				nextToken();
				break;
			}
			condition = parseVersionCondition();
			goto Lcondition;

			Lcondition:
			a = parseBlock();
			aelse = null;
			if (token.value == TOK.TOKelse)
			{   nextToken();
				aelse = parseBlock();
			}
			s = new ConditionalDeclaration(condition, a, aelse);
			break;

			case TOK.TOKsemicolon:		// empty declaration
			nextToken();
			continue;

			default:
			error("Declaration expected, not '%s'",token.toChars());
			Lerror:
			while (token.value != TOK.TOKsemicolon && token.value != TOK.TOKeof)
				nextToken();
			nextToken();
			s = null;
			continue;
		}
		if (s)
		{   decldefs.push(s);
			addComment(s, comment);
		}
		} while (!once);
		return decldefs;
	}
	
	/*****************************************
	 * 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 ';'
	 */
version (DMDV2)
{
    Dsymbols parseAutoDeclarations(StorageClass storageClass, const(char)[] comment)
	{
		auto a = new Dsymbols;

		while (true)
		{
			Identifier ident = token.ident;
			nextToken();		// skip over ident
			assert(token.value == TOKassign);
			nextToken();		// skip over '='
			Initializer init = parseInitializer();
			auto 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;
	}
}
	/********************************************
	 * Parse declarations after an align, protection, or extern decl.
	 */
    Dsymbols parseBlock()
	{
		Dsymbols a = null;
		Dsymbol ss;

		//printf("parseBlock()\n");
		switch (token.value)
		{
		case TOK.TOKsemicolon:
			error("declaration expected following attribute, not ';'");
			nextToken();
			break;

		case TOK.TOKeof:
			error("declaration expected following attribute, not EOF");
			break;

		case TOK.TOKlcurly:
			nextToken();
			a = parseDeclDefs(0);
			if (token.value != TOK.TOKrcurly)
			{   /* { */
				error("matching '}' expected, not %s", token.toChars());
			}
			else
				nextToken();
			break;

		case TOK.TOKcolon:
			nextToken();
static if (false) {
			a = null;
} else {
			a = parseDeclDefs(0);	// grab declarations up to closing curly bracket
}
			break;

		default:
			a = parseDeclDefs(1);
			break;
		}
		return a;
	}
version(DMDV2) {
    void composeStorageClass(StorageClass stc)
	{
		StorageClass u = stc;
		u &= STC.STCconst | STC.STCimmutable | STC.STCmanifest;
		if (u & (u - 1))
			error("conflicting storage class %s", Token.toChars(token.value));

		u = stc;
		u &= STC.STCgshared | STC.STCshared | STC.STCtls;
		if (u & (u - 1))
			error("conflicting storage class %s", Token.toChars(token.value));
        u = stc;
        u &= STCsafe | STCsystem | STCtrusted;
        if (u & (u - 1))
	        error("conflicting attribute @%s", token.toChars());
	}
}
    
/***********************************************
 * Parse storage class, lexer is on '@'
 */

version(DMDV2) {
    StorageClass parseAttribute()
    {
        nextToken();
        StorageClass stc = STCundefined;
        if (token.value != TOKidentifier)
        {
	        error("identifier expected after @, not %s", token.toChars());
        }
        else if (token.ident == Id.property)
	        stc = STCproperty;
        else if (token.ident == Id.safe)
	        stc = STCsafe;
        else if (token.ident == Id.trusted)
	        stc = STCtrusted;
        else if (token.ident == Id.system)
	        stc = STCsystem;
        else
	        error("valid attribute identifiers are @property, @safe, @trusted, @system, not @%s", token.toChars());
        return stc;
    }
}
	/**************************************
	 * Parse constraint.
	 * Constraint is of the form:
	 *	if ( ConstraintExpression )
	 */
version (DMDV2) {
    Expression parseConstraint()
	{
		Expression e = null;

		if (token.value == TOKif)
		{
			nextToken();	// skip over 'if'
			check(TOKlparen);
			e = parseExpression();
			check(TOKrparen);
		}
		return e;
	}
}
	/**************************************
	 * Parse a TemplateDeclaration.
	 */
    TemplateDeclaration parseTemplateDeclaration()
	{
		TemplateDeclaration tempdecl;
		Identifier id;
		TemplateParameters tpl;
		Dsymbols 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 parseTemplateParameterList(int flag = 0)
	{
		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 (true)
			{   
				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);
				}
///		version (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);
				}
///		}
				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 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 = cast(Identifier)tempinst;
				tiargs = null;
			}
			idents.push(cast(void*)id);
			nextToken();
			if (token.value != TOKidentifier)
			{   
				error("identifier expected following '.' instead of '%s'", token.toChars());
				break;
			}
			id = token.ident;
			nextToken();
		}
		idents.push(cast(void*)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 parseTemplateArgumentList()
	{
		//printf("Parser.parseTemplateArgumentList()\n");
		if (token.value != TOKlparen && token.value != TOKlcurly)
		{   
			error("!(TemplateArgumentList) expected following TemplateIdentifier");
			return new Objects();
		}
		return parseTemplateArgumentList2();
	}
	
    Objects parseTemplateArgumentList2()
	{
		//printf("Parser.parseTemplateArgumentList2()\n");
		Objects tiargs = new Objects();
		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();

					if (ea.op == TOKfunction)
					{   
						FuncLiteralDeclaration fd = (cast(FuncExp)ea).fd;
						if (fd.type.ty == Tfunction)
						{
							TypeFunction tf = cast(TypeFunction)fd.type;
							/* If there are parameters that consist of only an identifier,
							 * rather than assuming the identifier is a type, as we would
							 * for regular function declarations, assume the identifier
							 * is the parameter name, and we're building a template with
							 * a deduced type.
							 */
							TemplateParameters tpl = null;
							foreach (param; tf.parameters)
							{   
								if (param.ident is null &&
									param.type &&
									param.type.ty == Tident &&
									(cast(TypeIdentifier)param.type).idents.dim == 0
								   )
								{
									/* Switch parameter type to parameter identifier,
									 * parameterize with template type parameter _T
									 */
									auto pt = cast(TypeIdentifier)param.type;
									param.ident = pt.ident;
									Identifier id = Lexer.uniqueId("__T");
									param.type = new TypeIdentifier(pt.loc, id);
									auto tp = new TemplateTypeParameter(fd.loc, id, null, null);
									if (!tpl)
										tpl = new TemplateParameters();
									tpl.push(tp);
								}
							}

							if (tpl)
							{   
								// Wrap a template around function fd
								auto decldefs = new Dsymbols();
								decldefs.push(fd);
								auto tempdecl = new TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs);
								tempdecl.literal = 1;	// it's a template 'literal'
								tiargs.push(tempdecl);
								goto L1;
							}
						}
					}

					tiargs.push(ea);
				}
			 L1:
				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 parseTemplateArgument()
	{
		//printf("parseTemplateArgument()\n");
		Objects tiargs = new Objects();
		Type ta;
		switch (token.value)
		{
			case TOKidentifier:
				ta = new TypeIdentifier(loc, token.ident);
				goto LabelX;

			case TOKvoid:	 ta = Type.tvoid;  goto LabelX;
			case TOKint8:	 ta = Type.tint8;  goto LabelX;
			case TOKuns8:	 ta = Type.tuns8;  goto LabelX;
			case TOKint16:	 ta = Type.tint16; goto LabelX;
			case TOKuns16:	 ta = Type.tuns16; goto LabelX;
			case TOKint32:	 ta = Type.tint32; goto LabelX;
			case TOKuns32:	 ta = Type.tuns32; goto LabelX;
			case TOKint64:	 ta = Type.tint64; goto LabelX;
			case TOKuns64:	 ta = Type.tuns64; goto LabelX;
			case TOKfloat32: ta = Type.tfloat32; goto LabelX;
			case TOKfloat64: ta = Type.tfloat64; goto LabelX;
			case TOKfloat80: ta = Type.tfloat80; goto LabelX;
			case TOKimaginary32: ta = Type.timaginary32; goto LabelX;
			case TOKimaginary64: ta = Type.timaginary64; goto LabelX;
			case TOKimaginary80: ta = Type.timaginary80; goto LabelX;
			case TOKcomplex32: ta = Type.tcomplex32; goto LabelX;
			case TOKcomplex64: ta = Type.tcomplex64; goto LabelX;
			case TOKcomplex80: ta = Type.tcomplex80; goto LabelX;
			case TOKbit:	 ta = Type.tbit;     goto LabelX;
			case TOKbool:	 ta = Type.tbool;    goto LabelX;
			case TOKchar:	 ta = Type.tchar;    goto LabelX;
			case TOKwchar:	 ta = Type.twchar; goto LabelX;
			case TOKdchar:	 ta = Type.tdchar; goto LabelX;
			LabelX:
				tiargs.push(ta);
				nextToken();
				break;

			case TOKint32v:
			case TOKuns32v:
			case TOKint64v:
			case TOKuns64v:
			case 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;
	}
	
	/**********************************
	 * Parse a static assertion.
	 */
    StaticAssert parseStaticAssert()
	{
		Loc loc = this.loc;
		Expression exp;
		Expression msg = null;

		//printf("parseStaticAssert()\n");
		nextToken();
		check(TOK.TOKlparen);
		exp = parseAssignExp();
		if (token.value == TOK.TOKcomma)
		{	
			nextToken();
			msg = parseAssignExp();
		}

		check(TOK.TOKrparen);
		check(TOK.TOKsemicolon);
	
		return new StaticAssert(loc, exp, msg);
	}
	
    TypeQualified parseTypeof()
	{
		TypeQualified t;
		Loc loc = this.loc;

		nextToken();
		check(TOK.TOKlparen);
		if (token.value == TOK.TOKreturn)	// typeof(return)
		{
			nextToken();
			t = new TypeReturn(loc);
		}
		else
		{	
			Expression exp = parseExpression();	// typeof(expression)
			t = new TypeTypeof(loc, exp);
		}
		check(TOK.TOKrparen);
		return t;
	}
	
	/***********************************
	 * Parse extern (linkage)
	 * The parser is on the 'extern' token.
	 */
    LINK parseLinkage()
	{
		LINK link = LINK.LINKdefault;
		nextToken();
		assert(token.value == TOK.TOKlparen);
		nextToken();
		if (token.value == TOK.TOKidentifier)
		{   
			Identifier id = token.ident;

			nextToken();
			if (id == Id.Windows)
				link = LINK.LINKwindows;
			else if (id == Id.Pascal)
				link = LINK.LINKpascal;
			else if (id == Id.D)
				link = LINK.LINKd;
			else if (id == Id.C)
			{
				link = LINK.LINKc;
				if (token.value == TOK.TOKplusplus)
				{   
					link = LINK.LINKcpp;
					nextToken();
				}
			}
			else if (id == Id.System)
			{
version (Windows)
{
				link = LINK.LINKwindows;
}
else
{
				link = LINK.LINKc;
}
			}
			else
			{
				error("valid linkage identifiers are D, C, C++, Pascal, Windows, System");
				link = LINK.LINKd;
			}
		}
		else
		{
			link = LINK.LINKd;		// default
		}
		check(TOK.TOKrparen);

		return link;
	}


	/**************************************
	 * Parse a debug conditional
	 */	
    Condition parseDebugCondition()
	{
		Condition c;

		if (token.value == TOK.TOKlparen)
		{
			nextToken();
			uint level = 1;
			Identifier id = null;

			if (token.value == TOK.TOKidentifier)
				id = token.ident;
			else if (token.value == TOK.TOKint32v)
				level = cast(uint)token.uns64value;
			else
				error("identifier or integer expected, not %s", token.toChars());

			nextToken();
			check(TOK.TOKrparen);

			c = new DebugCondition(mod, level, id);
		}
		else
			c = new DebugCondition(mod, 1, null);

		return c;
	}
	
	/**************************************
	 * Parse a version conditional
	 */
    Condition parseVersionCondition()
	{
		Condition c;
		uint level = 1;
		Identifier id = null;

		if (token.value == TOK.TOKlparen)
		{
			nextToken();
			if (token.value == TOK.TOKidentifier)
				id = token.ident;
			else if (token.value == TOK.TOKint32v)
				level = cast(uint)token.uns64value;
			else {
version (DMDV2) {
				/* Allow:
				 *    version (unittest)
				 * even though unittest is a keyword
				 */
				if (token.value == TOK.TOKunittest)
					id = Lexer.idPool(Token.toChars(TOK.TOKunittest));
				else
					error("identifier or integer expected, not %s", token.toChars());
			} else {
				error("identifier or integer expected, not %s", token.toChars());
}
			}
			nextToken();
			check(TOK.TOKrparen);
		}
		else
		   error("(condition) expected following version");

		c = new VersionCondition(mod, level, id);

		return c;
	}

	/***********************************************
	 *	static if (expression)
	 *	    body
	 *	else
	 *	    body
	 */
    Condition 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(parameters) { body }
	 * or postblit:
	 *	this(this) { body }
	 * or constructor template:
	 *	this(templateparameters)(parameters) { body }
	 * Current token is 'this'.
	 */
	
    Dsymbol parseCtor()
	{
		Loc loc = this.loc;

		nextToken();
		if (token.value == TOK.TOKlparen && peek(&token).value == TOK.TOKthis)
		{	// this(this) { ... }
			nextToken();
			nextToken();
			check(TOK.TOKrparen);
			auto f = new PostBlitDeclaration(loc, Loc(0));
			parseContracts(f);
			return f;
		}

		/* Look ahead to see if:
		 *   this(...)(...)
		 * which is a constructor template
		 */
		TemplateParameters tpl = null;
		if (token.value == TOK.TOKlparen && peekPastParen(&token).value == TOK.TOKlparen)
		{	tpl = parseTemplateParameterList();

			int varargs;
			auto arguments = parseParameters(&varargs);

			Expression constraint = null;
			if (tpl)
				constraint = parseConstraint();

			CtorDeclaration f = new CtorDeclaration(loc, Loc(0), arguments, varargs);
			parseContracts(f);

			// Wrap a template around it
			auto decldefs = new Dsymbols();
			decldefs.push(f);
			auto tempdecl =	new TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
			return tempdecl;
		}

		/* Just a regular constructor
		 */
		int varargs;
		auto arguments = parseParameters(&varargs);
		CtorDeclaration f = new CtorDeclaration(loc, Loc(0), arguments, varargs);
		parseContracts(f);
		return f;
	}
	
    PostBlitDeclaration parsePostBlit()
	{
		assert(false);
	}
	
	/*****************************************
	 * Parse a destructor definition:
	 *	~this() { body }
	 * Current token is '~'.
	 */
    DtorDeclaration parseDtor()
	{
		DtorDeclaration f;
		Loc loc = this.loc;

		nextToken();
		check(TOKthis);
		check(TOKlparen);
		check(TOKrparen);

		f = new DtorDeclaration(loc, Loc(0));
		parseContracts(f);
		return f;
	}
	
	/*****************************************
	 * Parse a static constructor definition:
	 *	static this() { body }
	 * Current token is 'this'.
	 */
    StaticCtorDeclaration parseStaticCtor()
	{
		StaticCtorDeclaration f;
		Loc loc = this.loc;

		nextToken();
		check(TOKlparen);
		check(TOKrparen);

		f = new StaticCtorDeclaration(loc, Loc(0));
		parseContracts(f);
		return f;
	}
	
	/*****************************************
	 * Parse a static destructor definition:
	 *	static ~this() { body }
	 * Current token is '~'.
	 */
    StaticDtorDeclaration parseStaticDtor()
	{
		StaticDtorDeclaration f;
		Loc loc = this.loc;

		nextToken();
		check(TOKthis);
		check(TOKlparen);
		check(TOKrparen);

		f = new StaticDtorDeclaration(loc, Loc(0));
		parseContracts(f);
		return f;
	}
	
	/*****************************************
	 * Parse an invariant definition:
	 *	invariant() { body }
	 * Current token is 'invariant'.
	 */
    InvariantDeclaration parseInvariant()
	{
		InvariantDeclaration f;
		Loc loc = this.loc;

		nextToken();
		if (token.value == TOKlparen)	// optional ()
		{
			nextToken();
			check(TOKrparen);
		}

		f = new InvariantDeclaration(loc, Loc(0));
		f.fbody = parseStatement(ParseStatementFlags.PScurly);
		return f;
	}
	
	/*****************************************
	 * Parse a unittest definition:
	 *	unittest { body }
	 * Current token is 'unittest'.
	 */
    UnitTestDeclaration parseUnitTest()
	{
		Loc loc = this.loc;

		nextToken();

		UnitTestDeclaration f = new UnitTestDeclaration(loc, this.loc);
		f.fbody = parseStatement(ParseStatementFlags.PScurly);

		return f;
	}
	
	/*****************************************
	 * Parse a new definition:
	 *	new(arguments) { body }
	 * Current token is 'new'.
	 */
    NewDeclaration parseNew()
	{
		NewDeclaration f;
		scope arguments = new Parameters();
		int varargs;
		Loc loc = this.loc;

		nextToken();
		arguments = parseParameters(&varargs);
		f = new NewDeclaration(loc, Loc(0), arguments, varargs);
		parseContracts(f);
		return f;
	}
	
	/*****************************************
	 * Parse a delete definition:
	 *	delete(arguments) { body }
	 * Current token is 'delete'.
	 */
    DeleteDeclaration parseDelete()
	{
		DeleteDeclaration f;
		scope Parameters 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, Loc(0), arguments);
		parseContracts(f);
		return f;
	}
	
    Parameters parseParameters(int* pvarargs)
	{
		auto arguments = new Parameters();
		int varargs = 0;
		int hasdefault = 0;

		check(TOK.TOKlparen);
		while (1)
		{   Type *tb;
		Identifier ai = null;
		Type at;
		Parameter a;
		StorageClass storageClass = STC.STCundefined;
		StorageClass stc;
		Expression ae;

		for ( ;1; nextToken())
		{
			switch (token.value)
			{
			case TOK.TOKrparen:
				break;

			case TOK.TOKdotdotdot:
				varargs = 1;
				nextToken();
				break;

			case TOK.TOKconst:
				if (peek(&token).value == TOK.TOKlparen)
				goto Ldefault;
				stc = STC.STCconst;
				goto L2;

			case TOK.TOKinvariant:
			case TOK.TOKimmutable:
				if (peek(&token).value == TOK.TOKlparen)
				goto Ldefault;
				stc = STC.STCimmutable;
				goto L2;

			case TOK.TOKshared:
				if (peek(&token).value == TOK.TOKlparen)
				goto Ldefault;
				stc = STC.STCshared;
				goto L2;
                
		    case TOKwild:
		        if (peek(&token).value == TOK.TOKlparen)
			    goto Ldefault;
		        stc = STCwild;
		        goto L2;

			case TOK.TOKin:	   stc = STC.STCin;		goto L2;
			case TOK.TOKout:	   stc = STC.STCout;	goto L2;
version(D1INOUT) {
			case TOK.TOKinout:
}
			case TOK.TOKref:	   stc = STC.STCref;	goto L2;
			case TOK.TOKlazy:	   stc = STC.STClazy;	goto L2;
			case TOK.TOKscope:	   stc = STC.STCscope;	goto L2;
			case TOK.TOKfinal:	   stc = STC.STCfinal;	goto L2;
		    case TOK.TOKauto:	   stc = STCauto;	    goto L2;
			L2:
				if (storageClass & stc ||
				(storageClass & STC.STCin && stc & (STC.STCconst | STC.STCscope)) ||
				(stc & STC.STCin && storageClass & (STC.STCconst | STC.STCscope))
				   )
				error("redundant storage class %s", Token.toChars(token.value));
				storageClass |= stc;
				composeStorageClass(storageClass);
				continue;

static if (false) {
			case TOK.TOKstatic:	   stc = STC.STCstatic;		goto L2;
			case TOK.TOKauto:   storageClass = STC.STCauto;		goto L4;
			case TOK.TOKalias:  storageClass = STC.STCalias;	goto L4;
			L4:
				nextToken();
				if (token.value == TOK.TOKidentifier)
				{	ai = token.ident;
				nextToken();
				}
				else
				ai = null;
				at = null;		// no type
				ae = null;		// no default argument
				if (token.value == TOK.TOKassign)	// = defaultArg
				{   nextToken();
				ae = parseDefaultInitExp();
				hasdefault = 1;
				}
				else
				{   if (hasdefault)
					error("default argument expected for alias %s",
						ai ? ai.toChars() : "");
				}
				goto L3;
}

			default:
			Ldefault:
				stc = (storageClass & (STC.STCin | STC.STCout | STC.STCref | STC.STClazy));
				if (stc & (stc - 1))	// if stc is not a power of 2
				error("incompatible parameter storage classes");
				if ((storageClass & (STC.STCconst | STC.STCout)) == (STC.STCconst | STC.STCout))
				error("out cannot be const");
				if ((storageClass & (STC.STCimmutable | STC.STCout)) == (STC.STCimmutable | STC.STCout))
				error("out cannot be immutable");
				if ((storageClass & STC.STCscope) &&
				(storageClass & (STC.STCref | STC.STCout)))
				error("scope cannot be ref or out");
				at = parseType(&ai);
				ae = null;
				if (token.value == TOK.TOKassign)	// = defaultArg
				{   nextToken();
				ae = parseDefaultInitExp();
				hasdefault = 1;
				}
				else
				{   if (hasdefault)
					error("default argument expected for %s",
						ai ? ai.toChars() : at.toChars());
				}
				if (token.value == TOK.TOKdotdotdot)
				{   /* This is:
				 *	at ai ...
				 */

				if (storageClass & (STC.STCout | STC.STCref))
					error("variadic argument cannot be out or ref");
				varargs = 2;
				a = new Parameter(storageClass, at, ai, ae);
				arguments.push(a);
				nextToken();
				break;
				}
			L3:
				a = new Parameter(storageClass, at, ai, ae);
				arguments.push(a);
				if (token.value == TOK.TOKcomma)
				{   nextToken();
				goto L1;
				}
				break;
			}
			break;
		}
		break;

		L1:	;
		}
		check(TOK.TOKrparen);
		*pvarargs = varargs;
		return arguments;
	}
	
    EnumDeclaration parseEnum()
	{
		EnumDeclaration e;
		Identifier id;
		Type memtype;
		Loc loc = this.loc;

		//printf("Parser.parseEnum()\n");
		nextToken();
		if (token.value == TOK.TOKidentifier)
		{
			id = token.ident;
			nextToken();
		}
		else
			id = null;

		if (token.value == TOK.TOKcolon)
		{
			nextToken();
			memtype = parseBasicType();
			memtype = parseDeclarator(memtype, null, null);
		}
		else
			memtype = null;

		e = new EnumDeclaration(loc, id, memtype);
		if (token.value == TOK.TOKsemicolon && id)
			nextToken();
		else if (token.value == TOK.TOKlcurly)
		{
			//printf("enum definition\n");
			e.members = new Dsymbols();
			nextToken();
			string comment = token.blockComment;
			while (token.value != TOK.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 == TOK.TOKidentifier &&
					(tp.value == TOK.TOKassign || tp.value == TOK.TOKcomma || tp.value == TOK.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 == TOK.TOKassign)
				{
					nextToken();
					value = parseAssignExp();
				}
				else
				{	
					value = null;
					if (type)
						error("if type, there must be an initializer");
				}

				auto em = new EnumMember(loc, ident, value, type);
				e.members.push(em);

				if (token.value == TOK.TOKrcurly) {
					;
				} else {
					addComment(em, comment);
					comment = null;
					check(TOK.TOKcomma);
				}
				addComment(em, comment);
				comment = token.blockComment;
			}
			nextToken();
		}
		else
			error("enum declaration is invalid");

		//printf("-parseEnum() %s\n", e.toChars());
		return e;
	}
	
    Dsymbol parseAggregate()
	{
		AggregateDeclaration a = null;
		int anon = 0;
		TOK tok;
		Identifier id;
		TemplateParameters tpl = null;
		Expression constraint = null;

		//printf("Parser.parseAggregate()\n");
		tok = token.value;
		nextToken();
		if (token.value != TOK.TOKidentifier)
		{
			id = null;
		}
		else
		{
			id = token.ident;
			nextToken();

			if (token.value == TOK.TOKlparen)
			{   
				// Class template declaration.

				// Gather template parameter list
				tpl = parseTemplateParameterList();
				constraint = parseConstraint();
			}
		}

		Loc loc = this.loc;
		switch (tok)
		{	case TOK.TOKclass:
		case TOK.TOKinterface:
		{
			if (!id)
			error("anonymous classes not allowed");

			// Collect base class(es)
			BaseClasses baseclasses = null;
			if (token.value == TOK.TOKcolon)
			{
				nextToken();
				baseclasses = parseBaseClasses();

				if (token.value != TOK.TOKlcurly)
					error("members expected");
			}

			if (tok == TOK.TOKclass)
				a = new ClassDeclaration(loc, id, baseclasses);
			else
				a = new InterfaceDeclaration(loc, id, baseclasses);
			break;
		}

		case TOK.TOKstruct:
			if (id)
			a = new StructDeclaration(loc, id);
			else
			anon = 1;
			break;

		case TOK.TOKunion:
			if (id)
			a = new UnionDeclaration(loc, id);
			else
			anon = 2;
			break;

		default:
			assert(0);
			break;
		}
		if (a && token.value == TOK.TOKsemicolon)
		{ 	nextToken();
		}
		else if (token.value == TOK.TOKlcurly)
		{
		//printf("aggregate definition\n");
		nextToken();
		auto decl = parseDeclDefs(0);
		if (token.value != TOK.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

		auto decldefs = new Dsymbols();
		decldefs.push(a);
		auto tempdecl =	new TemplateDeclaration(loc, id, tpl, constraint, decldefs);
		return tempdecl;
		}

		return a;
	}
	
    BaseClasses parseBaseClasses()
	{
		BaseClasses baseclasses = new BaseClasses();

		for (; 1; nextToken())
		{
			PROT protection = PROT.PROTpublic;
			switch (token.value)
			{
				case TOK.TOKprivate:
					protection = PROT.PROTprivate;
					nextToken();
					break;
				case TOK.TOKpackage:
					protection = PROT.PROTpackage;
					nextToken();
					break;
				case TOK.TOKprotected:
					protection = PROT.PROTprotected;
					nextToken();
					break;
				case TOK.TOKpublic:
					protection = PROT.PROTpublic;
					nextToken();
					break;
				default:
					break;	///
			}
			if (token.value == TOK.TOKidentifier)
			{
				auto b = new BaseClass(parseBasicType(), protection);
				baseclasses.push(b);
				if (token.value != TOK.TOKcomma)
					break;
			}
			else
			{
				error("base classes expected instead of %s", token.toChars());
				return null;
			}
		}
		return baseclasses;
	}
	
    Import parseImport(Dsymbols decldefs, int isstatic)
	{
		Import s;
		Identifier id;
		Identifier aliasid = null;
		Identifiers a;
		Loc loc;

		//printf("Parser.parseImport()\n");
		do
		{
		 L1:
			nextToken();
			if (token.value != TOK.TOKidentifier)
			{   
				error("Identifier expected following import");
				break;
			}

			loc = this.loc;
			a = null;
			id = token.ident;
			nextToken();
			if (!aliasid && token.value == TOK.TOKassign)
			{
				aliasid = id;
				goto L1;
			}
			while (token.value == TOK.TOKdot)
			{
				if (!a)
					a = new Identifiers();
				a.push(id);
				nextToken();
				if (token.value != TOK.TOKidentifier)
				{   
					error("identifier expected following package");
					break;
				}
				id = token.ident;
				nextToken();
			}

			s = new Import(loc, a, id, aliasid, isstatic);
			decldefs.push(s);

			/* Look for
			 *	: alias=name, alias=name;
			 * syntax.
			 */
			if (token.value == TOK.TOKcolon)
			{
				do
				{	
					Identifier name;

					nextToken();
					if (token.value != TOK.TOKidentifier)
					{   
						error("Identifier expected following :");
						break;
					}
					Identifier alias_ = token.ident;
					nextToken();
					if (token.value == TOK.TOKassign)
					{
						nextToken();
						if (token.value != TOK.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 == TOK.TOKcomma);

				break;	// no comma-separated imports of this form
			}

			aliasid = null;

		} while (token.value == TOK.TOKcomma);

		if (token.value == TOK.TOKsemicolon)
			nextToken();
		else
		{
			error("';' expected");
			nextToken();
		}

		return null;
	}
	
    Type parseType(Identifier* pident = null, TemplateParameters* tpl = null)
	{
		Type t;

		/* Take care of the storage class prefixes that
		 * serve as type attributes:
		 *  const shared, shared const, const, invariant, shared
		 */
		if (token.value == TOK.TOKconst && peekNext() == TOK.TOKshared && peekNext2() != TOK.TOKlparen ||
			token.value == TOK.TOKshared && peekNext() == TOK.TOKconst && peekNext2() != TOK.TOKlparen)
		{
			nextToken();
			nextToken();
			/* shared const type
			 */
			t = parseType(pident, tpl);
			t = t.makeSharedConst();
			return t;
		}
        else if (token.value == TOKwild && peekNext() == TOKshared && peekNext2() != TOKlparen ||
	             token.value == TOKshared && peekNext() == TOKwild && peekNext2() != TOKlparen)
        {
	        nextToken();
	        nextToken();
	        /* shared wild type
	         */
	        t = parseType(pident, tpl);
	        t = t.makeSharedWild();
	        return t;
        }
		else if (token.value == TOK.TOKconst && peekNext() != TOK.TOKlparen)
		{
			nextToken();
			/* const type
			 */
			t = parseType(pident, tpl);
			t = t.makeConst();
			return t;
		}
		else if ((token.value == TOK.TOKinvariant || token.value == TOK.TOKimmutable) &&
				 peekNext() != TOK.TOKlparen)
		{
			nextToken();
			/* invariant type
			 */
			t = parseType(pident, tpl);
			t = t.makeInvariant();
			return t;
		}
		else if (token.value == TOK.TOKshared && peekNext() != TOK.TOKlparen)
		{
			nextToken();
			/* shared type
			 */
			t = parseType(pident, tpl);
			t = t.makeShared();
			return t;
		}
        else if (token.value == TOKwild && peekNext() != TOKlparen)
        {
	        nextToken();
	        /* wild type
	         */
	        t = parseType(pident, tpl);
	        t = t.makeWild();
	        return t;
        }
		else
			t = parseBasicType();	
		t = parseDeclarator(t, pident, tpl);
		return t;
	}
	
    Type parseBasicType()
	{
		Type t;
		Identifier id;
		TypeQualified tid;

		//printf("parseBasicType()\n");
		switch (token.value)
		{
			case TOK.TOKvoid:	 t = Type.tvoid;  goto LabelX;
			case TOK.TOKint8:	 t = Type.tint8;  goto LabelX;
			case TOK.TOKuns8:	 t = Type.tuns8;  goto LabelX;
			case TOK.TOKint16:	 t = Type.tint16; goto LabelX;
			case TOK.TOKuns16:	 t = Type.tuns16; goto LabelX;
			case TOK.TOKint32:	 t = Type.tint32; goto LabelX;
			case TOK.TOKuns32:	 t = Type.tuns32; goto LabelX;
			case TOK.TOKint64:	 t = Type.tint64; goto LabelX;
			case TOK.TOKuns64:	 t = Type.tuns64; goto LabelX;
			case TOK.TOKfloat32: t = Type.tfloat32; goto LabelX;
			case TOK.TOKfloat64: t = Type.tfloat64; goto LabelX;
			case TOK.TOKfloat80: t = Type.tfloat80; goto LabelX;
			case TOK.TOKimaginary32: t = Type.timaginary32; goto LabelX;
			case TOK.TOKimaginary64: t = Type.timaginary64; goto LabelX;
			case TOK.TOKimaginary80: t = Type.timaginary80; goto LabelX;
			case TOK.TOKcomplex32: t = Type.tcomplex32; goto LabelX;
			case TOK.TOKcomplex64: t = Type.tcomplex64; goto LabelX;
			case TOK.TOKcomplex80: t = Type.tcomplex80; goto LabelX;
			case TOK.TOKbit:	 t = Type.tbit;     goto LabelX;
			case TOK.TOKbool:	 t = Type.tbool;    goto LabelX;
			case TOK.TOKchar:	 t = Type.tchar;    goto LabelX;
			case TOK.TOKwchar:	 t = Type.twchar; goto LabelX;
			case TOK.TOKdchar:	 t = Type.tdchar; goto LabelX;
			LabelX:
				nextToken();
				break;

		case TOK.TOKidentifier:
			id = token.ident;
			nextToken();
			if (token.value == TOK.TOKnot)
			{	// ident!(template_arguments)
			TemplateInstance tempinst = new TemplateInstance(loc, id);
			nextToken();
			if (token.value == TOK.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 == TOK.TOKdot)
			{	nextToken();
			if (token.value != TOK.TOKidentifier)
			{   error("identifier expected following '.' instead of '%s'", token.toChars());
				break;
			}
			id = token.ident;
			nextToken();
			if (token.value == TOK.TOKnot)
			{
				TemplateInstance tempinst = new TemplateInstance(loc, id);
				nextToken();
				if (token.value == TOK.TOKlparen)
				// ident!(template_arguments)
				tempinst.tiargs = parseTemplateArgumentList();
				else
				// ident!template_argument
				tempinst.tiargs = parseTemplateArgument();
				tid.addIdent(tempinst);
			}
			else
				tid.addIdent(id);
			}
			t = tid;
			break;

		case TOK.TOKdot:
			// Leading . as in .foo
			id = Id.empty;
			goto Lident;

		case TOK.TOKtypeof:
			// typeof(expression)
			tid = parseTypeof();
			goto Lident2;

		case TOK.TOKconst:
			// const(type)
			nextToken();
			check(TOK.TOKlparen);
			t = parseType();
			check(TOK.TOKrparen);
			if (t.isShared())
			t = t.makeSharedConst();
			else
			t = t.makeConst();
			break;

		case TOK.TOKinvariant:
		case TOK.TOKimmutable:
			// invariant(type)
			nextToken();
			check(TOK.TOKlparen);
			t = parseType();
			check(TOK.TOKrparen);
			t = t.makeInvariant();
			break;

		case TOK.TOKshared:
			// shared(type)
			nextToken();
			check(TOK.TOKlparen);
			t = parseType();
			check(TOK.TOKrparen);
			if (t.isConst())
			t = t.makeSharedConst();
	        else if (t.isWild())
		    t = t.makeSharedWild();
			else
			t = t.makeShared();
			break;

	    case TOKwild:
	        // wild(type)
	        nextToken();
	        check(TOK.TOKlparen);
	        t = parseType();
	        check(TOK.TOKrparen);
	        if (t.isShared())
		    t = t.makeSharedWild();
	        else
		    t = t.makeWild();
	        break;
            
		default:
			error("basic type expected, not %s", token.toChars());
			t = Type.tint32;
			break;
		}
		return t;
	}
	
    Type parseBasicType2(Type t)
	{
		//writef("parseBasicType2()\n");
		while (1)
		{
			switch (token.value)
			{
				case TOK.TOKmul:
				t = new TypePointer(t);
				nextToken();
				continue;
	
				case TOK.TOKlbracket:
				// Handle []. Make sure things like
				//     int[3][1] a;
				// is (array[1] of array[3] of int)
				nextToken();
				if (token.value == TOK.TOKrbracket)
				{
					t = new TypeDArray(t);			// []
					nextToken();
				}
				else if (token.value == TOKnew && peekNext() == TOKrbracket)
				{
					t = new TypeNewArray(t);			// [new]
					nextToken();
					nextToken();
				}
				else if (isDeclaration(&token, 0, TOK.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(TOK.TOKrbracket);
				}
				else
				{
					//printf("it's type[expression]\n");
					inBrackets++;
					Expression e = parseAssignExp();		// [ expression ]
					if (token.value == TOK.TOKslice)
					{
					nextToken();
					Expression e2 = parseAssignExp();	// [ exp .. exp ]
					t = new TypeSlice(t, e, e2);
					}
					else
					t = new TypeSArray(t,e);
					inBrackets--;
					check(TOK.TOKrbracket);
				}
				continue;
	
				case TOK.TOKdelegate:
				case TOK.TOKfunction:
				{	// Handle delegate declaration:
				//	t delegate(parameter list) nothrow pure
				//	t function(parameter list) nothrow pure
				Parameters arguments;
				int varargs;
				bool ispure = false;
				bool isnothrow = false;
		        bool isproperty = false;
				TOK save = token.value;
		        TRUST trust = TRUSTdefault;
	
				nextToken();
				arguments = parseParameters(&varargs);
				while (1)
				{   // Postfixes
					if (token.value == TOK.TOKpure)
					    ispure = true;
					else if (token.value == TOK.TOKnothrow)
					    isnothrow = true;
		            else if (token.value == TOKat)
		            {	StorageClass stc = parseAttribute();
			            switch (cast(uint)(stc >> 32))
			            {   case STCproperty >> 32:
				                isproperty = true;
				                break;
			                case STCsafe >> 32:
				                trust = TRUSTsafe;
				                break;
			                case STCsystem >> 32:
				                trust = TRUSTsystem;
				                break;
			                case STCtrusted >> 32:
				                trust = TRUSTtrusted;
				                break;
			                case 0:
				                break;
			                default:
    				            assert(0);
			            }
		            }
					else
					    break;
					nextToken();
				}
				TypeFunction tf = new TypeFunction(arguments, t, varargs, linkage);
				tf.ispure = ispure;
				tf.isnothrow = isnothrow;
		        tf.isproperty = isproperty;
		        tf.trust = trust;
				if (save == TOK.TOKdelegate)
					t = new TypeDelegate(tf);
				else
					t = new TypePointer(tf);	// pointer to function
				continue;
				}
	
				default:
				return t;
			}
			assert(0);
		}
		assert(0);
		return null;
	}
	
    Type parseDeclarator(Type t, Identifier* pident, TemplateParameters* tpl = null)
	{
		Type ts;

		//printf("parseDeclarator(tpl = %p)\n", tpl);
		t = parseBasicType2(t);

		switch (token.value)
		{

		case TOK.TOKidentifier:
			if (pident)
			*pident = token.ident;
			else
			error("unexpected identifer '%s' in declarator", token.ident.toChars());
			ts = t;
			nextToken();
			break;

		case TOK.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(TOK.TOKrparen);
			break;

		default:
			ts = t;
			break;
		}

		// parse DeclaratorSuffixes
		while (1)
		{
		switch (token.value)
		{
version (CARRAYDECL) {
			/* Support C style array syntax:
			 *   int ident[]
			 * as opposed to D-style:
			 *   int[] ident
			 */
			case TOK.TOKlbracket:
			{	// This is the old C-style post [] syntax.
			TypeNext ta;
			nextToken();
			if (token.value == TOK.TOKrbracket)
			{   // It's a dynamic array
				ta = new TypeDArray(t);		// []
				nextToken();
			}
			else if (token.value == TOKnew && peekNext() == TOKrbracket)
			{
				t = new TypeNewArray(t);		// [new]
			    nextToken();
			    nextToken();
			}
			else if (isDeclaration(&token, 0, TOK.TOKrbracket, null))
			{   // It's an associative array

				//printf("it's an associative array\n");
				Type index = parseType();		// [ type ]
				check(TOK.TOKrbracket);
				ta = new TypeAArray(t, index);
			}
			else
			{
				//printf("It's a static array\n");
				Expression e = parseAssignExp();	// [ expression ]
				ta = new TypeSArray(t, e);
				check(TOK.TOKrbracket);
			}

			/* Insert ta into
			 *   ts . ... . t
			 * so that
			 *   ts . ... . ta . t
			 */
			Type* pt;
			for (pt = &ts; *pt !is t; pt = &(cast(TypeNext)*pt).next) {
				;
			}
			*pt = ta;
			continue;
			}
}
			case TOK.TOKlparen:
			{
			if (tpl)
			{
				/* Look ahead to see if this is (...)(...),
				 * i.e. a function template declaration
				 */
				if (peekPastParen(&token).value == TOK.TOKlparen)
				{
				//printf("function template declaration\n");

				// Gather template parameter list
				*tpl = parseTemplateParameterList();
				}
			}

			int varargs;
			auto arguments = parseParameters(&varargs);
			Type tf = new TypeFunction(arguments, t, varargs, linkage);

			/* Parse const/invariant/nothrow/pure postfix
			 */
			while (1)
			{
				switch (token.value)
				{
					case TOK.TOKconst:
						if (tf.isShared())
						tf = tf.makeSharedConst();
						else
						tf = tf.makeConst();
						nextToken();
						continue;

					case TOK.TOKinvariant:
					case TOK.TOKimmutable:
						tf = tf.makeInvariant();
						nextToken();
						continue;

					case TOK.TOKshared:
						if (tf.isConst())
						tf = tf.makeSharedConst();
						else
						tf = tf.makeShared();
						nextToken();
						continue;

                    case TOKwild:
			            if (tf.isShared())
				        tf = tf.makeSharedWild();
			            else
				        tf = tf.makeWild();
			            nextToken();
			            continue;

					case TOK.TOKnothrow:
						(cast(TypeFunction)tf).isnothrow = 1;
						nextToken();
						continue;

					case TOK.TOKpure:
						(cast(TypeFunction)tf).ispure = 1;
						nextToken();
						continue;

					case TOK.TOKat:
    	            {
                        StorageClass stc = parseAttribute();
	                    auto tfunc = cast(TypeFunction)tf;
		                switch (cast(uint)(stc >> 32))
		                {
                        case STCproperty >> 32:
		                    tfunc.isproperty = 1;
			                break;
			            case STCsafe >> 32:
			                tfunc.trust = TRUSTsafe;
			                break;
			            case STCsystem >> 32:
			                tfunc.trust = TRUSTsystem;
			                break;
			            case STCtrusted >> 32:
			                tfunc.trust = TRUSTtrusted;
			                break;
			            case 0:
			                break;
			            default:
			                assert(0);
					    }
					    nextToken();
					    continue;
                    }
					default:
						break;	///
				}
				break;
			}

			/* Insert tf into
			 *   ts . ... . t
			 * so that
			 *   ts . ... . tf . t
			 */
			Type* pt;
			for (pt = &ts; *pt !is t; pt = &(cast(TypeNext)*pt).next) {
				;
			}
			*pt = tf;
			break;
			}
			
			default:
				break;	///
		}
		break;
		}

		return ts;
	}
	
    Dsymbols parseDeclarations(StorageClass storage_class)
	{
		StorageClass stc;
		Type ts;
		Type t;
		Type tfirst;
		Identifier ident;
		Dsymbols a;
		TOK tok = TOK.TOKreserved;
		string comment = token.blockComment;
		LINK link = linkage;

		//printf("parseDeclarations() %s\n", token.toChars());
		if (storage_class)
		{	ts = null;		// infer type
		goto L2;
		}

		switch (token.value)
		{
			case TOK.TOKalias:
				/* Look for:
				 *   alias identifier this;
				 */
				tok = token.value;
				nextToken();
				if (token.value == TOK.TOKidentifier && peek(&token).value == TOK.TOKthis)
				{
				AliasThis s = new AliasThis(this.loc, token.ident);
				nextToken();
				check(TOK.TOKthis);
				check(TOK.TOKsemicolon);
				a = new Dsymbols();
				a.push(s);
				addComment(s, comment);
				return a;
				}
				break;
			case TOK.TOKtypedef:
				tok = token.value;
				nextToken();
				break;
			default:
				break;
		}

		storage_class = STC.STCundefined;
		while (1)
		{
		switch (token.value)
		{
			case TOK.TOKconst:
			if (peek(&token).value == TOK.TOKlparen)
				break;		// const as type constructor
			stc = STC.STCconst;		// const as storage class
			goto L1;

			case TOK.TOKinvariant:
			case TOK.TOKimmutable:
			if (peek(&token).value == TOK.TOKlparen)
				break;
			stc = STC.STCimmutable;
			goto L1;

			case TOK.TOKshared:
			if (peek(&token).value == TOK.TOKlparen)
				break;
			stc = STC.STCshared;
		    goto L1;

	        case TOKwild:
		    if (peek(&token).value == TOK.TOKlparen)
		        break;
		    stc = STC.STCwild;
			goto L1;

			case TOK.TOKstatic:	stc = STC.STCstatic;	 goto L1;
			case TOK.TOKfinal:	stc = STC.STCfinal;		 goto L1;
			case TOK.TOKauto:	stc = STC.STCauto;		 goto L1;
			case TOK.TOKscope:	stc = STC.STCscope;		 goto L1;
			case TOK.TOKoverride:	stc = STC.STCoverride;	 goto L1;
			case TOK.TOKabstract:	stc = STC.STCabstract;	 goto L1;
			case TOK.TOKsynchronized: stc = STC.STCsynchronized; goto L1;
			case TOK.TOKdeprecated: stc = STC.STCdeprecated;	 goto L1;
version (DMDV2) {
			case TOK.TOKnothrow:    stc = STC.STCnothrow;	 goto L1;
			case TOK.TOKpure:       stc = STC.STCpure;		 goto L1;
			case TOK.TOKref:        stc = STC.STCref;            goto L1;
			case TOK.TOKtls:        stc = STC.STCtls;		 goto L1;
			case TOK.TOKgshared:    stc = STC.STCgshared;	 goto L1;
			case TOK.TOKenum:	stc = STC.STCmanifest;	 goto L1;
	        case TOK.TOKat:         stc = parseAttribute();  goto L1;
}
			L1:
			if (storage_class & stc)
				error("redundant storage class '%s'", token.toChars());
			storage_class = (storage_class | stc);
			composeStorageClass(storage_class);
			nextToken();
			continue;

			case TOK.TOKextern:
			if (peek(&token).value != TOK.TOKlparen)
			{   stc = STC.STCextern;
				goto L1;
			}

			link = parseLinkage();
			continue;

			default:
			break;
		}
		break;
		}

		/* Look for auto initializers:
		 *	storage_class identifier = initializer;
		 */
		if (storage_class &&
		token.value == TOK.TOKidentifier &&
		peek(&token).value == TOK.TOKassign)
		{
		return parseAutoDeclarations(storage_class, comment);
		}

		if (token.value == TOK.TOKclass)
		{
		AggregateDeclaration s = cast(AggregateDeclaration)parseAggregate();
		s.storage_class |= storage_class;
		a = new Dsymbols();
		a.push(s);
		addComment(s, comment);
		return a;
		}

		/* Look for return type inference for template functions.
		 */
		{
		Token *tk;
		if (storage_class &&
		token.value == TOK.TOKidentifier &&
		(tk = peek(&token)).value == TOK.TOKlparen &&
		skipParens(tk, &tk) &&
		peek(tk).value == TOK.TOKlparen)
		{
		ts = null;
		}
		else
		{
		ts = parseBasicType();
		ts = parseBasicType2(ts);
		}
		}

	L2:
		tfirst = null;
		a = new Dsymbols();

		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 == TOK.TOKtypedef || tok == TOK.TOKalias)
		{   Declaration v;
			Initializer init = null;

			if (token.value == TOK.TOKassign)
			{
			nextToken();
			init = parseInitializer();
			}
			if (tok == 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
			{
			auto ax = new Dsymbols();
			ax.push(v);
			Dsymbol s = new LinkDeclaration(link, ax);
			a.push(s);
			}
			switch (token.value)
			{   case TOK.TOKsemicolon:
				nextToken();
				addComment(v, comment);
				break;

			case TOK.TOKcomma:
				nextToken();
				addComment(v, comment);
				continue;

			default:
				error("semicolon expected to close %s declaration", Token.toChars(tok));
				break;
			}
		}
		else if (t.ty == TY.Tfunction)
		{
			auto tf = cast(TypeFunction)t;
			Expression constraint = null;
static if (false) {
			if (Parameter.isTPL(tf.parameters))
			{
			if (!tpl)
				tpl = new TemplateParameters();
			}
}
			FuncDeclaration f =
			new FuncDeclaration(loc, Loc(0), ident, storage_class, t);
			addComment(f, comment);
			if (tpl)
			constraint = parseConstraint();
			parseContracts(f);
			addComment(f, null);
			Dsymbol s;
			if (link == linkage)
			{
			s = f;
			}
			else
			{
			auto ax = new Dsymbols();
			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
			auto decldefs = new Dsymbols();
			decldefs.push(s);
			auto tempdecl =
				new TemplateDeclaration(loc, s.ident, tpl, constraint, decldefs);
			s = tempdecl;
			}
			addComment(s, comment);
			a.push(s);
		}
		else
		{
			Initializer init = null;
			if (token.value == TOK.TOKassign)
			{
			nextToken();
			init = parseInitializer();
			}

			VarDeclaration v = new VarDeclaration(loc, t, ident, init);
			v.storage_class = storage_class;
			if (link == linkage)
			a.push(v);
			else
			{
			auto ax = new Dsymbols();
			ax.push(v);
			auto s = new LinkDeclaration(link, ax);
			a.push(s);
			}
			switch (token.value)
			{   case TOK.TOKsemicolon:
				nextToken();
				addComment(v, comment);
				break;

			case TOK.TOKcomma:
				nextToken();
				addComment(v, comment);
				continue;

			default:
				error("semicolon expected, not '%s'", token.toChars());
				break;
			}
		}
		break;
		}
		return a;
	}
	
    void parseContracts(FuncDeclaration f)
	{
		LINK linksave = linkage;

		// The following is irrelevant, as it is overridden by sc.linkage in
		// TypeFunction.semantic
		linkage = LINK.LINKd;		// nested functions have D linkage
	L1:
		switch (token.value)
		{
		case TOK.TOKlcurly:
			if (f.frequire || f.fensure)
			error("missing body { ... } after in or out");
			f.fbody = parseStatement(ParseStatementFlags.PSsemi);
			f.endloc = endloc;
			break;

		case TOK.TOKbody:
			nextToken();
			f.fbody = parseStatement(ParseStatementFlags.PScurly);
			f.endloc = endloc;
			break;

		case TOK.TOKsemicolon:
			if (f.frequire || f.fensure)
			error("missing body { ... } after in or out");
			nextToken();
			break;

static if (false) {	// Do we want this for function declarations, so we can do:
		// int x, y, foo(), z;
		case TOK.TOKcomma:
			nextToken();
			continue;
}

static if (false) { // Dumped feature
		case TOK.TOKthrow:
			if (!f.fthrows)
			f.fthrows = new Array();
			nextToken();
			check(TOK.TOKlparen);
			while (1)
			{
			Type tb = parseBasicType();
			f.fthrows.push(tb);
			if (token.value == TOK.TOKcomma)
			{   nextToken();
				continue;
			}
			break;
			}
			check(TOK.TOKrparen);
			goto L1;
}

		case TOK.TOKin:
			nextToken();
			if (f.frequire)
			error("redundant 'in' statement");
			f.frequire = parseStatement(ParseStatementFlags.PScurly | ParseStatementFlags.PSscope);
			goto L1;

		case TOK.TOKout:
			// parse: out (identifier) { statement }
			nextToken();
			if (token.value != TOK.TOKlcurly)
			{
			check(TOK.TOKlparen);
			if (token.value != TOK.TOKidentifier)	   
				error("(identifier) following 'out' expected, not %s", token.toChars());
			f.outId = token.ident;
			nextToken();
			check(TOK.TOKrparen);
			}
			if (f.fensure)
			error("redundant 'out' statement");
			f.fensure = parseStatement(ParseStatementFlags.PScurly | ParseStatementFlags.PSscope);
			goto L1;

		default:
			error("semicolon expected following function declaration");
			break;
		}
		linkage = linksave;
	}
	
    Statement parseStatement(ParseStatementFlags flags)
	{
		Statement s;
		Token* t;
		Condition condition;
		Statement ifbody;
		Statement elsebody;
		bool isfinal;
		Loc loc = this.loc;

		//printf("parseStatement()\n");

		if (flags & ParseStatementFlags.PScurly && token.value != TOK.TOKlcurly)
			error("statement expected to be { }, not %s", token.toChars());

		switch (token.value)
		{
		case TOK.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 == TOK.TOKcolon)
			{	// It's a label

			Identifier ident = token.ident;
			nextToken();
			nextToken();
			s = parseStatement(ParseStatementFlags.PSsemi);
			s = new LabelStatement(loc, ident, s);
			break;
			}
			// fallthrough to TOK.TOKdot
		case TOK.TOKdot:
		case TOK.TOKtypeof:
			if (isDeclaration(&token, 2, TOK.TOKreserved, null))
			goto Ldeclaration;
			else
			goto Lexp;
			break;

		case TOK.TOKassert:
		case TOK.TOKthis:
		case TOK.TOKsuper:
		case TOK.TOKint32v:
		case TOK.TOKuns32v:
		case TOK.TOKint64v:
		case TOK.TOKuns64v:
		case TOK.TOKfloat32v:
		case TOK.TOKfloat64v:
		case TOK.TOKfloat80v:
		case TOK.TOKimaginary32v:
		case TOK.TOKimaginary64v:
		case TOK.TOKimaginary80v:
		case TOK.TOKcharv:
		case TOK.TOKwcharv:
		case TOK.TOKdcharv:
		case TOK.TOKnull:
		case TOK.TOKtrue:
		case TOK.TOKfalse:
		case TOK.TOKstring:
		case TOK.TOKlparen:
		case TOK.TOKcast:
		case TOK.TOKmul:
		case TOK.TOKmin:
		case TOK.TOKadd:
		case TOK.TOKplusplus:
		case TOK.TOKminusminus:
		case TOK.TOKnew:
		case TOK.TOKdelete:
		case TOK.TOKdelegate:
		case TOK.TOKfunction:
		case TOK.TOKtypeid:
		case TOK.TOKis:
		case TOK.TOKlbracket:
version (DMDV2) {
		case TOK.TOKtraits:
		case TOK.TOKfile:
		case TOK.TOKline:
}
		Lexp:
		{
			auto exp = parseExpression();
			check(TOK.TOKsemicolon, "statement");
			s = new ExpStatement(loc, exp);
			break;
		}

		case TOK.TOKstatic:
		{   // Look ahead to see if it's static assert() or static if()
			Token *tt;

			tt = peek(&token);
			if (tt.value == TOK.TOKassert)
			{
				nextToken();
				s = new StaticAssertStatement(parseStaticAssert());
				break;
			}
			if (tt.value == TOK.TOKif)
			{
				nextToken();
				condition = parseStaticIfCondition();
				goto Lcondition;
			}
	        if (tt.value == TOK.TOKstruct || tt.value == TOK.TOKunion || tt.value == TOK.TOKclass)
	        {
		        nextToken();
		        auto a = parseBlock();
		        Dsymbol d = new StorageClassDeclaration(STCstatic, a);
		        s = new DeclarationStatement(loc, d);
		        if (flags & ParseStatementFlags.PSscope)
		            s = new ScopeStatement(loc, s);
		        break;
	        }
			goto Ldeclaration;
		}

		case TOK.TOKfinal:
			if (peekNext() == TOK.TOKswitch)
			{
			nextToken();
			isfinal = true;
			goto Lswitch;
			}
			goto Ldeclaration;

		case TOK.TOKwchar: case TOK.TOKdchar:
		case TOK.TOKbit: case TOK.TOKbool: case TOK.TOKchar:
		case TOK.TOKint8: case TOK.TOKuns8:
		case TOK.TOKint16: case TOK.TOKuns16:
		case TOK.TOKint32: case TOK.TOKuns32:
		case TOK.TOKint64: case TOK.TOKuns64:
		case TOK.TOKfloat32: case TOK.TOKfloat64: case TOK.TOKfloat80:
		case TOK.TOKimaginary32: case TOK.TOKimaginary64: case TOK.TOKimaginary80:
		case TOK.TOKcomplex32: case TOK.TOKcomplex64: case TOK.TOKcomplex80:
		case TOK.TOKvoid:
		case TOK.TOKtypedef:
		case TOK.TOKalias:
		case TOK.TOKconst:
		case TOK.TOKauto:
		case TOK.TOKextern:
		case TOK.TOKinvariant:
version (DMDV2) {
		case TOK.TOKimmutable:
		case TOK.TOKshared:
        case TOKwild:
		case TOK.TOKnothrow:
		case TOK.TOKpure:
		case TOK.TOKtls:
		case TOK.TOKgshared:
	    case TOK.TOKat:
}
	//	case TOK.TOKtypeof:
		Ldeclaration:
		{   Dsymbols a;

			a = parseDeclarations(STC.STCundefined);
			if (a.dim > 1)
			{
			Statements as = new Statements();
			as.reserve(a.dim);
			foreach(Dsymbol d; a)
			{
				s = new DeclarationStatement(loc, d);
				as.push(s);
			}
			s = new CompoundDeclarationStatement(loc, as);
			}
			else if (a.dim == 1)
			{
				auto d = a[0];
			s = new DeclarationStatement(loc, d);
			}
			else
			assert(0);
			if (flags & ParseStatementFlags.PSscope)
			s = new ScopeStatement(loc, s);
			break;
		}

		case TOK.TOKstruct:
		case TOK.TOKunion:
		case TOK.TOKclass:
		case TOK.TOKinterface:
		{   Dsymbol d;

			d = parseAggregate();
			s = new DeclarationStatement(loc, d);
			break;
		}

		case TOK.TOKenum:
		{   /* Determine if this is a manifest constant declaration,
			 * or a conventional enum.
			 */
			Dsymbol d;
			Token* tt = peek(&token);
			if (tt.value == TOK.TOKlcurly || tt.value == TOK.TOKcolon)
			d = parseEnum();
			else if (tt.value != TOK.TOKidentifier)
			goto Ldeclaration;
			else
			{
			tt = peek(tt);
			if (tt.value == TOK.TOKlcurly || tt.value == TOK.TOKcolon ||
				tt.value == TOK.TOKsemicolon)
				d = parseEnum();
			else
				goto Ldeclaration;
			}
			s = new DeclarationStatement(loc, d);
			break;
		}

		case TOK.TOKmixin:
		{   t = peek(&token);
			if (t.value == TOK.TOKlparen)
			{	// mixin(string)
			nextToken();
			check(TOK.TOKlparen, "mixin");
			Expression e = parseAssignExp();
			check(TOK.TOKrparen);
			check(TOK.TOKsemicolon);
			s = new CompileStatement(loc, e);
			break;
			}
			Dsymbol d = parseMixin();
			s = new DeclarationStatement(loc, d);
			break;
		}

		case TOK.TOKlcurly:
		{
			nextToken();
			Statements statements = new Statements();
			while (token.value != TOK.TOKrcurly && token.value != TOKeof)
			{
			statements.push(parseStatement(ParseStatementFlags.PSsemi | ParseStatementFlags.PScurlyscope));
			}
			endloc = this.loc;
			s = new CompoundStatement(loc, statements);
			if (flags & (ParseStatementFlags.PSscope | ParseStatementFlags.PScurlyscope))
			s = new ScopeStatement(loc, s);
			nextToken();
			break;
		}

		case TOK.TOKwhile:
		{   Expression condition2;
			Statement body_;

			nextToken();
			check(TOK.TOKlparen);
			condition2 = parseExpression();
			check(TOK.TOKrparen);
			body_ = parseStatement(ParseStatementFlags.PSscope);
			s = new WhileStatement(loc, condition2, body_);
			break;
		}

		case TOK.TOKsemicolon:
			if (!(flags & ParseStatementFlags.PSsemi))
			error("use '{ }' for an empty statement, not a ';'");
			nextToken();
			s = new ExpStatement(loc, null);
			break;

		case TOK.TOKdo:
		{   Statement body_;
			Expression condition2;

			nextToken();
			body_ = parseStatement(ParseStatementFlags.PSscope);
			check(TOK.TOKwhile);
			check(TOK.TOKlparen);
			condition2 = parseExpression();
			check(TOK.TOKrparen);
			s = new DoStatement(loc, body_, condition2);
			break;
		}

		case TOK.TOKfor:
		{
			Statement init;
			Expression condition2;
			Expression increment;
			Statement body_;

			nextToken();
			check(TOK.TOKlparen);
			if (token.value == TOK.TOKsemicolon)
			{	init = null;
			nextToken();
			}
			else
			{	init = parseStatement(cast(ParseStatementFlags)0);
			}
			if (token.value == TOK.TOKsemicolon)
			{
			condition2 = null;
			nextToken();
			}
			else
			{
			condition2 = parseExpression();
			check(TOK.TOKsemicolon, "for condition");
			}
			if (token.value == TOK.TOKrparen)
			{	increment = null;
			nextToken();
			}
			else
			{	increment = parseExpression();
			check(TOK.TOKrparen);
			}
			body_ = parseStatement(ParseStatementFlags.PSscope);
			s = new ForStatement(loc, init, condition2, increment, body_);
			if (init)
			s = new ScopeStatement(loc, s);
			break;
		}

		case TOK.TOKforeach:
		case TOK.TOKforeach_reverse:
		{
			TOK op = token.value;

			nextToken();
			check(TOK.TOKlparen);

			auto arguments = new Parameters();

			while (1)
			{
			Identifier ai = null;
			Type at;
			StorageClass storageClass = STC.STCundefined;

		if (token.value == TOKref
//#if D1INOUT
//			|| token.value == TOKinout
//#endif
		   )
			{   storageClass = STC.STCref;
				nextToken();
			}
			if (token.value == TOK.TOKidentifier)
			{
				Token *tt = peek(&token);
				if (tt.value == TOK.TOKcomma || tt.value == TOK.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:
			auto a = new Parameter(storageClass, at, ai, null);
			arguments.push(a);
			if (token.value == TOK.TOKcomma)
			{   nextToken();
				continue;
			}
			break;
			}
			check(TOK.TOKsemicolon);

			Expression aggr = parseExpression();
			if (token.value == TOK.TOKslice && arguments.dim == 1)
			{
			auto a = arguments[0];
			delete arguments;
			nextToken();
			Expression upr = parseExpression();
			check(TOK.TOKrparen);
			auto body_ = parseStatement(cast(ParseStatementFlags)0);
			s = new ForeachRangeStatement(loc, op, a, aggr, upr, body_);
			}
			else
			{
			check(TOK.TOKrparen);
			auto body_ = parseStatement(cast(ParseStatementFlags)0);
			s = new ForeachStatement(loc, op, arguments, aggr, body_);
			}
			break;
		}

		case TOK.TOKif:
		{   Parameter arg = null;
			Expression condition2;
			Statement ifbody2;
			Statement elsebody2;

			nextToken();
			check(TOK.TOKlparen);

			if (token.value == TOK.TOKauto)
			{
			nextToken();
			if (token.value == TOK.TOKidentifier)
			{
				Token *tt = peek(&token);
				if (tt.value == TOK.TOKassign)
				{
					arg = new Parameter(STC.STCundefined, 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, TOK.TOKassign, null))
			{
			Type at;
			Identifier ai;

			at = parseType(&ai);
			check(TOK.TOKassign);
			arg = new Parameter(STC.STCundefined, at, ai, null);
			}

			// Check for " ident;"
			else if (token.value == TOK.TOKidentifier)
			{
			Token *tt = peek(&token);
			if (tt.value == TOK.TOKcomma || tt.value == TOK.TOKsemicolon)
			{
				arg = new Parameter(STC.STCundefined, null, token.ident, null);
				nextToken();
				nextToken();
				if (1 || !global.params.useDeprecated)
				error("if (v; e) is deprecated, use if (auto v = e)");
			}
			}

			condition2 = parseExpression();
			check(TOK.TOKrparen);
			ifbody2 = parseStatement(ParseStatementFlags.PSscope);
			if (token.value == TOK.TOKelse)
			{
			nextToken();
			elsebody2 = parseStatement(ParseStatementFlags.PSscope);
			}
			else
			elsebody2 = null;
			s = new IfStatement(loc, arg, condition2, ifbody2, elsebody2);
			break;
		}

		case TOK.TOKscope:
			if (peek(&token).value != TOK.TOKlparen)
			goto Ldeclaration;		// scope used as storage class
			nextToken();
			check(TOK.TOKlparen);
			if (token.value != TOK.TOKidentifier)
			{	error("scope identifier expected");
			goto Lerror;
			}
			else
			{	TOK tt = TOK.TOKon_scope_exit;
			Identifier id = token.ident;

			if (id == Id.exit)
				tt = TOK.TOKon_scope_exit;
			else if (id == Id.failure)
				tt = TOK.TOKon_scope_failure;
			else if (id == Id.success)
				tt = TOK.TOKon_scope_success;
			else
				error("valid scope identifiers are exit, failure, or success, not %s", id.toChars());
			nextToken();
			check(TOK.TOKrparen);
			Statement st = parseStatement(ParseStatementFlags.PScurlyscope);
			s = new OnScopeStatement(loc, tt, st);
			break;
			}

		case TOK.TOKdebug:
			nextToken();
			condition = parseDebugCondition();
			goto Lcondition;

		case TOK.TOKversion:
			nextToken();
			condition = parseVersionCondition();
			goto Lcondition;

		Lcondition:
			ifbody = parseStatement(cast(ParseStatementFlags)0 /*ParseStatementFlags.PSsemi*/);
			elsebody = null;
			if (token.value == TOK.TOKelse)
			{
			nextToken();
			elsebody = parseStatement(cast(ParseStatementFlags)0 /*ParseStatementFlags.PSsemi*/);
			}
			s = new ConditionalStatement(loc, condition, ifbody, elsebody);
			break;

		case TOK.TOKpragma:
		{   Identifier ident;
			Expressions args = null;
			Statement body_;

			nextToken();
			check(TOK.TOKlparen);
			if (token.value != TOK.TOKidentifier)
			{   error("pragma(identifier expected");
			goto Lerror;
			}
			ident = token.ident;
			nextToken();
			if (token.value == TOK.TOKcomma && peekNext() != TOK.TOKrparen)
			args = parseArguments();	// pragma(identifier, args...);
			else
			check(TOK.TOKrparen);		// pragma(identifier);
			if (token.value == TOK.TOKsemicolon)
			{	nextToken();
			body_ = null;
			}
			else
			body_ = parseStatement(ParseStatementFlags.PSsemi);
			s = new PragmaStatement(loc, ident, args, body_);
			break;
		}

		case TOK.TOKswitch:
			isfinal = false;
			goto Lswitch;

		Lswitch:
		{
			nextToken();
			check(TOK.TOKlparen);
			Expression condition2 = parseExpression();
			check(TOK.TOKrparen);
			Statement body_ = parseStatement(ParseStatementFlags.PSscope);
			s = new SwitchStatement(loc, condition2, body_, isfinal);
			break;
		}

		case TOK.TOKcase:
		{   Expression exp;
			Statements statements;
			scope cases = new Expressions();	// array of Expression's
			Expression last = null;

			while (1)
			{
			nextToken();
			exp = parseAssignExp();
			cases.push(exp);
			if (token.value != TOK.TOKcomma)
				break;
			}
			check(TOK.TOKcolon);

version (DMDV2) {
			/* case exp: .. case last:
			 */
			if (token.value == TOK.TOKslice)
			{
			if (cases.dim > 1)
				error("only one case allowed for start of case range");
			nextToken();
			check(TOK.TOKcase);
			last = parseAssignExp();
			check(TOK.TOKcolon);
			}
}

			statements = new Statements();
			while (token.value != TOK.TOKcase &&
			   token.value != TOK.TOKdefault &&
			   token.value != TOKeof &&
			   token.value != TOK.TOKrcurly)
			{
			statements.push(parseStatement(ParseStatementFlags.PSsemi | ParseStatementFlags.PScurlyscope));
			}
			s = new CompoundStatement(loc, statements);
			s = new ScopeStatement(loc, s);

///version (DMDV2) {
			if (last)
			{
				s = new CaseRangeStatement(loc, exp, last, s);
			}
			else
///}
			{
				// Keep cases in order by building the case statements backwards
				for (int i = cases.dim; i; i--)
				{
					exp = cases[i - 1];
					s = new CaseStatement(loc, exp, s);
				}
			}
			break;
		}

		case TOK.TOKdefault:
		{
			Statements statements;

			nextToken();
			check(TOK.TOKcolon);

			statements = new Statements();
			while (token.value != TOK.TOKcase &&
			   token.value != TOK.TOKdefault &&
			   token.value != TOKeof &&
			   token.value != TOK.TOKrcurly)
			{
			statements.push(parseStatement(ParseStatementFlags.PSsemi | ParseStatementFlags.PScurlyscope));
			}
			s = new CompoundStatement(loc, statements);
			s = new ScopeStatement(loc, s);
			s = new DefaultStatement(loc, s);
			break;
		}

		case TOK.TOKreturn:
		{   Expression exp;

			nextToken();
			if (token.value == TOK.TOKsemicolon)
			exp = null;
			else
			exp = parseExpression();
			check(TOK.TOKsemicolon, "return statement");
			s = new ReturnStatement(loc, exp);
			break;
		}

		case TOK.TOKbreak:
		{   Identifier ident;

			nextToken();
			if (token.value == TOK.TOKidentifier)
			{	ident = token.ident;
			nextToken();
			}
			else
			ident = null;
			check(TOK.TOKsemicolon, "break statement");
			s = new BreakStatement(loc, ident);
			break;
		}

		case TOK.TOKcontinue:
		{   Identifier ident;

			nextToken();
			if (token.value == TOK.TOKidentifier)
			{	ident = token.ident;
			nextToken();
			}
			else
			ident = null;
			check(TOK.TOKsemicolon, "continue statement");
			s = new ContinueStatement(loc, ident);
			break;
		}

		case TOK.TOKgoto:
		{   Identifier ident;

			nextToken();
			if (token.value == TOK.TOKdefault)
			{
			nextToken();
			s = new GotoDefaultStatement(loc);
			}
			else if (token.value == TOK.TOKcase)
			{
			Expression exp = null;

			nextToken();
			if (token.value != TOK.TOKsemicolon)
				exp = parseExpression();
			s = new GotoCaseStatement(loc, exp);
			}
			else
			{
			if (token.value != TOK.TOKidentifier)
			{   error("Identifier expected following goto");
				ident = null;
			}
			else
			{   ident = token.ident;
				nextToken();
			}
			s = new GotoStatement(loc, ident);
			}
			check(TOK.TOKsemicolon, "goto statement");
			break;
		}

		case TOK.TOKsynchronized:
		{   Expression exp;
			Statement body_;

			nextToken();
			if (token.value == TOK.TOKlparen)
			{
			nextToken();
			exp = parseExpression();
			check(TOK.TOKrparen);
			}
			else
			exp = null;
			body_ = parseStatement(ParseStatementFlags.PSscope);
			s = new SynchronizedStatement(loc, exp, body_);
			break;
		}

		case TOK.TOKwith:
		{   Expression exp;
			Statement body_;

			nextToken();
			check(TOK.TOKlparen);
			exp = parseExpression();
			check(TOK.TOKrparen);
			body_ = parseStatement(ParseStatementFlags.PSscope);
			s = new WithStatement(loc, exp, body_);
			break;
		}

		case TOK.TOKtry:
		{   Statement body_;
			Array catches = null;
			Statement finalbody = null;

			nextToken();
			body_ = parseStatement(ParseStatementFlags.PSscope);
			while (token.value == TOK.TOKcatch)
			{
				Statement handler;
				Catch c;
				Type tt;
				Identifier id;
				Loc loc2 = this.loc;

				nextToken();
				if (token.value == TOK.TOKlcurly)
				{
					tt = null;
					id = null;
				}
				else
				{
					check(TOK.TOKlparen);
					id = null;
					tt = parseType(&id);
					check(TOK.TOKrparen);
				}
				handler = parseStatement(cast(ParseStatementFlags)0);
				c = new Catch(loc2, tt, id, handler);
				if (!catches)
					catches = new Array();
				catches.push(cast(void*)c);
			}

			if (token.value == TOK.TOKfinally)
			{	nextToken();
			finalbody = parseStatement(cast(ParseStatementFlags)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 TOK.TOKthrow:
		{   Expression exp;

			nextToken();
			exp = parseExpression();
			check(TOK.TOKsemicolon, "throw statement");
			s = new ThrowStatement(loc, exp);
			break;
		}

		case TOK.TOKvolatile:
			nextToken();
			s = parseStatement(ParseStatementFlags.PSsemi | ParseStatementFlags.PScurlyscope);
version (DMDV2) {
			if (!global.params.useDeprecated)
				error("volatile statements deprecated; used synchronized statements instead");
}
			s = new VolatileStatement(loc, s);
			break;

		case TOK.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(TOK.TOKlcurly);
			toklist = null;
			ptoklist = &toklist;
			label = null;
			statements = new Statements();
			while (1)
			{
			switch (token.value)
			{
				case TOK.TOKidentifier:
				if (!toklist)
				{
					// Look ahead to see if it is a label
					t = peek(&token);
					if (t.value == TOK.TOKcolon)
					{   // It's a label
					label = token.ident;
					labelloc = this.loc;
					nextToken();
					nextToken();
					continue;
					}
				}
				goto Ldefault;

				case TOK.TOKrcurly:
				if (toklist || label)
				{
					error("asm statements must end in ';'");
				}
				break;

				case TOK.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 TOK.TOKeof:
				/* { */
				error("matching '}' expected, not end of file");
				break;

				default:
				Ldefault:
				*ptoklist = new Token();
				memcpy(*ptoklist, &token, Token.sizeof);
				ptoklist = &(*ptoklist).next;
				*ptoklist = null;

				nextToken();
				continue;
			}
			break;
			}
			s = new CompoundStatement(loc, statements);
			nextToken();
			break;
		}

		default:
			error("found '%s' instead of statement", token.toChars());
			goto Lerror;

		Lerror:
			while (token.value != TOK.TOKrcurly &&
			   token.value != TOK.TOKsemicolon &&
			   token.value != TOK.TOKeof)
			nextToken();
			if (token.value == TOK.TOKsemicolon)
			nextToken();
			s = null;
			break;
		}

		return s;
	}
	
	/*****************************************
	 * Parse initializer for variable declaration.
	 */
    Initializer 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 TOK.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 TOK.TOKsemicolon:
						case TOK.TOKreturn:
							goto Lexpression;

						case TOK.TOKlcurly:
							braces++;
							continue;

						case TOK.TOKrcurly:
							if (--braces == 0)
								break;
							continue;

						case TOK.TOKeof:
							break;

						default:
							continue;
					}
					break;
				}

				is_ = new StructInitializer(loc);
				nextToken();
				comma = 0;
				while (1)
				{
					switch (token.value)
					{
						case TOK.TOKidentifier:
							if (comma == 1)
								error("comma expected separating field initializers");
							t = peek(&token);
							if (t.value == TOK.TOKcolon)
							{
								id = token.ident;
								nextToken();
								nextToken();	// skip over ':'
							}
							else
							{   
								id = null;
							}
							value = parseInitializer();
							is_.addInit(id, value);
							comma = 1;
							continue;

						case TOK.TOKcomma:
							nextToken();
							comma = 2;
							continue;

						case TOK.TOKrcurly:		// allow trailing comma's
							nextToken();
							break;

						case TOK.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 TOK.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 TOK.TOKlbracket:
							brackets++;
							continue;

						case TOK.TOKrbracket:
							if (--brackets == 0)
							{   
								t = peek(t);
								if (t.value != TOK.TOKsemicolon &&
									t.value != TOK.TOKcomma &&
								t.value != TOK.TOKrcurly)
								goto Lexpression;
								break;
							}
							continue;

						case TOK.TOKeof:
							break;

						default:
							continue;
					}
					break;
				}

				ia = new ArrayInitializer(loc);
				nextToken();
				comma = 0;
				while (true)
				{
					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 == TOK.TOKcolon)
							{
								nextToken();
								value = parseInitializer();
							}
							else
							{   value = new ExpInitializer(e.loc, e);
								e = null;
							}
							ia.addInit(e, value);
							comma = 1;
							continue;

						case TOK.TOKlcurly:
						case TOK.TOKlbracket:
							if (comma == 1)
								error("comma expected separating array initializers, not %s", token.toChars());
							value = parseInitializer();
							ia.addInit(null, value);
							comma = 1;
							continue;

						case TOK.TOKcomma:
							nextToken();
							comma = 2;
							continue;

						case TOK.TOKrbracket:		// allow trailing comma's
							nextToken();
							break;

						case TOK.TOKeof:
							error("found '%s' instead of array initializer", token.toChars());
							break;
					}
					break;
				}
				return ia;

			case TOK.TOKvoid:
				t = peek(&token);
				if (t.value == TOK.TOKsemicolon || t.value == TOK.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__.
	 */
version (DMDV2) {
    Expression parseDefaultInitExp()
	{
		if (token.value == TOK.TOKfile ||
			token.value == TOK.TOKline)
		{
			Token* t = peek(&token);
			if (t.value == TOK.TOKcomma || t.value == TOK.TOKrparen)
			{   
				Expression e;

				if (token.value == TOK.TOKfile)
					e = new FileInitExp(loc);
				else
					e = new LineInitExp(loc);
				nextToken();
				return e;
			}
		}

		Expression e = parseAssignExp();
		return e;
	}
}
    void check(Loc loc, TOK value)
	{
		if (token.value != value)
			error(loc, "found '%s' when expecting '%s'", token.toChars(), Token.toChars(value));
		nextToken();
	}
	
    void check(TOK value)
	{
		check(loc, value);
	}
	
    void check(TOK value, string 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
	 * Output:
	 *	if *pt is not null, it is set to the ending token, which would be endtok
	 */

    bool isDeclaration(Token* t, int needId, TOK endtok, Token** pt)
	{
		//printf("isDeclaration(needId = %d)\n", needId);
		int haveId = 0;

version (DMDV2) {
		if ((t.value == TOK.TOKconst ||
			t.value == TOK.TOKinvariant ||
			t.value == TOK.TOKimmutable ||
	        t.value == TOKwild ||
			t.value == TOK.TOKshared) &&
			peek(t).value != TOK.TOKlparen)
		{
			/* const type
			* immutable type
			* shared type
	        * wild type
			*/
			t = peek(t);
		}
}

		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, t = %s\n", t.toChars());
		return true;

	Lisnot:
		//printf("\tis not declaration\n");
		return false;
	}
	
    bool isBasicType(Token** pt)
	{
		// This code parallels parseBasicType()
		Token* t = *pt;
		Token* t2;
		int parens;
		int haveId = 0;

		switch (t.value)
		{
		case TOKwchar:
		case TOKdchar:
		case TOKbit:
		case TOKbool:
		case TOKchar:
		case TOKint8:
		case TOKuns8:
		case TOKint16:
		case TOKuns16:
		case TOKint32:
		case TOKuns32:
		case TOKint64:
		case TOKuns64:
		case TOKfloat32:
		case TOKfloat64:
		case TOKfloat80:
		case TOKimaginary32:
		case TOKimaginary64:
		case TOKimaginary80:
		case TOKcomplex32:
		case TOKcomplex64:
		case TOKcomplex80:
		case TOKvoid:
			t = peek(t);
			break;

		case TOK.TOKidentifier:
		L5:
			t = peek(t);
			if (t.value == TOK.TOKnot)
			{
			goto L4;
			}
			goto L3;
			while (1)
			{
		L2:
			t = peek(t);
		L3:
			if (t.value == TOK.TOKdot)
			{
		Ldot:
				t = peek(t);
				if (t.value != TOK.TOKidentifier)
				goto Lfalse;
				t = peek(t);
				if (t.value != TOK.TOKnot)
				goto L3;
		L4:
				/* Seen a !
				 * Look for:
				 * !( args ), !identifier, etc.
				 */
				t = peek(t);
				switch (t.value)
				{	case TOK.TOKidentifier:
					goto L5;
				case TOK.TOKlparen:
					if (!skipParens(t, &t))
					goto Lfalse;
					break;
				case TOK.TOKwchar: case TOK.TOKdchar:
				case TOK.TOKbit: case TOK.TOKbool: case TOK.TOKchar:
				case TOK.TOKint8: case TOK.TOKuns8:
				case TOK.TOKint16: case TOK.TOKuns16:
				case TOK.TOKint32: case TOK.TOKuns32:
				case TOK.TOKint64: case TOK.TOKuns64:
				case TOK.TOKfloat32: case TOK.TOKfloat64: case TOK.TOKfloat80:
				case TOK.TOKimaginary32: case TOK.TOKimaginary64: case TOK.TOKimaginary80:
				case TOK.TOKcomplex32: case TOK.TOKcomplex64: case TOK.TOKcomplex80:
				case TOK.TOKvoid:
				case TOK.TOKint32v:
				case TOK.TOKuns32v:
				case TOK.TOKint64v:
				case TOK.TOKuns64v:
				case TOK.TOKfloat32v:
				case TOK.TOKfloat64v:
				case TOK.TOKfloat80v:
				case TOK.TOKimaginary32v:
				case TOK.TOKimaginary64v:
				case TOK.TOKimaginary80v:
				case TOK.TOKnull:
				case TOK.TOKtrue:
				case TOK.TOKfalse:
				case TOK.TOKcharv:
				case TOK.TOKwcharv:
				case TOK.TOKdcharv:
				case TOK.TOKstring:
				case TOK.TOKfile:
				case TOK.TOKline:
					goto L2;
				default:
					goto Lfalse;
				}
			}
			else
				break;
			}
			break;

		case TOK.TOKdot:
			goto Ldot;

		case TOK.TOKtypeof:
			/* typeof(exp).identifier...
			 */
			t = peek(t);
			if (t.value != TOK.TOKlparen)
			goto Lfalse;
			if (!skipParens(t, &t))
			goto Lfalse;
			goto L2;

		case TOK.TOKconst:
		case TOK.TOKinvariant:
		case TOK.TOKimmutable:
		case TOK.TOKshared:
    	case TOKwild:
			// const(type)  or  immutable(type)  or  shared(type)  or  wild(type)
			t = peek(t);
			if (t.value != TOK.TOKlparen)
			goto Lfalse;
			t = peek(t);
			if (!isDeclaration(t, 0, TOK.TOKrparen, &t))
			{
			goto Lfalse;
			}
			t = peek(t);
			break;

		default:
			goto Lfalse;
		}
		*pt = t;
		//printf("is\n");
		return true;

	Lfalse:
		//printf("is not\n");
		return false;
	}
	
    bool isDeclarator(Token** pt, int* haveId, TOK endtok)
	{
		// This code parallels parseDeclarator()
		Token* t = *pt;
		bool parens;

		//printf("Parser.isDeclarator()\n");
		//t.print();
		if (t.value == TOK.TOKassign)
			return false;

		while (true)
		{
			parens = false;
			switch (t.value)
			{
				case TOK.TOKmul:
				//case TOKand:
					t = peek(t);
					continue;

				case TOK.TOKlbracket:
					t = peek(t);
					if (t.value == TOK.TOKrbracket)
					{
						t = peek(t);
					}
					else if (t.value == TOKnew && peek(t).value == TOKrbracket)
					{
						t = peek(t);
						t = peek(t);
					}
					else if (isDeclaration(t, 0, TOK.TOKrbracket, &t))
					{   
						// It's an associative array declaration
						t = peek(t);
					}
					else
					{
						// [ expression ]
						// [ expression .. expression ]
						if (!isExpression(&t))
							return false;
						if (t.value == TOK.TOKslice)
						{	
							t = peek(t);
							if (!isExpression(&t))
								return false;
						}
						if (t.value != TOK.TOKrbracket)
							return false;
						t = peek(t);
					}
					continue;

				case TOK.TOKidentifier:
					if (*haveId)
						return false;
					*haveId = true;
					t = peek(t);
					break;

				case TOK.TOKlparen:
					t = peek(t);

					if (t.value == TOK.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 == TOK.TOKidentifier)
					{   
						Token *t2 = peek(t);
						if (t2.value == TOK.TOKrparen)
							return false;
					}

					if (!isDeclarator(&t, haveId, TOK.TOKrparen))
						return false;
					t = peek(t);
					parens = true;
					break;

				case TOK.TOKdelegate:
				case TOK.TOKfunction:
					t = peek(t);
					if (!isParameters(&t))
						return false;
					continue;
				default:
					break;	///
			}
			break;
		}

		while (1)
		{
			switch (t.value)
			{
version (CARRAYDECL) {
				case TOK.TOKlbracket:
					parens = false;
					t = peek(t);
					if (t.value == TOK.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 != TOK.TOKrbracket)
							return false;
						t = peek(t);
					}
					continue;
}

				case TOK.TOKlparen:
					parens = false;
					if (!isParameters(&t))
						return false;
version (DMDV2) {
					while (true)
					{
						switch (t.value)
						{
							case TOK.TOKconst:
							case TOK.TOKinvariant:
							case TOK.TOKimmutable:
							case TOK.TOKshared:
                            case TOKwild:
							case TOK.TOKpure:
							case TOK.TOKnothrow:
								t = peek(t);
								continue;
							case TOK.TOKat:
								t = peek(t);	// skip '@'
								t = peek(t);	// skip identifier
								continue;
							default:
								break;
						}
						break;
					}
}
					continue;

				// Valid tokens that follow a declaration
				case TOK.TOKrparen:
				case TOK.TOKrbracket:
				case TOK.TOKassign:
				case TOK.TOKcomma:
				case TOK.TOKsemicolon:
				case TOK.TOKlcurly:
				case TOK.TOKin:
					// The !parens is to disallow unnecessary parentheses
					if (!parens && (endtok == TOK.TOKreserved || endtok == t.value))
					{   
						*pt = t;
						return true;
					}
					return false;

				default:
					return false;
			}
		}
	}
	
    bool isParameters(Token** pt)
	{
		// This code parallels parseParameters()
		Token* t = *pt;

		//printf("isParameters()\n");
		if (t.value != TOKlparen)
			return false;

		t = peek(t);
		for (;1; t = peek(t))
		{
			 L1:
			switch (t.value)
			{
				case TOKrparen:
					break;

				case TOKdotdotdot:
					t = peek(t);
					break;

version(D1INOUT) {
	            case TOKinout:
}
				case TOKin:
				case TOKout:
				case TOKref:
				case TOKlazy:
				case TOKfinal:
	            case TOKauto:
					continue;

				case TOKconst:
				case TOKinvariant:
				case TOKimmutable:
				case TOKshared:
	            case TOKwild:
					t = peek(t);
					if (t.value == TOKlparen)
					{
						t = peek(t);
						if (!isDeclaration(t, 0, TOKrparen, &t))
						return false;
						t = peek(t);	// skip past closing ')'
						goto L2;
					}
					goto L1;

		static if (false) {
				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;
		}

				default:
				{	
					if (!isBasicType(&t))
						return false;
					L2:
					int 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;
	}
	
    bool 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;
	}
	
    int isTemplateInstance(Token t, Token* pt)
	{
		assert(false);
	}
	
	/*******************************************
	 * 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
	 */
    bool 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 true;

	  Lfalse:
		return false;
	}

    Expression parseExpression()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		//printf("Parser.parseExpression() loc = %d\n", loc.linnum);
		e = parseAssignExp();
		while (token.value == TOK.TOKcomma)
		{
			nextToken();
			e2 = parseAssignExp();
			e = new CommaExp(loc, e, e2);
			loc = this.loc;
		}
		return e;
	}
	
    Expression parsePrimaryExp()
	{
		Expression e;
		Type t;
		Identifier id;
		TOK save;
		Loc loc = this.loc;

		//printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
		switch (token.value)
		{
		case TOK.TOKidentifier:
			id = token.ident;
			nextToken();
			if (token.value == TOK.TOKnot && peekNext() != TOK.TOKis)
			{	// identifier!(template-argument-list)
			TemplateInstance tempinst;

			tempinst = new TemplateInstance(loc, id);
			nextToken();
			if (token.value == TOK.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 TOK.TOKdollar:
			if (!inBrackets)
			error("'$' is valid only inside [] of index or slice");
			e = new DollarExp(loc);
			nextToken();
			break;

		case TOK.TOKdot:
			// Signal global scope '.' operator with "" identifier
			e = new IdentifierExp(loc, Id.empty);
			break;

		case TOK.TOKthis:
			e = new ThisExp(loc);
			nextToken();
			break;

		case TOK.TOKsuper:
			e = new SuperExp(loc);
			nextToken();
			break;

		case TOK.TOKint32v:
			e = new IntegerExp(loc, token.int32value, Type.tint32);
			nextToken();
			break;

		case TOK.TOKuns32v:
			e = new IntegerExp(loc, token.uns32value, Type.tuns32);
			nextToken();
			break;

		case TOK.TOKint64v:
			e = new IntegerExp(loc, token.int64value, Type.tint64);
			nextToken();
			break;

		case TOK.TOKuns64v:
			e = new IntegerExp(loc, token.uns64value, Type.tuns64);
			nextToken();
			break;

		case TOK.TOKfloat32v:
			e = new RealExp(loc, token.float80value, Type.tfloat32);
			nextToken();
			break;

		case TOK.TOKfloat64v:
			e = new RealExp(loc, token.float80value, Type.tfloat64);
			nextToken();
			break;

		case TOK.TOKfloat80v:
			e = new RealExp(loc, token.float80value, Type.tfloat80);
			nextToken();
			break;

		case TOK.TOKimaginary32v:
			e = new RealExp(loc, token.float80value, Type.timaginary32);
			nextToken();
			break;

		case TOK.TOKimaginary64v:
			e = new RealExp(loc, token.float80value, Type.timaginary64);
			nextToken();
			break;

		case TOK.TOKimaginary80v:
			e = new RealExp(loc, token.float80value, Type.timaginary80);
			nextToken();
			break;

		case TOK.TOKnull:
			e = new NullExp(loc);
			nextToken();
			break;

version (DMDV2) {
		case TOK.TOKfile:
		{
			string s = loc.filename ? loc.filename : mod.ident.toChars();
			e = new StringExp(loc, s, 0);
			nextToken();
			break;
		}

		case TOK.TOKline:
			e = new IntegerExp(loc, loc.linnum, Type.tint32);
			nextToken();
			break;
}

		case TOK.TOKtrue:
			e = new IntegerExp(loc, 1, Type.tbool);
			nextToken();
			break;

		case TOK.TOKfalse:
			e = new IntegerExp(loc, 0, Type.tbool);
			nextToken();
			break;

		case TOK.TOKcharv:
			e = new IntegerExp(loc, token.uns32value, Type.tchar);
			nextToken();
			break;

		case TOK.TOKwcharv:
			e = new IntegerExp(loc, token.uns32value, Type.twchar);
			nextToken();
			break;

		case TOK.TOKdcharv:
			e = new IntegerExp(loc, token.uns32value, Type.tdchar);
			nextToken();
			break;

		case TOK.TOKstring:
		{  
			const(char)* s;
			uint len;
			ubyte postfix;

			// cat adjacent strings
			s = token.ustring;
			len = token.len;
			postfix = token.postfix;
			while (1)
			{
				nextToken();
				if (token.value == TOK.TOKstring)
				{   uint len1;
					uint len2;
					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 = cast(char*)GC.malloc((len + 1) * ubyte.sizeof);
					memcpy(s2, s, len1 * ubyte.sizeof);
					memcpy(s2 + len1, token.ustring, (len2 + 1) * ubyte.sizeof);
					s = s2;
				}
				else
					break;
			}
			e = new StringExp(loc, assumeUnique(s[0..len]), postfix);
			break;
		}

		case TOK.TOKvoid:	 t = Type.tvoid;  goto LabelX;
		case TOK.TOKint8:	 t = Type.tint8;  goto LabelX;
		case TOK.TOKuns8:	 t = Type.tuns8;  goto LabelX;
		case TOK.TOKint16:	 t = Type.tint16; goto LabelX;
		case TOK.TOKuns16:	 t = Type.tuns16; goto LabelX;
		case TOK.TOKint32:	 t = Type.tint32; goto LabelX;
		case TOK.TOKuns32:	 t = Type.tuns32; goto LabelX;
		case TOK.TOKint64:	 t = Type.tint64; goto LabelX;
		case TOK.TOKuns64:	 t = Type.tuns64; goto LabelX;
		case TOK.TOKfloat32: t = Type.tfloat32; goto LabelX;
		case TOK.TOKfloat64: t = Type.tfloat64; goto LabelX;
		case TOK.TOKfloat80: t = Type.tfloat80; goto LabelX;
		case TOK.TOKimaginary32: t = Type.timaginary32; goto LabelX;
		case TOK.TOKimaginary64: t = Type.timaginary64; goto LabelX;
		case TOK.TOKimaginary80: t = Type.timaginary80; goto LabelX;
		case TOK.TOKcomplex32: t = Type.tcomplex32; goto LabelX;
		case TOK.TOKcomplex64: t = Type.tcomplex64; goto LabelX;
		case TOK.TOKcomplex80: t = Type.tcomplex80; goto LabelX;
		case TOK.TOKbit:	 t = Type.tbit;     goto LabelX;
		case TOK.TOKbool:	 t = Type.tbool;    goto LabelX;
		case TOK.TOKchar:	 t = Type.tchar;    goto LabelX;
		case TOK.TOKwchar:	 t = Type.twchar; goto LabelX;
		case TOK.TOKdchar:	 t = Type.tdchar; goto LabelX;
		LabelX:
			nextToken();
		L1:
			check(TOK.TOKdot, t.toChars());
			if (token.value != TOK.TOKidentifier)
			{   error("found '%s' when expecting identifier following '%s.'", token.toChars(), t.toChars());
			goto Lerr;
			}
			e = typeDotIdExp(loc, t, token.ident);
			nextToken();
			break;

		case TOK.TOKtypeof:
		{
			t = parseTypeof();
			e = new TypeExp(loc, t);
			break;
		}

		case TOK.TOKtypeid:
		{
			nextToken();
			check(TOK.TOKlparen, "typeid");
	        Object o;
	        if (isDeclaration(&token, 0, TOKreserved, null))
	        {	// argument is a type
		        o = parseType();
	        }
	        else
	        {	// argument is an expression
		        o = parseAssignExp();
	        }
			check(TOK.TOKrparen);
		    e = new TypeidExp(loc, o);
			break;
		}

version (DMDV2) {
		case TOK.TOKtraits:
		{   /* __traits(identifier, args...)
			 */
			Identifier ident;
			Objects args = null;

			nextToken();
			check(TOK.TOKlparen);
			if (token.value != TOK.TOKidentifier)
			{   error("__traits(identifier, args...) expected");
			goto Lerr;
			}
			ident = token.ident;
			nextToken();
			if (token.value == TOK.TOKcomma)
			args = parseTemplateArgumentList2();	// __traits(identifier, args...)
			else
			check(TOK.TOKrparen);		// __traits(identifier)

			e = new TraitsExp(loc, ident, args);
			break;
		}
}

		case TOK.TOKis:
		{   Type targ;
			Identifier ident = null;
			Type tspec = null;
			TOK tok = TOK.TOKreserved;
			TOK tok2 = TOK.TOKreserved;
			TemplateParameters tpl = null;
			Loc loc2 = this.loc;

			nextToken();
			if (token.value == TOK.TOKlparen)
			{
				nextToken();
				targ = parseType(&ident);
				if (token.value == TOK.TOKcolon || token.value == TOK.TOKequal)
				{
					tok = token.value;
					nextToken();
					if (tok == TOK.TOKequal &&
					(token.value == TOK.TOKtypedef ||
					 token.value == TOK.TOKstruct ||
					 token.value == TOK.TOKunion ||
					 token.value == TOK.TOKclass ||
					 token.value == TOK.TOKsuper ||
					 token.value == TOK.TOKenum ||
					 token.value == TOK.TOKinterface ||
	///version (DMDV2) {
					 token.value == TOK.TOKconst && peek(&token).value == TOK.TOKrparen ||
					 token.value == TOK.TOKinvariant && peek(&token).value == TOK.TOKrparen ||
					 token.value == TOK.TOKimmutable && peek(&token).value == TOK.TOKrparen ||
					 token.value == TOK.TOKshared && peek(&token).value == TOK.TOKrparen ||
                     token.value == TOKwild && peek(&token).value == TOKrparen ||
	///}
					 token.value == TOK.TOKfunction ||
					 token.value == TOK.TOKdelegate ||
					 token.value == TOK.TOKreturn))
					{
					tok2 = token.value;
					nextToken();
					}
					else
					{
					tspec = parseType();
					}
				}
				if (ident && tspec)
				{
					if (token.value == TOK.TOKcomma)
					tpl = parseTemplateParameterList(1);
					else
					{	tpl = new TemplateParameters();
					check(TOK.TOKrparen);
					}
					TemplateParameter tp = new TemplateTypeParameter(loc2, ident, null, null);
					tpl.insert(0, tp);
				}
				else
					check(TOK.TOKrparen);
			}
			else
			{   error("(type identifier : specialization) expected following is");
			goto Lerr;
			}
			e = new IsExp(loc2, targ, ident, tok, tspec, tok2, tpl);
			break;
		}

		case TOK.TOKassert:
		{   
			Expression msg = null;

			nextToken();
			check(TOK.TOKlparen, "assert");
			e = parseAssignExp();
			if (token.value == TOK.TOKcomma)
			{	nextToken();
			msg = parseAssignExp();
			}
			check(TOK.TOKrparen);
			e = new AssertExp(loc, e, msg);
			break;
		}

		case TOK.TOKmixin:
		{
			nextToken();
			check(TOK.TOKlparen, "mixin");
			e = parseAssignExp();
			check(TOK.TOKrparen);
			e = new CompileExp(loc, e);
			break;
		}

		case TOK.TOKimport:
		{
			nextToken();
			check(TOK.TOKlparen, "import");
			e = parseAssignExp();
			check(TOK.TOKrparen);
			e = new FileExp(loc, e);
			break;
		}

		case TOK.TOKlparen:
			if (peekPastParen(&token).value == TOK.TOKlcurly)
			{	// (arguments) { statements... }
			save = TOK.TOKdelegate;
			goto case_delegate;
			}
			// ( expression )
			nextToken();
			e = parseExpression();
			check(loc, TOK.TOKrparen);
			break;

		case TOK.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 != TOK.TOKrbracket)
			{
			while (token.value != TOK.TOKeof)
			{
				Expression e2 = parseAssignExp();
				if (token.value == TOK.TOKcolon && (keys || values.dim == 0))
				{	
					nextToken();
					if (!keys)
						keys = new Expressions();
					keys.push(e2);
					e2 = parseAssignExp();
				}
				else if (keys)
				{	
					error("'key:value' expected for associative array literal");
					delete keys;
					keys = null;
				}
				values.push(e2);
				if (token.value == TOK.TOKrbracket)
					break;
				check(TOK.TOKcomma);
			}
			}
			check(TOK.TOKrbracket);

			if (keys)
				e = new AssocArrayLiteralExp(loc, keys, values);
			else
				e = new ArrayLiteralExp(loc, values);
			break;
		}

		case TOK.TOKlcurly:
			// { statements... }
			save = TOK.TOKdelegate;
			goto case_delegate;

		case TOK.TOKfunction:
		case TOK.TOKdelegate:
			save = token.value;
			nextToken();
		case_delegate:
		{
			/* function type(parameters) { body } pure nothrow
			 * delegate type(parameters) { body } pure nothrow
			 * (parameters) { body }
			 * { body }
			 */
			Parameters arguments;
			int varargs;
			FuncLiteralDeclaration fd;
			Type tt;
			bool isnothrow = false;
			bool ispure = false;
	        bool isproperty = false;
	        TRUST trust = TRUSTdefault;
            
			if (token.value == TOK.TOKlcurly)
			{
			tt = null;
			varargs = 0;
			arguments = new Parameters();
			}
			else
			{
			if (token.value == TOK.TOKlparen)
				tt = null;
			else
			{
				tt = parseBasicType();
				tt = parseBasicType2(tt);	// function return type
			}
			arguments = parseParameters(&varargs);
			while (1)
			{
				if (token.value == TOK.TOKpure)
				ispure = true;
				else if (token.value == TOK.TOKnothrow)
				isnothrow = true;
		        else if (token.value == TOKat)
		        {
                    StorageClass stc = parseAttribute();
			        switch (cast(uint)(stc >> 32))
			        {
                        case STCproperty >> 32:
				            isproperty = true;
				            break;
			            case STCsafe >> 32:
				            trust = TRUSTsafe;
				            break;
			            case STCsystem >> 32:
				            trust = TRUSTsystem;
				            break;
			            case STCtrusted >> 32:
				            trust = TRUSTtrusted;
				            break;
			            case 0:
				            break;
			            default:
    				        assert(0);
			        }
	            }
				else
	    			break;
				nextToken();
			}
			}

			TypeFunction tf = new TypeFunction(arguments, tt, varargs, linkage);
			tf.ispure = ispure;
			tf.isnothrow = isnothrow;
	        tf.isproperty = isproperty;
	        tf.trust = trust;
			fd = new FuncLiteralDeclaration(loc, 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 parseUnaryExp()
	{
		Expression e;
		Loc loc = this.loc;

		switch (token.value)
		{
		case TOK.TOKand:
			nextToken();
			e = parseUnaryExp();
			e = new AddrExp(loc, e);
			break;

		case TOK.TOKplusplus:
			nextToken();
			e = parseUnaryExp();
			e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type.tint32));
			break;

		case TOK.TOKminusminus:
			nextToken();
			e = parseUnaryExp();
			e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type.tint32));
			break;

		case TOK.TOKmul:
			nextToken();
			e = parseUnaryExp();
			e = new PtrExp(loc, e);
			break;

		case TOK.TOKmin:
			nextToken();
			e = parseUnaryExp();
			e = new NegExp(loc, e);
			break;

		case TOK.TOKadd:
			nextToken();
			e = parseUnaryExp();
			e = new UAddExp(loc, e);
			break;

		case TOK.TOKnot:
			nextToken();
			e = parseUnaryExp();
			e = new NotExp(loc, e);
			break;

		case TOK.TOKtilde:
			nextToken();
			e = parseUnaryExp();
			e = new ComExp(loc, e);
			break;

		case TOK.TOKdelete:
			nextToken();
			e = parseUnaryExp();
			e = new DeleteExp(loc, e);
			break;

		case TOK.TOKnew:
			e = parseNewExp(null);
			break;

		case TOK.TOKcast:				// cast(type) expression
		{
			nextToken();
			check(TOK.TOKlparen);
			/* Look for cast(), cast(const), cast(immutable),
			 * cast(shared), cast(shared const), cast(wild), cast(shared wild)
			 */
			MOD m;
			if (token.value == TOK.TOKrparen)
			{
			m = MOD.MODundefined;
			goto Lmod1;
			}
			else if (token.value == TOK.TOKconst && peekNext() == TOK.TOKrparen)
			{
			m = MOD.MODconst;
			goto Lmod2;
			}
			else if ((token.value == TOK.TOKimmutable || token.value == TOK.TOKinvariant) && peekNext() == TOK.TOKrparen)
			{
			m = MOD.MODimmutable;
			goto Lmod2;
			}
			else if (token.value == TOK.TOKshared && peekNext() == TOK.TOKrparen)
			{
			m = MOD.MODshared;
			goto Lmod2;
			}
	        else if (token.value == TOKwild && peekNext() == TOK.TOKrparen)
	        {
		    m = MODwild;
		    goto Lmod2;
	        }
	        else if (token.value == TOKwild && peekNext() == TOK.TOKshared && peekNext2() == TOK.TOKrparen ||
		         token.value == TOK.TOKshared && peekNext() == TOKwild && peekNext2() == TOK.TOKrparen)
	        {
		    m = MOD.MODshared | MOD.MODwild;
		    goto Lmod3;
	        }
			else if (token.value == TOK.TOKconst && peekNext() == TOK.TOKshared && peekNext2() == TOK.TOKrparen ||
				 token.value == TOK.TOKshared && peekNext() == TOK.TOKconst && peekNext2() == TOK.TOKrparen)
			{
			m = MOD.MODshared | MOD.MODconst;
	          Lmod3:
			nextToken();
			  Lmod2:
			nextToken();
			  Lmod1:
			nextToken();
			e = parseUnaryExp();
			e = new CastExp(loc, e, m);
			}
			else
			{
			Type t = parseType();		// ( type )
			check(TOK.TOKrparen);
			e = parseUnaryExp();
			e = new CastExp(loc, e, t);
			}
			break;
		}

		case TOK.TOKlparen:
		{   Token *tk;

			tk = peek(&token);
version (CCASTSYNTAX) {
			// If cast
			if (isDeclaration(tk, 0, TOK.TOKrparen, &tk))
			{
				tk = peek(tk);		// skip over right parenthesis
				switch (tk.value)
				{
					case TOK.TOKnot:
						tk = peek(tk);
						if (tk.value == TOK.TOKis)	// !is
							break;
					case TOK.TOKdot:
					case TOK.TOKplusplus:
					case TOK.TOKminusminus:
					case TOK.TOKdelete:
					case TOK.TOKnew:
					case TOK.TOKlparen:
					case TOK.TOKidentifier:
					case TOK.TOKthis:
					case TOK.TOKsuper:
					case TOK.TOKint32v:
					case TOK.TOKuns32v:
					case TOK.TOKint64v:
					case TOK.TOKuns64v:
					case TOK.TOKfloat32v:
					case TOK.TOKfloat64v:
					case TOK.TOKfloat80v:
					case TOK.TOKimaginary32v:
					case TOK.TOKimaginary64v:
					case TOK.TOKimaginary80v:
					case TOK.TOKnull:
					case TOK.TOKtrue:
					case TOK.TOKfalse:
					case TOK.TOKcharv:
					case TOK.TOKwcharv:
					case TOK.TOKdcharv:
					case TOK.TOKstring:
static if (false) {
					case TOK.TOKtilde:
					case TOK.TOKand:
					case TOK.TOKmul:
					case TOK.TOKmin:
					case TOK.TOKadd:
}
					case TOK.TOKfunction:
					case TOK.TOKdelegate:
					case TOK.TOKtypeof:
version (DMDV2) {
					case TOK.TOKfile:
					case TOK.TOKline:
}
					case TOK.TOKwchar: case TOK.TOKdchar:
					case TOK.TOKbit: case TOK.TOKbool: case TOK.TOKchar:
					case TOK.TOKint8: case TOK.TOKuns8:
					case TOK.TOKint16: case TOK.TOKuns16:
					case TOK.TOKint32: case TOK.TOKuns32:
					case TOK.TOKint64: case TOK.TOKuns64:
					case TOK.TOKfloat32: case TOK.TOKfloat64: case TOK.TOKfloat80:
					case TOK.TOKimaginary32: case TOK.TOKimaginary64: case TOK.TOKimaginary80:
					case TOK.TOKcomplex32: case TOK.TOKcomplex64: case TOK.TOKcomplex80:
					case TOK.TOKvoid:		// (type)int.size
					{	
						// (type) una_exp
						nextToken();
						Type t = parseType();
						check(TOK.TOKrparen);

						// if .identifier
						if (token.value == TOK.TOKdot)
						{
							nextToken();
							if (token.value != TOK.TOKidentifier)
							{   
								error("Identifier expected following (type).");
								return null;
							}
							e = 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;
					}
					
					default:
						break;	///
				}
			}
}
			e = parsePrimaryExp();
			e = parsePostExp(e);
			break;
		}
		default:
			e = parsePrimaryExp();
			e = parsePostExp(e);
			break;
		}
		assert(e);

        // ^^ is right associative and has higher precedence than the unary operators
        while (token.value == TOK.TOKpow)
        {
	        nextToken();
	        Expression e2 = parseUnaryExp();
	        e = new PowExp(loc, e, e2);
        }

		return e;
	}
	
    Expression parsePostExp(Expression e)
	{
		Loc loc;

		while (1)
		{
		loc = this.loc;
		switch (token.value)
		{
			case TOK.TOKdot:
			nextToken();
			if (token.value == TOK.TOKidentifier)
			{   Identifier id = token.ident;

				nextToken();
				if (token.value == TOK.TOKnot && peekNext() != TOK.TOKis)
				{   // identifier!(template-argument-list)
				TemplateInstance tempinst = new TemplateInstance(loc, id);
			    Objects tiargs;
				nextToken();
				if (token.value == TOK.TOKlparen)
					// ident!(template_arguments)
					tiargs = parseTemplateArgumentList();
				else
					// ident!template_argument
					tiargs = parseTemplateArgument();
				e = new DotTemplateInstanceExp(loc, e, id, tiargs);
				}
				else
				e = new DotIdExp(loc, e, id);
				continue;
			}
			else if (token.value == TOK.TOKnew)
			{
				e = parseNewExp(e);
				continue;
			}
			else
				error("identifier expected following '.', not '%s'", token.toChars());
			break;

			case TOK.TOKplusplus:
			e = new PostExp(TOK.TOKplusplus, loc, e);
			break;

			case TOK.TOKminusminus:
			e = new PostExp(TOK.TOKminusminus, loc, e);
			break;

			case TOK.TOKlparen:
			e = new CallExp(loc, e, parseArguments());
			continue;

			case TOK.TOKlbracket:
			{	// array dereferences:
			//	array[index]
			//	array[]
			//	array[lwr .. upr]
			Expression index;
			Expression upr;

			inBrackets++;
			nextToken();
			if (token.value == TOK.TOKrbracket)
			{   // array[]
				e = new SliceExp(loc, e, null, null);
				nextToken();
			}
			else
			{
				index = parseAssignExp();
				if (token.value == TOK.TOKslice)
				{	// array[lwr .. upr]
				nextToken();
				upr = parseAssignExp();
				e = new SliceExp(loc, e, index, upr);
				}
				else
				{	// array[index, i2, i3, i4, ...]
				auto arguments = new Expressions();
				arguments.push(index);
				if (token.value == TOK.TOKcomma)
				{
					nextToken();
					while (1)
					{   Expression arg;

					arg = parseAssignExp();
					arguments.push(arg);
					if (token.value == TOK.TOKrbracket)
						break;
					check(TOK.TOKcomma);
					}
				}
				e = new ArrayExp(loc, e, arguments);
				}
				check(TOK.TOKrbracket);
				inBrackets--;
			}
			continue;
			}

			default:
			return e;
		}
		nextToken();
		}
		
		assert(false);
	}
	
    Expression parseMulExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseUnaryExp();
		while (1)
		{
			switch (token.value)
			{
				case TOK.TOKmul: nextToken(); e2 = parseUnaryExp(); e = new MulExp(loc,e,e2); continue;
	            case TOK.TOKdiv: nextToken(); e2 = parseUnaryExp(); e = new DivExp(loc,e,e2); continue;
	            case TOK.TOKmod: nextToken(); e2 = parseUnaryExp(); e = new ModExp(loc,e,e2); continue;

				default:
				break;
			}
			break;
		}
		return e;
	}
	
    Expression parseShiftExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseAddExp();
		while (1)
		{
			switch (token.value)
			{
				case TOK.TOKshl:  nextToken(); e2 = parseAddExp(); e = new ShlExp(loc,e,e2);  continue;
				case TOK.TOKshr:  nextToken(); e2 = parseAddExp(); e = new ShrExp(loc,e,e2);  continue;
				case TOK.TOKushr: nextToken(); e2 = parseAddExp(); e = new UshrExp(loc,e,e2); continue;

				default:
				break;
			}
			break;
		}
		return e;
	}
	
    Expression parseAddExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseMulExp();
		while (1)
		{
			switch (token.value)
			{
				case TOK.TOKadd:    nextToken(); e2 = parseMulExp(); e = new AddExp(loc,e,e2); continue;
				case TOK.TOKmin:    nextToken(); e2 = parseMulExp(); e = new MinExp(loc,e,e2); continue;
				case TOK.TOKtilde:  nextToken(); e2 = parseMulExp(); e = new CatExp(loc,e,e2); continue;

				default:
				break;
			}
			break;
		}
		return e;
	}
	
    Expression parseRelExp()
	{
		assert(false);
	}
	
    Expression parseEqualExp()
	{
		assert(false);
	}
	
    Expression parseCmpExp()
	{
		Expression e;
		Expression e2;
		Token* t;
		Loc loc = this.loc;

		e = parseShiftExp();
		TOK op = token.value;

		switch (op)
		{
		case TOK.TOKequal:
		case TOK.TOKnotequal:
			nextToken();
			e2 = parseShiftExp();
			e = new EqualExp(op, loc, e, e2);
			break;

		case TOK.TOKis:
			op = TOK.TOKidentity;
			goto L1;

		case TOK.TOKnot:
			// Attempt to identify '!is'
			t = peek(&token);
			if (t.value != TOK.TOKis)
			break;
			nextToken();
			op = TOK.TOKnotidentity;
			goto L1;

		L1:
			nextToken();
			e2 = parseShiftExp();
			e = new IdentityExp(op, loc, e, e2);
			break;

		case TOK.TOKlt:
		case TOK.TOKle:
		case TOK.TOKgt:
		case TOK.TOKge:
		case TOK.TOKunord:
		case TOK.TOKlg:
		case TOK.TOKleg:
		case TOK.TOKule:
		case TOK.TOKul:
		case TOK.TOKuge:
		case TOK.TOKug:
		case TOK.TOKue:
			nextToken();
			e2 = parseShiftExp();
			e = new CmpExp(op, loc, e, e2);
			break;

		case TOK.TOKin:
			nextToken();
			e2 = parseShiftExp();
			e = new InExp(loc, e, e2);
			break;

		default:
			break;
		}
		return e;
	}
	
    Expression parseAndExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		if (global.params.Dversion == 1)
		{
			e = parseEqualExp();
			while (token.value == TOK.TOKand)
			{
				nextToken();
				e2 = parseEqualExp();
				e = new AndExp(loc,e,e2);
				loc = this.loc;
			}
		}
		else
		{
			e = parseCmpExp();
			while (token.value == TOK.TOKand)
			{
				nextToken();
				e2 = parseCmpExp();
				e = new AndExp(loc,e,e2);
				loc = this.loc;
			}
		}
		return e;
	}
	
    Expression parseXorExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseAndExp();
		while (token.value == TOK.TOKxor)
		{
			nextToken();
			e2 = parseAndExp();
			e = new XorExp(loc, e, e2);
		}

		return e;
	}

    Expression parseOrExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseXorExp();
		while (token.value == TOK.TOKor)
		{
			nextToken();
			e2 = parseXorExp();
			e = new OrExp(loc, e, e2);
		}
		return e;
	}
	
    Expression parseAndAndExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseOrExp();
		while (token.value == TOK.TOKandand)
		{
			nextToken();
			e2 = parseOrExp();
			e = new AndAndExp(loc, e, e2);
		}
		return e;
	}
	
    Expression parseOrOrExp()
	{
		Expression e;
		Expression e2;
		Loc loc = this.loc;

		e = parseAndAndExp();
		while (token.value == TOK.TOKoror)
		{
			nextToken();
			e2 = parseAndAndExp();
			e = new OrOrExp(loc, e, e2);
		}

		return e;
	}
	
    Expression parseCondExp()
	{
		Expression e;
		Expression e1;
		Expression e2;
		Loc loc = this.loc;

		e = parseOrOrExp();
		if (token.value == TOK.TOKquestion)
		{
			nextToken();
			e1 = parseExpression();
			check(TOK.TOKcolon);
			e2 = parseCondExp();
			e = new CondExp(loc, e, e1, e2);
		}
		return e;
	}
	
    Expression parseAssignExp()
	{
		Expression e;
		Expression e2;
		Loc loc;

		e = parseCondExp();
		while (1)
		{
			loc = this.loc;
			switch (token.value)
			{
				case TOK.TOKassign:  nextToken(); e2 = parseAssignExp(); e = new AssignExp(loc,e,e2); continue;
				case TOK.TOKaddass:  nextToken(); e2 = parseAssignExp(); e = new AddAssignExp(loc,e,e2); continue;
				case TOK.TOKminass:  nextToken(); e2 = parseAssignExp(); e = new MinAssignExp(loc,e,e2); continue;
				case TOK.TOKmulass:  nextToken(); e2 = parseAssignExp(); e = new MulAssignExp(loc,e,e2); continue;
				case TOK.TOKdivass:  nextToken(); e2 = parseAssignExp(); e = new DivAssignExp(loc,e,e2); continue;
				case TOK.TOKmodass:  nextToken(); e2 = parseAssignExp(); e = new ModAssignExp(loc,e,e2); continue;
				case TOK.TOKpowass:  nextToken(); e2 = parseAssignExp(); e = new PowAssignExp(loc,e,e2); continue;
				case TOK.TOKandass:  nextToken(); e2 = parseAssignExp(); e = new AndAssignExp(loc,e,e2); continue;
				case TOK.TOKorass:   nextToken(); e2 = parseAssignExp(); e = new OrAssignExp(loc,e,e2); continue;
				case TOK.TOKxorass:  nextToken(); e2 = parseAssignExp(); e = new XorAssignExp(loc,e,e2); continue;
				case TOK.TOKshlass:  nextToken(); e2 = parseAssignExp(); e = new ShlAssignExp(loc,e,e2); continue;
				case TOK.TOKshrass:  nextToken(); e2 = parseAssignExp(); e = new ShrAssignExp(loc,e,e2); continue;
				case TOK.TOKushrass: nextToken(); e2 = parseAssignExp(); e = new UshrAssignExp(loc,e,e2); continue;
				case TOK.TOKcatass:  nextToken(); e2 = parseAssignExp(); e = new CatAssignExp(loc,e,e2); continue;
				
				default:
					break;
			}
			break;
		}

		return e;
	}
	
	/*************************
	 * Collect argument list.
	 * Assume current token is ',', '(' or '['.
	 */
    Expressions parseArguments()
	{
		// function call
		Expressions arguments = new Expressions();
		Expression arg;
		TOK endtok;
		
		if (token.value == TOK.TOKlbracket)
			endtok = TOK.TOKrbracket;
		else
			endtok = TOK.TOKrparen;

		{
			nextToken();
			if (token.value != endtok)
			{
				while (1)
				{
					arg = parseAssignExp();
					arguments.push(arg);
					if (token.value == endtok)
						break;
					check(TOK.TOKcomma);
				}
			}
			check(endtok);
		}
		return arguments;
	}

    Expression 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();
				auto 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 = cast(TypeAArray)t;
			Type index = taa.index;

			Expression e2 = index.toExpression();
			if (e2)
			{   
				arguments = new Expressions();
				arguments.push(e2);
				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 = cast(TypeSArray)t;
			Expression ee = tsa.dim;

			arguments = new Expressions();
			arguments.push(ee);
			t = new TypeDArray(tsa.next);
		}
		else if (token.value == TOKlparen)
		{
			arguments = parseArguments();
		}

		e = new NewExp(loc, thisexp, newargs, t, arguments);
		return e;
	}
	
    void addComment(Dsymbol s, const(char)[] blockComment)
	{
		s.addComment(combineComments(blockComment, token.lineComment));
		token.lineComment = null;
	}
}