view dmd2/parse.c @ 1083:c1e9f612e2e2

Fix for dual operand form of fistp, also make reg ST(0) explicit and fix lindquists previous code that allowed dual operand form of fstp but dissallowed the single operand form accidently
author Kelly Wilson <wilsonk cpsc.ucalgary.ca>
date Tue, 10 Mar 2009 06:23:26 -0600
parents 356e65836fb5
children 638d16625da2
line wrap: on
line source


// Compiler implementation of the D programming language
// Copyright (c) 1999-2008 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

// This is the D parser

#include <stdio.h>
#include <assert.h>

#include "mem.h"
#include "lexer.h"
#include "parse.h"
#include "init.h"
#include "attrib.h"
#include "cond.h"
#include "mtype.h"
#include "template.h"
#include "staticassert.h"
#include "expression.h"
#include "statement.h"
#include "module.h"
#include "dsymbol.h"
#include "import.h"
#include "declaration.h"
#include "aggregate.h"
#include "enum.h"
#include "id.h"
#include "version.h"

// How multiple declarations are parsed.
// If 1, treat as C.
// If 0, treat:
//	int *p, i;
// as:
//	int* p;
//	int* i;
#define CDECLSYNTAX	0

// Support C cast syntax:
//	(type)(expression)
#define CCASTSYNTAX	1

// Support postfix C array declarations, such as
//	int a[3][4];
#define CARRAYDECL	1


Parser::Parser(Module *module, unsigned char *base, unsigned length, int doDocComment)
    : Lexer(module, base, 0, length, doDocComment, 0)
{
    //printf("Parser::Parser()\n");
    md = NULL;
    linkage = LINKd;
    endloc = 0;
    inBrackets = 0;
    //nextToken();		// start up the scanner
}

Array *Parser::parseModule()
{
    Array *decldefs;

    // ModuleDeclation leads off
    if (token.value == TOKmodule)
    {
	unsigned char *comment = token.blockComment;
	bool safe = FALSE;

	nextToken();
	if (token.value == TOKlparen)
	{
	    nextToken();
	    if (token.value != TOKidentifier)
	    {	error("module (safe) identifier expected");
		goto Lerr;
	    }
	    Identifier *id = token.ident;

	    if (id == Id::system)
		safe = TRUE;
	    else
		error("(safe) expected, not %s", id->toChars());
	    nextToken();
	    check(TOKrparen);
	}

	if (token.value != TOKidentifier)
	{   error("Identifier expected following module");
	    goto Lerr;
	}
	else
	{
	    Array *a = NULL;
	    Identifier *id;

	    id = token.ident;
	    while (nextToken() == TOKdot)
	    {
		if (!a)
		    a = new Array();
		a->push(id);
		nextToken();
		if (token.value != TOKidentifier)
		{   error("Identifier expected following package");
		    goto Lerr;
		}
		id = token.ident;
	    }

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

	    if (token.value != TOKsemicolon)
		error("';' expected following module declaration instead of %s", token.toChars());
	    nextToken();
	    addComment(mod, comment);
	}
    }

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

Lerr:
    while (token.value != TOKsemicolon && token.value != TOKeof)
	nextToken();
    nextToken();
    return new Array();
}

Array *Parser::parseDeclDefs(int once)
{   Dsymbol *s;
    Array *decldefs;
    Array *a;
    Array *aelse;
    enum PROT prot;
    enum STC stc;
    unsigned storageClass;
    Condition *condition;
    unsigned char *comment;

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

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

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

	    case TOKtemplate:
		s = (Dsymbol *)parseTemplateDeclaration();
		break;

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

	    CASE_BASIC_TYPES:
	    case TOKalias:
	    case TOKtypedef:
	    case TOKidentifier:
	    case TOKtypeof:
	    case TOKdot:
	    Ldeclaration:
		a = parseDeclarations(STCundefined);
		decldefs->append(a);
		continue;

	    case TOKthis:
		s = parseCtor();
		break;

	    case TOKassign:
		s = parsePostBlit();
		break;

	    case TOKtilde:
		s = parseDtor();
		break;

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

	    case TOKunittest:
		s = parseUnitTest();
		break;

	    case TOKnew:
		s = parseNew();
		break;

	    case TOKdelete:
		s = parseDelete();
		break;

	    case TOKeof:
	    case TOKrcurly:
		return decldefs;

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

	    case TOKconst:
		if (peek(&token)->value == TOKlparen)
		    goto Ldeclaration;
		stc = STCconst;
		goto Lstc;

	    case TOKimmutable:
		if (peek(&token)->value == TOKlparen)
		    goto Ldeclaration;
		stc = STCinvariant;
		goto Lstc;

	    case TOKfinal:	  stc = STCfinal;	 goto Lstc;
	    case TOKauto:	  stc = STCauto;	 goto Lstc;
	    case TOKscope:	  stc = STCscope;	 goto Lstc;
	    case TOKoverride:	  stc = STCoverride;	 goto Lstc;
	    case TOKabstract:	  stc = STCabstract;	 goto Lstc;
	    case TOKsynchronized: stc = STCsynchronized; goto Lstc;
	    case TOKdeprecated:   stc = STCdeprecated;	 goto Lstc;
	    case TOKnothrow:      stc = STCnothrow;	 goto Lstc;
	    case TOKpure:         stc = STCpure;	 goto Lstc;
	    case TOKref:          stc = STCref;          goto Lstc;
	    case TOKtls:          stc = STCtls;		 goto Lstc;
	    //case TOKmanifest:	  stc = STCmanifest;	 goto Lstc;

	    Lstc:
		if (storageClass & stc)
		    error("redundant storage class %s", Token::toChars(token.value));
		{
		unsigned u = storageClass | stc;
		u &= STCconst | STCinvariant | STCmanifest;
		if (u & (u - 1))
		    error("conflicting storage class %s", Token::toChars(token.value));
		}
		nextToken();
	    Lstc2:
		storageClass |= stc;
		switch (token.value)
		{
		    case TOKconst:
		    case TOKinvariant:
		    case TOKimmutable:
			// If followed by a (, it is not a storage class
			if (peek(&token)->value == TOKlparen)
			    break;
			if (token.value == TOKconst)
			    stc = STCconst;
			else
			    stc = STCinvariant;
			goto Lstc;
		    case TOKfinal:	  stc = STCfinal;	 goto Lstc;
		    case TOKauto:	  stc = STCauto;	 goto Lstc;
		    case TOKscope:	  stc = STCscope;	 goto Lstc;
		    case TOKoverride:	  stc = STCoverride;	 goto Lstc;
		    case TOKabstract:	  stc = STCabstract;	 goto Lstc;
		    case TOKsynchronized: stc = STCsynchronized; goto Lstc;
		    case TOKdeprecated:   stc = STCdeprecated;	 goto Lstc;
		    case TOKnothrow:      stc = STCnothrow;	 goto Lstc;
		    case TOKpure:         stc = STCpure;	 goto Lstc;
		    case TOKref:          stc = STCref;          goto Lstc;
		    case TOKtls:          stc = STCtls;		 goto Lstc;
		    //case TOKmanifest:	  stc = STCmanifest;	 goto Lstc;
		    default:
			break;
		}

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

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

	    case TOKextern:
		if (peek(&token)->value != TOKlparen)
		{   stc = STCextern;
		    goto Lstc;
		}
	    {
		enum LINK linksave = linkage;
		linkage = parseLinkage();
		a = parseBlock();
		s = new LinkDeclaration(linkage, a);
		linkage = linksave;
		break;
	    }
	    case TOKprivate:	prot = PROTprivate;	goto Lprot;
	    case TOKpackage:	prot = PROTpackage;	goto Lprot;
	    case TOKprotected:	prot = PROTprotected;	goto Lprot;
	    case TOKpublic:	prot = PROTpublic;	goto Lprot;
	    case TOKexport:	prot = PROTexport;	goto Lprot;

	    Lprot:
		nextToken();
		switch (token.value)
		{
		    case TOKprivate:
		    case TOKpackage:
		    case TOKprotected:
		    case TOKpublic:
		    case TOKexport:
			error("redundant protection attribute");
			break;
		}
		a = parseBlock();
		s = new ProtDeclaration(prot, a);
		break;

	    case TOKalign:
	    {	unsigned n;

        // LDC better align code locations
        Loc alignloc = loc;

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

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

	    case TOKpragma:
	    {	Identifier *ident;
		Expressions *args = NULL;

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

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

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

		condition = parseDebugCondition();
		goto Lcondition;

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

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

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

	    default:
		error("Declaration expected, not '%s'",token.toChars());
	    Lerror:
		while (token.value != TOKsemicolon && token.value != TOKeof)
		    nextToken();
		nextToken();
		s = NULL;
		continue;
	}
	if (s)
	{   decldefs->push(s);
	    addComment(s, comment);
	}
    } while (!once);
    return decldefs;
}


/********************************************
 * Parse declarations after an align, protection, or extern decl.
 */

Array *Parser::parseBlock()
{
    Array *a = NULL;
    Dsymbol *s;

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

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

	case TOKcolon:
	    nextToken();
#if 0
	    a = NULL;
#else
	    a = parseDeclDefs(0);	// grab declarations up to closing curly bracket
#endif
	    break;

	default:
	    a = parseDeclDefs(1);
	    break;
    }
    return a;
}

/**********************************
 * Parse a static assertion.
 */

StaticAssert *Parser::parseStaticAssert()
{
    Loc loc = this->loc;
    Expression *exp;
    Expression *msg = NULL;

    //printf("parseStaticAssert()\n");
    nextToken();
    check(TOKlparen);
    exp = parseAssignExp();
    if (token.value == TOKcomma)
    {	nextToken();
	msg = parseAssignExp();
    }
    check(TOKrparen);
    check(TOKsemicolon);
    return new StaticAssert(loc, exp, msg);
}

/***********************************
 * Parse typeof(expression).
 * Current token is on the 'typeof'.
 */

#if DMDV2
TypeQualified *Parser::parseTypeof()
{   TypeQualified *t;
    Loc loc = this->loc;

    nextToken();
    check(TOKlparen);
    if (token.value == TOKreturn)	// typeof(return)
    {
	nextToken();
	t = new TypeReturn(loc);
    }
    else
    {	Expression *exp = parseExpression();	// typeof(expression)
	t = new TypeTypeof(loc, exp);
    }
    check(TOKrparen);
    return t;
}
#endif

/***********************************
 * Parse extern (linkage)
 * The parser is on the 'extern' token.
 */

enum LINK Parser::parseLinkage()
{
    enum LINK link = LINKdefault;
    nextToken();
    assert(token.value == TOKlparen);
    nextToken();
    if (token.value == TOKidentifier)
    {   Identifier *id = token.ident;

	nextToken();
	if (id == Id::Windows)
	    link = LINKwindows;
	else if (id == Id::Pascal)
	    link = LINKpascal;
	else if (id == Id::D)
	    link = LINKd;
	else if (id == Id::C)
	{
	    link = LINKc;
	    if (token.value == TOKplusplus)
	    {   link = LINKcpp;
		nextToken();
	    }
	}
	else if (id == Id::System)
	{
        // LDC we configure target at runtime
        if (global.params.os == OSWindows)
            link = LINKwindows;
        else
            link = LINKc;
	}
	else
	{
	    error("valid linkage identifiers are D, C, C++, Pascal, Windows, System");
	    link = LINKd;
	}
    }
    else
    {
	link = LINKd;		// default
    }
    check(TOKrparen);
    return link;
}

/**************************************
 * Parse a debug conditional
 */

Condition *Parser::parseDebugCondition()
{
    Condition *c;

    if (token.value == TOKlparen)
    {
	nextToken();
	unsigned level = 1;
	Identifier *id = NULL;

	if (token.value == TOKidentifier)
	    id = token.ident;
	else if (token.value == TOKint32v)
	    level = (unsigned)token.uns64value;
	else
	    error("identifier or integer expected, not %s", token.toChars());
	nextToken();
	check(TOKrparen);
	c = new DebugCondition(mod, level, id);
    }
    else
	c = new DebugCondition(mod, 1, NULL);
    return c;

}

/**************************************
 * Parse a version conditional
 */

Condition *Parser::parseVersionCondition()
{
    Condition *c;
    unsigned level = 1;
    Identifier *id = NULL;

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

    }
    else
       error("(condition) expected following version");
    c = new VersionCondition(mod, level, id);
    return c;

}

/***********************************************
 *	static if (expression)
 *	    body
 *	else
 *	    body
 */

Condition *Parser::parseStaticIfCondition()
{   Expression *exp;
    Condition *condition;
    Array *aif;
    Array *aelse;
    Loc loc = this->loc;

    nextToken();
    if (token.value == TOKlparen)
    {
	nextToken();
	exp = parseAssignExp();
	check(TOKrparen);
    }
    else
    {   error("(expression) expected following static if");
	exp = NULL;
    }
    condition = new StaticIfCondition(loc, exp);
    return condition;
}


/*****************************************
 * Parse a constructor definition:
 *	this(arguments) { body }
 * or postblit:
 *	this(this) { body }
 * Current token is 'this'.
 */

FuncDeclaration *Parser::parseCtor()
{
    Loc loc = this->loc;

    nextToken();
    if (token.value == TOKlparen && peek(&token)->value == TOKthis)
    {	// this(this) { ... }
	nextToken();
	nextToken();
	check(TOKrparen);
	PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0);
	parseContracts(f);
	return f;
    }
    int varargs;
    Arguments *arguments = parseParameters(&varargs);
    CtorDeclaration *f = new CtorDeclaration(loc, 0, arguments, varargs);
    parseContracts(f);
    return f;
}

/*****************************************
 * Parse a postblit definition:
 *	=this() { body }
 * Current token is '='.
 */

PostBlitDeclaration *Parser::parsePostBlit()
{
    Loc loc = this->loc;

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

    PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0);
    parseContracts(f);
    return f;
}

/*****************************************
 * Parse a destructor definition:
 *	~this() { body }
 * Current token is '~'.
 */

DtorDeclaration *Parser::parseDtor()
{
    DtorDeclaration *f;
    Loc loc = this->loc;

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

    f = new DtorDeclaration(loc, 0);
    parseContracts(f);
    return f;
}

/*****************************************
 * Parse a static constructor definition:
 *	static this() { body }
 * Current token is 'this'.
 */

StaticCtorDeclaration *Parser::parseStaticCtor()
{
    StaticCtorDeclaration *f;
    Loc loc = this->loc;

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

    f = new StaticCtorDeclaration(loc, 0);
    parseContracts(f);
    return f;
}

/*****************************************
 * Parse a static destructor definition:
 *	static ~this() { body }
 * Current token is '~'.
 */

StaticDtorDeclaration *Parser::parseStaticDtor()
{
    StaticDtorDeclaration *f;
    Loc loc = this->loc;

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

    f = new StaticDtorDeclaration(loc, 0);
    parseContracts(f);
    return f;
}

/*****************************************
 * Parse an invariant definition:
 *	invariant() { body }
 * Current token is 'invariant'.
 */

InvariantDeclaration *Parser::parseInvariant()
{
    InvariantDeclaration *f;
    Loc loc = this->loc;

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

    f = new InvariantDeclaration(loc, 0);
    f->fbody = parseStatement(PScurly);
    return f;
}

/*****************************************
 * Parse a unittest definition:
 *	unittest { body }
 * Current token is 'unittest'.
 */

UnitTestDeclaration *Parser::parseUnitTest()
{
    UnitTestDeclaration *f;
    Statement *body;
    Loc loc = this->loc;

    nextToken();

    body = parseStatement(PScurly);

    f = new UnitTestDeclaration(loc, this->loc);
    f->fbody = body;
    return f;
}

/*****************************************
 * Parse a new definition:
 *	new(arguments) { body }
 * Current token is 'new'.
 */

NewDeclaration *Parser::parseNew()
{
    NewDeclaration *f;
    Arguments *arguments;
    int varargs;
    Loc loc = this->loc;

    nextToken();
    arguments = parseParameters(&varargs);
    f = new NewDeclaration(loc, 0, arguments, varargs);
    parseContracts(f);
    return f;
}

/*****************************************
 * Parse a delete definition:
 *	delete(arguments) { body }
 * Current token is 'delete'.
 */

DeleteDeclaration *Parser::parseDelete()
{
    DeleteDeclaration *f;
    Arguments *arguments;
    int varargs;
    Loc loc = this->loc;

    nextToken();
    arguments = parseParameters(&varargs);
    if (varargs)
	error("... not allowed in delete function parameter list");
    f = new DeleteDeclaration(loc, 0, arguments);
    parseContracts(f);
    return f;
}

/**********************************************
 * Parse parameter list.
 */

Arguments *Parser::parseParameters(int *pvarargs)
{
    Arguments *arguments = new Arguments();
    int varargs = 0;
    int hasdefault = 0;

    check(TOKlparen);
    while (1)
    {   Type *tb;
	Identifier *ai = NULL;
	Type *at;
	Argument *a;
	unsigned storageClass = 0;
	unsigned stc;
	Expression *ae;

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

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

		case TOKconst:
		    if (peek(&token)->value == TOKlparen)
			goto Ldefault;
		    stc = STCconst;
		    goto L2;

		case TOKinvariant:
		case TOKimmutable:
		    if (peek(&token)->value == TOKlparen)
			goto Ldefault;
		    stc = STCinvariant;
		    goto L2;

		case TOKin:	   stc = STCin;		goto L2;
		case TOKout:	   stc = STCout;	goto L2;
		case TOKinout:
		case TOKref:	   stc = STCref;	goto L2;
		case TOKlazy:	   stc = STClazy;	goto L2;
		case TOKscope:	   stc = STCscope;	goto L2;
		case TOKfinal:	   stc = STCfinal;	goto L2;
		L2:
		    if (storageClass & stc ||
			(storageClass & STCin && stc & (STCconst | STCscope)) ||
			(stc & STCin && storageClass & (STCconst | STCscope))
		       )
			error("redundant storage class %s", Token::toChars(token.value));
		    storageClass |= stc;
		    {
		    unsigned u = storageClass & (STCconst | STCinvariant);
		    if (u & (u - 1))
			error("conflicting storage class %s", Token::toChars(token.value));
		    }
		    continue;

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

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

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

    L1:	;
    }
    check(TOKrparen);
    *pvarargs = varargs;
    return arguments;
}


/*************************************
 */

EnumDeclaration *Parser::parseEnum()
{   EnumDeclaration *e;
    Identifier *id;
    Type *memtype;
    Loc loc = this->loc;

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

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

    e = new EnumDeclaration(loc, id, memtype);
    if (token.value == TOKsemicolon && id)
 	nextToken();
    else if (token.value == TOKlcurly)
    {
	//printf("enum definition\n");
	e->members = new Array();
	nextToken();
	unsigned char *comment = token.blockComment;
	while (token.value != TOKrcurly)
	{
	    /* Can take the following forms:
	     *	1. ident
	     *	2. ident = value
	     *	3. type ident = value
	     */

	    loc = this->loc;

	    Type *type = NULL;
	    Identifier *ident;
	    Token *tp = peek(&token);
	    if (token.value == TOKidentifier &&
		(tp->value == TOKassign || tp->value == TOKcomma || tp->value == TOKrcurly))
	    {
		ident = token.ident;
		type = NULL;
		nextToken();
	    }
	    else
	    {
		type = parseType(&ident, NULL);
		if (id || memtype)
		    error("type only allowed if anonymous enum and no enum type");
	    }

	    Expression *value;
	    if (token.value == TOKassign)
	    {
		nextToken();
		value = parseAssignExp();
	    }
	    else
	    {	value = NULL;
		if (type)
		    error("if type, there must be an initializer");
	    }

	    EnumMember *em = new EnumMember(loc, ident, value, type);
	    e->members->push(em);

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

    //printf("-parseEnum() %s\n", e->toChars());
    return e;
}

/********************************
 * Parse struct, union, interface, class.
 */

Dsymbol *Parser::parseAggregate()
{   AggregateDeclaration *a = NULL;
    int anon = 0;
    enum TOK tok;
    Identifier *id;
    TemplateParameters *tpl = NULL;
    Expression *constraint = NULL;

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

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

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

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

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

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

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

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

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

	default:
	    assert(0);
	    break;
    }
    if (a && token.value == TOKsemicolon)
    { 	nextToken();
    }
    else if (token.value == TOKlcurly)
    {
	//printf("aggregate definition\n");
	nextToken();
	Array *decl = parseDeclDefs(0);
	if (token.value != TOKrcurly)
	    error("} expected following member declarations in aggregate");
	nextToken();
	if (anon)
	{
	    /* Anonymous structs/unions are more like attributes.
	     */
	    return new AnonDeclaration(loc, anon - 1, decl);
	}
	else
	    a->members = decl;
    }
    else
    {
	error("{ } expected following aggregate declaration");
	a = new StructDeclaration(loc, NULL);
    }

    if (tpl)
    {	// Wrap a template around the aggregate declaration

	Array *decldefs = new Array();
	decldefs->push(a);
	TemplateDeclaration *tempdecl =
		new TemplateDeclaration(loc, id, tpl, constraint, decldefs);
	return tempdecl;
    }

    return a;
}

/*******************************************
 */

BaseClasses *Parser::parseBaseClasses()
{
    BaseClasses *baseclasses = new BaseClasses();

    for (; 1; nextToken())
    {
	enum PROT protection = PROTpublic;
	switch (token.value)
	{
	    case TOKprivate:
		protection = PROTprivate;
		nextToken();
		break;
	    case TOKpackage:
		protection = PROTpackage;
		nextToken();
		break;
	    case TOKprotected:
		protection = PROTprotected;
		nextToken();
		break;
	    case TOKpublic:
		protection = PROTpublic;
		nextToken();
		break;
	}
	if (token.value == TOKidentifier)
	{
	    BaseClass *b = new BaseClass(parseBasicType(), protection);
	    baseclasses->push(b);
	    if (token.value != TOKcomma)
		break;
	}
	else
	{
	    error("base classes expected instead of %s", token.toChars());
	    return NULL;
	}
    }
    return baseclasses;
}

/**************************************
 * Parse constraint.
 * Constraint is of the form:
 *	if ( ConstraintExpression )
 */

#if DMDV2
Expression *Parser::parseConstraint()
{   Expression *e = NULL;

    if (token.value == TOKif)
    {
	nextToken();	// skip over 'if'
	check(TOKlparen);
	e = parseExpression();
	check(TOKrparen);
    }
    return e;
}
#endif

/**************************************
 * Parse a TemplateDeclaration.
 */

TemplateDeclaration *Parser::parseTemplateDeclaration()
{
    TemplateDeclaration *tempdecl;
    Identifier *id;
    TemplateParameters *tpl;
    Array *decldefs;
    Expression *constraint = NULL;
    Loc loc = this->loc;

    nextToken();
    if (token.value != TOKidentifier)
    {   error("TemplateIdentifier expected following template");
	goto Lerr;
    }
    id = token.ident;
    nextToken();
    tpl = parseTemplateParameterList();
    if (!tpl)
	goto Lerr;

    constraint = parseConstraint();

    if (token.value != TOKlcurly)
    {	error("members of template declaration expected");
	goto Lerr;
    }
    else
    {
	nextToken();
	decldefs = parseDeclDefs(0);
	if (token.value != TOKrcurly)
	{   error("template member expected");
	    goto Lerr;
	}
	nextToken();
    }

    tempdecl = new TemplateDeclaration(loc, id, tpl, constraint, decldefs);
    return tempdecl;

Lerr:
    return NULL;
}

/******************************************
 * Parse template parameter list.
 * Input:
 *	flag	0: parsing "( list )"
 *		1: parsing non-empty "list )"
 */

TemplateParameters *Parser::parseTemplateParameterList(int flag)
{
    TemplateParameters *tpl = new TemplateParameters();

    if (!flag && token.value != TOKlparen)
    {   error("parenthesized TemplateParameterList expected following TemplateIdentifier");
	goto Lerr;
    }
    nextToken();

    // Get array of TemplateParameters
    if (flag || token.value != TOKrparen)
    {	int isvariadic = 0;

	while (1)
	{   TemplateParameter *tp;
	    Identifier *tp_ident = NULL;
	    Type *tp_spectype = NULL;
	    Type *tp_valtype = NULL;
	    Type *tp_defaulttype = NULL;
	    Expression *tp_specvalue = NULL;
	    Expression *tp_defaultvalue = NULL;
	    Token *t;

	    // Get TemplateParameter

	    // First, look ahead to see if it is a TypeParameter or a ValueParameter
	    t = peek(&token);
	    if (token.value == TOKalias)
	    {	// AliasParameter
		nextToken();
		Type *spectype = NULL;
		if (isDeclaration(&token, 2, TOKreserved, NULL))
		{
		    spectype = parseType(&tp_ident);
		}
		else
		{
		    if (token.value != TOKidentifier)
		    {   error("identifier expected for template alias parameter");
			goto Lerr;
		    }
		    tp_ident = token.ident;
		    nextToken();
		}
		Object *spec = NULL;
		if (token.value == TOKcolon)	// : Type
		{
		    nextToken();
		    if (isDeclaration(&token, 0, TOKreserved, NULL))
			spec = parseType();
		    else
			spec = parseCondExp();
		}
		Object *def = NULL;
		if (token.value == TOKassign)	// = Type
		{
		    nextToken();
		    if (isDeclaration(&token, 0, TOKreserved, NULL))
			def = parseType();
		    else
			def = parseCondExp();
		}
		tp = new TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
	    }
	    else if (t->value == TOKcolon || t->value == TOKassign ||
		     t->value == TOKcomma || t->value == TOKrparen)
	    {	// TypeParameter
		if (token.value != TOKidentifier)
		{   error("identifier expected for template type parameter");
		    goto Lerr;
		}
		tp_ident = token.ident;
		nextToken();
		if (token.value == TOKcolon)	// : Type
		{
		    nextToken();
		    tp_spectype = parseType();
		}
		if (token.value == TOKassign)	// = Type
		{
		    nextToken();
		    tp_defaulttype = parseType();
		}
		tp = new TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
	    }
	    else if (token.value == TOKidentifier && t->value == TOKdotdotdot)
	    {	// ident...
		if (isvariadic)
		    error("variadic template parameter must be last");
		isvariadic = 1;
		tp_ident = token.ident;
		nextToken();
		nextToken();
		tp = new TemplateTupleParameter(loc, tp_ident);
	    }
#if DMDV2
	    else if (token.value == TOKthis)
	    {	// ThisParameter
		nextToken();
		if (token.value != TOKidentifier)
		{   error("identifier expected for template this parameter");
		    goto Lerr;
		}
		tp_ident = token.ident;
		nextToken();
		if (token.value == TOKcolon)	// : Type
		{
		    nextToken();
		    tp_spectype = parseType();
		}
		if (token.value == TOKassign)	// = Type
		{
		    nextToken();
		    tp_defaulttype = parseType();
		}
		tp = new TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
	    }
#endif
	    else
	    {	// ValueParameter
		tp_valtype = parseType(&tp_ident);
		if (!tp_ident)
		{
		    error("identifier expected for template value parameter");
		    tp_ident = new Identifier("error", TOKidentifier);
		}
		if (token.value == TOKcolon)	// : CondExpression
		{
		    nextToken();
		    tp_specvalue = parseCondExp();
		}
		if (token.value == TOKassign)	// = CondExpression
		{
		    nextToken();
		    tp_defaultvalue = parseDefaultInitExp();
		}
		tp = new TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
	    }
	    tpl->push(tp);
	    if (token.value != TOKcomma)
		break;
	    nextToken();
	}
    }
    check(TOKrparen);
Lerr:
    return tpl;
}

/******************************************
 * Parse template mixin.
 *	mixin Foo;
 *	mixin Foo!(args);
 *	mixin a.b.c!(args).Foo!(args);
 *	mixin Foo!(args) identifier;
 *	mixin typeof(expr).identifier!(args);
 */

Dsymbol *Parser::parseMixin()
{
    TemplateMixin *tm;
    Identifier *id;
    Type *tqual;
    Objects *tiargs;
    Array *idents;

    //printf("parseMixin()\n");
    nextToken();
    tqual = NULL;
    if (token.value == TOKdot)
    {
	id = Id::empty;
    }
    else
    {
	if (token.value == TOKtypeof)
	{
	    tqual = parseTypeof();
	    check(TOKdot);
	}
	if (token.value != TOKidentifier)
	{
	    error("identifier expected, not %s", token.toChars());
	    id = Id::empty;
	}
	else
	    id = token.ident;
	nextToken();
    }

    idents = new Array();
    while (1)
    {
	tiargs = NULL;
	if (token.value == TOKnot)
	{
	    nextToken();
	    if (token.value == TOKlparen)
		tiargs = parseTemplateArgumentList();
	    else
		tiargs = parseTemplateArgument();
	}

	if (token.value != TOKdot)
	    break;

	if (tiargs)
	{   TemplateInstance *tempinst = new TemplateInstance(loc, id);
	    tempinst->tiargs = tiargs;
	    id = (Identifier *)tempinst;
	    tiargs = NULL;
	}
	idents->push(id);

	nextToken();
	if (token.value != TOKidentifier)
	{   error("identifier expected following '.' instead of '%s'", token.toChars());
	    break;
	}
	id = token.ident;
	nextToken();
    }
    idents->push(id);

    if (token.value == TOKidentifier)
    {
	id = token.ident;
	nextToken();
    }
    else
	id = NULL;

    tm = new TemplateMixin(loc, id, tqual, idents, tiargs);
    if (token.value != TOKsemicolon)
	error("';' expected after mixin");
    nextToken();

    return tm;
}

/******************************************
 * Parse template argument list.
 * Input:
 * 	current token is opening '('
 * Output:
 *	current token is one after closing ')'
 */

Objects *Parser::parseTemplateArgumentList()
{
    //printf("Parser::parseTemplateArgumentList()\n");
    if (token.value != TOKlparen && token.value != TOKlcurly)
    {   error("!(TemplateArgumentList) expected following TemplateIdentifier");
	return new Objects();
    }
    return parseTemplateArgumentList2();
}

Objects *Parser::parseTemplateArgumentList2()
{
    Objects *tiargs = new Objects();
    enum TOK endtok = TOKrparen;
    nextToken();

    // Get TemplateArgumentList
    if (token.value != endtok)
    {
	while (1)
	{
	    // See if it is an Expression or a Type
	    if (isDeclaration(&token, 0, TOKreserved, NULL))
	    {	// Template argument is a type
		Type *ta = parseType();
		tiargs->push(ta);
	    }
	    else
	    {	// Template argument is an expression
		Expression *ea = parseAssignExp();
		tiargs->push(ea);
	    }
	    if (token.value != TOKcomma)
		break;
	    nextToken();
	}
    }
    check(endtok, "template argument list");
    return tiargs;
}

/*****************************
 * Parse single template argument, to support the syntax:
 *	foo!arg
 * Input:
 *	current token is the arg
 */

Objects *Parser::parseTemplateArgument()
{
    //printf("parseTemplateArgument()\n");
    Objects *tiargs = new Objects();
    Type *ta;
    switch (token.value)
    {
	case TOKidentifier:
	    ta = new TypeIdentifier(loc, token.ident);
	    goto LabelX;

	CASE_BASIC_TYPES_X(ta):
	    tiargs->push(ta);
	    nextToken();
	    break;

	case TOKint32v:
	case TOKuns32v:
	case TOKint64v:
	case TOKuns64v:
	case TOKfloat32v:
	case TOKfloat64v:
	case TOKfloat80v:
	case TOKimaginary32v:
	case TOKimaginary64v:
	case TOKimaginary80v:
	case TOKnull:
	case TOKtrue:
	case TOKfalse:
	case TOKcharv:
	case TOKwcharv:
	case TOKdcharv:
	case TOKstring:
	case TOKfile:
	case TOKline:
	{   // Template argument is an expression
	    Expression *ea = parsePrimaryExp();
	    tiargs->push(ea);
	    break;
	}

	default:
	    error("template argument expected following !");
	    break;
    }
    if (token.value == TOKnot)
	error("multiple ! arguments are not allowed");
    return tiargs;
}

Import *Parser::parseImport(Array *decldefs, int isstatic)
{   Import *s;
    Identifier *id;
    Identifier *aliasid = NULL;
    Array *a;
    Loc loc;

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

	loc = this->loc;
	a = NULL;
	id = token.ident;
	nextToken();
	if (!aliasid && token.value == TOKassign)
	{
	    aliasid = id;
	    goto L1;
	}
	while (token.value == TOKdot)
	{
	    if (!a)
		a = new Array();
	    a->push(id);
	    nextToken();
	    if (token.value != TOKidentifier)
	    {   error("Identifier expected following package");
		break;
	    }
	    id = token.ident;
	    nextToken();
	}

	s = new Import(loc, a, token.ident, aliasid, isstatic);
	decldefs->push(s);

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

		nextToken();
		if (token.value != TOKidentifier)
		{   error("Identifier expected following :");
		    break;
		}
		alias = token.ident;
		nextToken();
		if (token.value == TOKassign)
		{
		    nextToken();
		    if (token.value != TOKidentifier)
		    {   error("Identifier expected following %s=", alias->toChars());
			break;
		    }
		    name = token.ident;
		    nextToken();
		}
		else
		{   name = alias;
		    alias = NULL;
		}
		s->addAlias(name, alias);
	    } while (token.value == TOKcomma);
	    break;	// no comma-separated imports of this form
	}

	aliasid = NULL;
    } while (token.value == TOKcomma);

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

    return NULL;
}

Type *Parser::parseType(Identifier **pident, TemplateParameters **tpl)
{   Type *t;

    if (token.value == TOKconst && peek(&token)->value != TOKlparen)
    {
	nextToken();
	/* const type
	 */
	t = parseType(pident, tpl);
	t = t->makeConst();
	return t;
    }
    else if ((token.value == TOKinvariant || token.value == TOKimmutable) &&
             peek(&token)->value != TOKlparen)
    {
	nextToken();
	/* invariant type
	 */
	t = parseType(pident, tpl);
	t = t->makeInvariant();
	return t;
    }
    else
	t = parseBasicType();
    t = parseDeclarator(t, pident, tpl);
    return t;
}

Type *Parser::parseBasicType()
{   Type *t;
    Identifier *id;
    TypeQualified *tid;

    //printf("parseBasicType()\n");
    switch (token.value)
    {
	CASE_BASIC_TYPES_X(t):
	    nextToken();
	    break;

	case TOKidentifier:
	    id = token.ident;
	    nextToken();
	    if (token.value == TOKnot)
	    {	// ident!(template_arguments)
		TemplateInstance *tempinst = new TemplateInstance(loc, id);
		nextToken();
		if (token.value == TOKlparen)
		    // ident!(template_arguments)
		    tempinst->tiargs = parseTemplateArgumentList();
		else
		    // ident!template_argument
		    tempinst->tiargs = parseTemplateArgument();
		tid = new TypeInstance(loc, tempinst);
		goto Lident2;
	    }
	Lident:
	    tid = new TypeIdentifier(loc, id);
	Lident2:
	    while (token.value == TOKdot)
	    {	nextToken();
		if (token.value != TOKidentifier)
		{   error("identifier expected following '.' instead of '%s'", token.toChars());
		    break;
		}
		id = token.ident;
		nextToken();
		if (token.value == TOKnot)
		{
		    TemplateInstance *tempinst = new TemplateInstance(loc, id);
		    nextToken();
		    if (token.value == TOKlparen)
			// ident!(template_arguments)
			tempinst->tiargs = parseTemplateArgumentList();
		    else
			// ident!template_argument
			tempinst->tiargs = parseTemplateArgument();
		    tid->addIdent((Identifier *)tempinst);
		}
		else
		    tid->addIdent(id);
	    }
	    t = tid;
	    break;

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

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

	case TOKconst:
	    // const(type)
	    nextToken();
	    check(TOKlparen);
	    t = parseType();
	    check(TOKrparen);
	    t = t->makeConst();
	    break;

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

	default:
	    error("basic type expected, not %s", token.toChars());
	    t = Type::tint32;
	    break;
    }
    return t;
}

/******************************************
 * Parse things that follow the initial type t.
 *	t *
 *	t []
 *	t [type]
 *	t [expression]
 *	t [expression .. expression]
 *	t function
 *	t delegate
 */

Type *Parser::parseBasicType2(Type *t)
{
    //printf("parseBasicType2()\n");
    while (1)
    {
	switch (token.value)
	{
	    case TOKmul:
		t = new TypePointer(t);
		nextToken();
		continue;

	    case TOKlbracket:
		// Handle []. Make sure things like
		//     int[3][1] a;
		// is (array[1] of array[3] of int)
		nextToken();
		if (token.value == TOKrbracket)
		{
		    t = new TypeDArray(t);			// []
		    nextToken();
		}
		else if (isDeclaration(&token, 0, TOKrbracket, NULL))
		{   // It's an associative array declaration

		    //printf("it's an associative array\n");
		    Type *index = parseType();		// [ type ]
		    t = new TypeAArray(t, index);
		    check(TOKrbracket);
		}
		else
		{
		    //printf("it's type[expression]\n");
		    inBrackets++;
		    Expression *e = parseExpression();		// [ expression ]
		    if (token.value == TOKslice)
		    {
			nextToken();
			Expression *e2 = parseExpression();	// [ exp .. exp ]
			t = new TypeSlice(t, e, e2);
		    }
		    else
			t = new TypeSArray(t,e);
		    inBrackets--;
		    check(TOKrbracket);
		}
		continue;

	    case TOKdelegate:
	    case TOKfunction:
	    {	// Handle delegate declaration:
		//	t delegate(parameter list) nothrow pure
		//	t function(parameter list) nothrow pure
		Arguments *arguments;
		int varargs;
		bool ispure = false;
		bool isnothrow = false;
		enum TOK save = token.value;

		nextToken();
		arguments = parseParameters(&varargs);
		while (1)
		{   // Postfixes of 'pure' or 'nothrow'
		    if (token.value == TOKpure)
			ispure = true;
		    else if (token.value == TOKnothrow)
			isnothrow = true;
		    else
			break;
		    nextToken();
		}
		TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage);
		tf->ispure = ispure;
		tf->isnothrow = isnothrow;
		if (save == TOKdelegate)
		    t = new TypeDelegate(tf);
		else
		    t = new TypePointer(tf);	// pointer to function
		continue;
	    }

	    default:
		return t;
	}
	assert(0);
    }
    assert(0);
    return NULL;
}

Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl)
{   Type *ts;

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

    switch (token.value)
    {

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

	case TOKlparen:
	    /* Parse things with parentheses around the identifier, like:
	     *	int (*ident[3])[]
	     * although the D style would be:
	     *	int[]*[3] ident
	     */
	    nextToken();
	    ts = parseDeclarator(t, pident);
	    check(TOKrparen);
	    break;

	default:
	    ts = t;
	    break;
    }

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

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

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

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

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

		/* Parse const/invariant/nothrow/pure postfix
		 */
		while (1)
		{
		    switch (token.value)
		    {
			case TOKconst:
			    tf = tf->makeConst();
			    nextToken();
			    continue;

			case TOKinvariant:
			case TOKimmutable:
			    tf = tf->makeInvariant();
			    nextToken();
			    continue;

			case TOKnothrow:
			    ((TypeFunction *)tf)->isnothrow = 1;
			    nextToken();
			    continue;

			case TOKpure:
			    ((TypeFunction *)tf)->ispure = 1;
			    nextToken();
			    continue;
		    }
		    break;
		}

		/* Insert tf into
		 *   ts -> ... -> t
		 * so that
		 *   ts -> ... -> tf -> t
		 */
		Type **pt;
		for (pt = &ts; *pt != t; pt = &((TypeNext*)*pt)->next)
		    ;
		*pt = tf;
		break;
	    }
	}
	break;
    }

    return ts;
}

/**********************************
 * Parse Declarations.
 * These can be:
 *	1. declarations at global/class level
 *	2. declarations at statement level
 * Return array of Declaration *'s.
 */

Array *Parser::parseDeclarations(unsigned storage_class)
{
    enum STC stc;
    Type *ts;
    Type *t;
    Type *tfirst;
    Identifier *ident;
    Array *a;
    enum TOK tok = TOKreserved;
    unsigned char *comment = token.blockComment;
    enum LINK link = linkage;

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

    switch (token.value)
    {
	case TOKtypedef:
	case TOKalias:
	    tok = token.value;
	    nextToken();
	    break;
    }

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

	    case TOKinvariant:
	    case TOKimmutable:
		if (peek(&token)->value == TOKlparen)
		    break;
		stc = STCinvariant;
		goto L1;

	    case TOKshared:
		if (peek(&token)->value == TOKlparen)
		    break;
		stc = STCshared;
		goto L1;

	    case TOKstatic:	stc = STCstatic;	 goto L1;
	    case TOKfinal:	stc = STCfinal;		 goto L1;
	    case TOKauto:	stc = STCauto;		 goto L1;
	    case TOKscope:	stc = STCscope;		 goto L1;
	    case TOKoverride:	stc = STCoverride;	 goto L1;
	    case TOKabstract:	stc = STCabstract;	 goto L1;
	    case TOKsynchronized: stc = STCsynchronized; goto L1;
	    case TOKdeprecated: stc = STCdeprecated;	 goto L1;
	    case TOKnothrow:    stc = STCnothrow;	 goto L1;
	    case TOKpure:       stc = STCpure;		 goto L1;
	    case TOKref:        stc = STCref;            goto L1;
	    case TOKtls:        stc = STCtls;		 goto L1;
	    case TOKenum:	stc = STCmanifest;	 goto L1;
	    L1:
		if (storage_class & stc)
		    error("redundant storage class '%s'", token.toChars());
		storage_class = (STC) (storage_class | stc);
		{
		unsigned u = storage_class;
		u &= STCconst | STCinvariant | STCmanifest;
		if (u & (u - 1))
		    error("conflicting storage class %s", Token::toChars(token.value));
		}
		nextToken();
		continue;

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

		link = parseLinkage();
		continue;

	    default:
		break;
	}
	break;
    }

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

    if (token.value == TOKclass)
    {
	AggregateDeclaration *s = (AggregateDeclaration *)parseAggregate();
	s->storage_class |= storage_class;
	a = new Array();
	a->push(s);
	addComment(s, comment);
	return a;
    }

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

L2:
    tfirst = NULL;
    a = new Array();

    while (1)
    {
	Loc loc = this->loc;
	TemplateParameters *tpl = NULL;

	ident = NULL;
	t = parseDeclarator(ts, &ident, &tpl);
	assert(t);
	if (!tfirst)
	    tfirst = t;
	else if (t != tfirst)
	    error("multiple declarations must have the same type, not %s and %s",
		tfirst->toChars(), t->toChars());
	if (!ident)
	    error("no identifier for declarator %s", t->toChars());

	if (tok == TOKtypedef || tok == TOKalias)
	{   Declaration *v;
	    Initializer *init = NULL;

	    if (token.value == TOKassign)
	    {
		nextToken();
		init = parseInitializer();
	    }
	    if (tok == TOKtypedef)
		v = new TypedefDeclaration(loc, ident, t, init);
	    else
	    {	if (init)
		    error("alias cannot have initializer");
		v = new AliasDeclaration(loc, ident, t);
	    }
	    v->storage_class = storage_class;
	    if (link == linkage)
		a->push(v);
	    else
	    {
		Array *ax = new Array();
		ax->push(v);
		Dsymbol *s = new LinkDeclaration(link, ax);
		a->push(s);
	    }
	    switch (token.value)
	    {   case TOKsemicolon:
		    nextToken();
		    addComment(v, comment);
		    break;

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

		default:
		    error("semicolon expected to close %s declaration", Token::toChars(tok));
		    break;
	    }
	}
	else if (t->ty == Tfunction)
	{
	    TypeFunction *tf = (TypeFunction *)t;
	    Expression *constraint = NULL;
#if 0
	    if (Argument::isTPL(tf->parameters))
	    {
		if (!tpl)
		    tpl = new TemplateParameters();
	    }
#endif
	    FuncDeclaration *f =
		new FuncDeclaration(loc, 0, ident, (enum STC)storage_class, t);
	    addComment(f, comment);
	    if (tpl)
		constraint = parseConstraint();
	    parseContracts(f);
	    addComment(f, NULL);
	    Dsymbol *s;
	    if (link == linkage)
	    {
		s = f;
	    }
	    else
	    {
		Array *ax = new Array();
		ax->push(f);
		s = new LinkDeclaration(link, ax);
	    }
	    /* A template parameter list means it's a function template
	     */
	    if (tpl)
	    {
		// Wrap a template around the function declaration
		Array *decldefs = new Array();
		decldefs->push(s);
		TemplateDeclaration *tempdecl =
		    new TemplateDeclaration(loc, s->ident, tpl, constraint, decldefs);
		s = tempdecl;
	    }
	    addComment(s, comment);
	    a->push(s);
	}
	else
	{
	    Initializer *init = NULL;
	    if (token.value == TOKassign)
	    {
		nextToken();
		init = parseInitializer();
	    }

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

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

		default:
		    error("semicolon expected, not '%s'", token.toChars());
		    break;
	    }
	}
	break;
    }
    return a;
}

/*****************************************
 * Parse auto declarations of the form:
 *   storageClass ident = init, ident = init, ... ;
 * and return the array of them.
 * Starts with token on the first ident.
 * Ends with scanner past closing ';'
 */

#if DMDV2
Array *Parser::parseAutoDeclarations(unsigned storageClass, unsigned char *comment)
{
    Array *a = new Array;

    while (1)
    {
	Identifier *ident = token.ident;
	nextToken();		// skip over ident
	assert(token.value == TOKassign);
	nextToken();		// skip over '='
	Initializer *init = parseInitializer();
	VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
	v->storage_class = storageClass;
	a->push(v);
	if (token.value == TOKsemicolon)
	{
	    nextToken();
	    addComment(v, comment);
	}
	else if (token.value == TOKcomma)
	{
	    nextToken();
	    if (token.value == TOKidentifier &&
		peek(&token)->value == TOKassign)
	    {
		addComment(v, comment);
		continue;
	    }
	    else
		error("Identifier expected following comma");
	}
	else
	    error("semicolon expected following auto declaration, not '%s'", token.toChars());
	break;
    }
    return a;
}
#endif

/*****************************************
 * Parse contracts following function declaration.
 */

void Parser::parseContracts(FuncDeclaration *f)
{
    Type *tb;
    enum LINK linksave = linkage;

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

	case TOKbody:
	    nextToken();
	    f->fbody = parseStatement(PScurly);
	    f->endloc = endloc;
	    break;

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

#if 0	// Do we want this for function declarations, so we can do:
    // int x, y, foo(), z;
	case TOKcomma:
	    nextToken();
	    continue;
#endif

#if 0 // Dumped feature
	case TOKthrow:
	    if (!f->fthrows)
		f->fthrows = new Array();
	    nextToken();
	    check(TOKlparen);
	    while (1)
	    {
		tb = parseBasicType();
		f->fthrows->push(tb);
		if (token.value == TOKcomma)
		{   nextToken();
		    continue;
		}
		break;
	    }
	    check(TOKrparen);
	    goto L1;
#endif

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

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

	default:
	    error("semicolon expected following function declaration");
	    break;
    }
    linkage = linksave;
}

/*****************************************
 * Parse initializer for variable declaration.
 */

Initializer *Parser::parseInitializer()
{
    StructInitializer *is;
    ArrayInitializer *ia;
    ExpInitializer *ie;
    Expression *e;
    Identifier *id;
    Initializer *value;
    int comma;
    Loc loc = this->loc;
    Token *t;
    int braces;
    int brackets;

    switch (token.value)
    {
	case TOKlcurly:
	    /* Scan ahead to see if it is a struct initializer or
	     * a function literal.
	     * If it contains a ';', it is a function literal.
	     * Treat { } as a struct initializer.
	     */
	    braces = 1;
	    for (t = peek(&token); 1; t = peek(t))
	    {
		switch (t->value)
		{
		    case TOKsemicolon:
		    case TOKreturn:
			goto Lexpression;

		    case TOKlcurly:
			braces++;
			continue;

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

		    case TOKeof:
			break;

		    default:
			continue;
		}
		break;
	    }

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

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

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

		    case TOKeof:
			error("found EOF instead of initializer");
			break;

		    default:
			value = parseInitializer();
			is->addInit(NULL, value);
			comma = 1;
			continue;
			//error("found '%s' instead of field initializer", token.toChars());
			//break;
		}
		break;
	    }
	    return is;

	case TOKlbracket:
	    /* Scan ahead to see if it is an array initializer or
	     * an expression.
	     * If it ends with a ';' ',' or '}', it is an array initializer.
	     */
	    brackets = 1;
	    for (t = peek(&token); 1; t = peek(t))
	    {
		switch (t->value)
		{
		    case TOKlbracket:
			brackets++;
			continue;

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

		    case TOKeof:
			break;

		    default:
			continue;
		}
		break;
	    }

	    ia = new ArrayInitializer(loc);
	    nextToken();
	    comma = 0;
	    while (1)
	    {
		switch (token.value)
		{
		    default:
			if (comma == 1)
			{   error("comma expected separating array initializers, not %s", token.toChars());
			    nextToken();
			    break;
			}
			e = parseAssignExp();
			if (!e)
			    break;
			if (token.value == TOKcolon)
			{
			    nextToken();
			    value = parseInitializer();
			}
			else
			{   value = new ExpInitializer(e->loc, e);
			    e = NULL;
			}
			ia->addInit(e, value);
			comma = 1;
			continue;

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

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

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

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

	case TOKvoid:
	    t = peek(&token);
	    if (t->value == TOKsemicolon || t->value == TOKcomma)
	    {
		nextToken();
		return new VoidInitializer(loc);
	    }
	    goto Lexpression;

	default:
	Lexpression:
	    e = parseAssignExp();
	    ie = new ExpInitializer(loc, e);
	    return ie;
    }
}

/*****************************************
 * Parses default argument initializer expression that is an assign expression,
 * with special handling for __FILE__ and __LINE__.
 */

#if DMDV2
Expression *Parser::parseDefaultInitExp()
{
    if (token.value == TOKfile ||
	token.value == TOKline)
    {
	Token *t = peek(&token);
	if (t->value == TOKcomma || t->value == TOKrparen)
	{   Expression *e;

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

    Expression *e = parseAssignExp();
    return e;
}
#endif

/*****************************************
 * Input:
 *	flags	PSxxxx
 */

Statement *Parser::parseStatement(int flags)
{   Statement *s;
    Token *t;
    Condition *condition;
    Statement *ifbody;
    Statement *elsebody;
    Loc loc = this->loc;

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

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

    switch (token.value)
    {
	case TOKidentifier:
	    /* A leading identifier can be a declaration, label, or expression.
	     * The easiest case to check first is label:
	     */
	    t = peek(&token);
	    if (t->value == TOKcolon)
	    {	// It's a label

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

	case TOKassert:
	case TOKthis:
	case TOKsuper:
	case TOKint32v:
	case TOKuns32v:
	case TOKint64v:
	case TOKuns64v:
	case TOKfloat32v:
	case TOKfloat64v:
	case TOKfloat80v:
	case TOKimaginary32v:
	case TOKimaginary64v:
	case TOKimaginary80v:
	case TOKcharv:
	case TOKwcharv:
	case TOKdcharv:
	case TOKnull:
	case TOKtrue:
	case TOKfalse:
	case TOKstring:
	case TOKlparen:
	case TOKcast:
	case TOKmul:
	case TOKmin:
	case TOKadd:
	case TOKplusplus:
	case TOKminusminus:
	case TOKnew:
	case TOKdelete:
	case TOKdelegate:
	case TOKfunction:
	case TOKtypeid:
	case TOKis:
	case TOKlbracket:
#if DMDV2
	case TOKtraits:
	case TOKfile:
	case TOKline:
#endif
	Lexp:
	{   Expression *exp;

	    exp = parseExpression();
	    check(TOKsemicolon, "statement");
	    s = new ExpStatement(loc, exp);
	    break;
	}

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

	    t = peek(&token);
	    if (t->value == TOKassert)
	    {
		nextToken();
		s = new StaticAssertStatement(parseStaticAssert());
		break;
	    }
	    if (t->value == TOKif)
	    {
		nextToken();
		condition = parseStaticIfCondition();
		goto Lcondition;
	    }
	    goto Ldeclaration;
	}

	CASE_BASIC_TYPES:
	case TOKtypedef:
	case TOKalias:
	case TOKconst:
	case TOKauto:
	case TOKextern:
	case TOKfinal:
	case TOKinvariant:
	case TOKimmutable:
//	case TOKtypeof:
	Ldeclaration:
	{   Array *a;

	    a = parseDeclarations(STCundefined);
	    if (a->dim > 1)
	    {
		Statements *as = new Statements();
		as->reserve(a->dim);
		for (int i = 0; i < a->dim; i++)
		{
		    Dsymbol *d = (Dsymbol *)a->data[i];
		    s = new DeclarationStatement(loc, d);
		    as->push(s);
		}
		s = new CompoundStatement(loc, as);
	    }
	    else if (a->dim == 1)
	    {
		Dsymbol *d = (Dsymbol *)a->data[0];
		s = new DeclarationStatement(loc, d);
	    }
	    else
		assert(0);
	    if (flags & PSscope)
		s = new ScopeStatement(loc, s);
	    break;
	}

	case TOKstruct:
	case TOKunion:
	case TOKclass:
	case TOKinterface:
	{   Dsymbol *d;

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

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

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

	case TOKlcurly:
	{   Statements *statements;

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

	case TOKwhile:
	{   Expression *condition;
	    Statement *body;

	    nextToken();
	    check(TOKlparen);
	    condition = parseExpression();
	    check(TOKrparen);
	    body = parseStatement(PSscope);
	    s = new WhileStatement(loc, condition, body);
	    break;
	}

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

	case TOKdo:
	{   Statement *body;
	    Expression *condition;

	    nextToken();
	    body = parseStatement(PSscope);
	    check(TOKwhile);
	    check(TOKlparen);
	    condition = parseExpression();
	    check(TOKrparen);
	    s = new DoStatement(loc, body, condition);
	    break;
	}

	case TOKfor:
	{
	    Statement *init;
	    Expression *condition;
	    Expression *increment;
	    Statement *body;

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

	case TOKforeach:
	case TOKforeach_reverse:
	{
	    enum TOK op = token.value;
	    Arguments *arguments;

	    Statement *d;
	    Statement *body;
	    Expression *aggr;

	    nextToken();
	    check(TOKlparen);

	    arguments = new Arguments();

	    while (1)
	    {
		Type *tb;
		Identifier *ai = NULL;
		Type *at;
		unsigned storageClass;
		Argument *a;

		storageClass = 0;
		if (token.value == TOKinout || token.value == TOKref)
		{   storageClass = STCref;
		    nextToken();
		}
		if (token.value == TOKidentifier)
		{
		    Token *t = peek(&token);
		    if (t->value == TOKcomma || t->value == TOKsemicolon)
		    {	ai = token.ident;
			at = NULL;		// infer argument type
			nextToken();
			goto Larg;
		    }
		}
		at = parseType(&ai);
		if (!ai)
		    error("no identifier for declarator %s", at->toChars());
	      Larg:
		a = new Argument(storageClass, at, ai, NULL);
		arguments->push(a);
		if (token.value == TOKcomma)
		{   nextToken();
		    continue;
		}
		break;
	    }
	    check(TOKsemicolon);

	    aggr = parseExpression();
	    if (token.value == TOKslice && arguments->dim == 1)
	    {
		Argument *a = (Argument *)arguments->data[0];
		delete arguments;
		nextToken();
		Expression *upr = parseExpression();
		check(TOKrparen);
		body = parseStatement(0);
		s = new ForeachRangeStatement(loc, op, a, aggr, upr, body);
	    }
	    else
	    {
		check(TOKrparen);
		body = parseStatement(0);
		s = new ForeachStatement(loc, op, arguments, aggr, body);
	    }
	    break;
	}

	case TOKif:
	{   Argument *arg = NULL;
	    Expression *condition;
	    Statement *ifbody;
	    Statement *elsebody;

	    nextToken();
	    check(TOKlparen);

	    if (token.value == TOKauto)
	    {
		nextToken();
		if (token.value == TOKidentifier)
		{
		    Token *t = peek(&token);
		    if (t->value == TOKassign)
		    {
			arg = new Argument(0, NULL, token.ident, NULL);
			nextToken();
			nextToken();
		    }
		    else
		    {   error("= expected following auto identifier");
			goto Lerror;
		    }
		}
		else
		{   error("identifier expected following auto");
		    goto Lerror;
		}
	    }
	    else if (isDeclaration(&token, 2, TOKassign, NULL))
	    {
		Type *at;
		Identifier *ai;

		at = parseType(&ai);
		check(TOKassign);
		arg = new Argument(0, at, ai, NULL);
	    }

	    // Check for " ident;"
	    else if (token.value == TOKidentifier)
	    {
		Token *t = peek(&token);
		if (t->value == TOKcomma || t->value == TOKsemicolon)
		{
		    arg = new Argument(0, NULL, token.ident, NULL);
		    nextToken();
		    nextToken();
		    if (1 || !global.params.useDeprecated)
			error("if (v; e) is deprecated, use if (auto v = e)");
		}
	    }

	    condition = parseExpression();
	    check(TOKrparen);
	    ifbody = parseStatement(PSscope);
	    if (token.value == TOKelse)
	    {
		nextToken();
		elsebody = parseStatement(PSscope);
	    }
	    else
		elsebody = NULL;
	    s = new IfStatement(loc, arg, condition, ifbody, elsebody);
	    break;
	}

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

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

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

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

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

	case TOKpragma:
	{   Identifier *ident;
	    Expressions *args = NULL;
	    Statement *body;

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

	case TOKswitch:
	{   Expression *condition;
	    Statement *body;

	    nextToken();
	    check(TOKlparen);
	    condition = parseExpression();
	    check(TOKrparen);
	    body = parseStatement(PSscope);
	    s = new SwitchStatement(loc, condition, body);
	    break;
	}

	case TOKcase:
	{   Expression *exp;
	    Statements *statements;
	    Array cases;	// array of Expression's

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

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

	    // Keep cases in order by building the case statements backwards
	    for (int i = cases.dim; i; i--)
	    {
		exp = (Expression *)cases.data[i - 1];
		s = new CaseStatement(loc, exp, s);
	    }
	    break;
	}

	case TOKdefault:
	{
	    Statements *statements;

	    nextToken();
	    check(TOKcolon);

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

	case TOKreturn:
	{   Expression *exp;

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

	case TOKbreak:
	{   Identifier *ident;

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

	case TOKcontinue:
	{   Identifier *ident;

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

	case TOKgoto:
	{   Identifier *ident;

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

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

	case TOKsynchronized:
	{   Expression *exp;
	    Statement *body;

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

	case TOKwith:
	{   Expression *exp;
	    Statement *body;

	    nextToken();
	    check(TOKlparen);
	    exp = parseExpression();
	    check(TOKrparen);
	    body = parseStatement(PSscope);
	    s = new WithStatement(loc, exp, body);
	    break;
	}

	case TOKtry:
	{   Statement *body;
	    Array *catches = NULL;
	    Statement *finalbody = NULL;

	    nextToken();
	    body = parseStatement(PSscope);
	    while (token.value == TOKcatch)
	    {
		Statement *handler;
		Catch *c;
		Type *t;
		Identifier *id;
		Loc loc = this->loc;

		nextToken();
		if (token.value == TOKlcurly)
		{
		    t = NULL;
		    id = NULL;
		}
		else
		{
		    check(TOKlparen);
		    id = NULL;
		    t = parseType(&id);
		    check(TOKrparen);
		}
		handler = parseStatement(0);
		c = new Catch(loc, t, id, handler);
		if (!catches)
		    catches = new Array();
		catches->push(c);
	    }

	    if (token.value == TOKfinally)
	    {	nextToken();
		finalbody = parseStatement(0);
	    }

	    s = body;
	    if (!catches && !finalbody)
		error("catch or finally expected following try");
	    else
	    {	if (catches)
		    s = new TryCatchStatement(loc, body, catches);
		if (finalbody)
		    s = new TryFinallyStatement(loc, s, finalbody);
	    }
	    break;
	}

	case TOKthrow:
	{   Expression *exp;

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

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

	case TOKasm:
	{   Statements *statements;
	    Identifier *label;
	    Loc labelloc;
	    Token *toklist;
	    Token **ptoklist;

	    // Parse the asm block into a sequence of AsmStatements,
	    // each AsmStatement is one instruction.
	    // Separate out labels.
	    // Defer parsing of AsmStatements until semantic processing.

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

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

		    case TOKsemicolon:
			s = NULL;
			if (toklist || label)
			{   // Create AsmStatement from list of tokens we've saved
			    s = new AsmStatement(this->loc, toklist);
			    toklist = NULL;
			    ptoklist = &toklist;
			    if (label)
			    {   s = new LabelStatement(labelloc, label, s);
				label = NULL;
			    }
			    statements->push(s);
			}
			nextToken();
			continue;

		    case TOKeof:
			/* { */
			error("matching '}' expected, not end of file");
			break;

		    default:
		    Ldefault:
			*ptoklist = new Token();
			memcpy(*ptoklist, &token, sizeof(Token));
			ptoklist = &(*ptoklist)->next;
			*ptoklist = NULL;

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

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

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

    return s;
}

void Parser::check(enum TOK value)
{
    check(loc, value);
}

void Parser::check(Loc loc, enum TOK value)
{
    if (token.value != value)
	error(loc, "found '%s' when expecting '%s'", token.toChars(), Token::toChars(value));
    nextToken();
}

void Parser::check(enum TOK value, const char *string)
{
    if (token.value != value)
	error("found '%s' when expecting '%s' following '%s'",
	    token.toChars(), Token::toChars(value), string);
    nextToken();
}

/************************************
 * Determine if the scanner is sitting on the start of a declaration.
 * Input:
 *	needId	0	no identifier
 *		1	identifier optional
 *		2	must have identifier
 */

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

#if DMDV2
    if ((t->value == TOKconst || t->value == TOKinvariant || token.value == TOKimmutable) &&
	peek(t)->value != TOKlparen)
    {	/* const type
	 * invariant type
	 */
	t = peek(t);
    }
#endif

    if (!isBasicType(&t))
	goto Lisnot;
    if (!isDeclarator(&t, &haveId, endtok))
	goto Lisnot;
    if ( needId == 1 ||
	(needId == 0 && !haveId) ||
	(needId == 2 &&  haveId))
    {	if (pt)
	    *pt = t;
	goto Lis;
    }
    else
	goto Lisnot;

Lis:
    //printf("\tis declaration\n");
    return TRUE;

Lisnot:
    //printf("\tis not declaration\n");
    return FALSE;
}

int Parser::isBasicType(Token **pt)
{
    // This code parallels parseBasicType()
    Token *t = *pt;
    Token *t2;
    int parens;
    int haveId = 0;

    switch (t->value)
    {
	CASE_BASIC_TYPES:
	    t = peek(t);
	    break;

	case TOKidentifier:
	L5:
	    t = peek(t);
	    if (t->value == TOKnot)
	    {
		goto L4;
	    }
	    goto L3;
	    while (1)
	    {
	L2:
		t = peek(t);
	L3:
		if (t->value == TOKdot)
		{
	Ldot:
		    t = peek(t);
		    if (t->value != TOKidentifier)
			goto Lfalse;
		    t = peek(t);
		    if (t->value != TOKnot)
			goto L3;
	L4:
		    /* Seen a !
		     * Look for:
		     * !( args ), !identifier, etc.
		     */
		    t = peek(t);
		    switch (t->value)
		    {	case TOKidentifier:
			    goto L5;
			case TOKlparen:
			    if (!skipParens(t, &t))
				goto Lfalse;
			    break;
			CASE_BASIC_TYPES:
			case TOKint32v:
			case TOKuns32v:
			case TOKint64v:
			case TOKuns64v:
			case TOKfloat32v:
			case TOKfloat64v:
			case TOKfloat80v:
			case TOKimaginary32v:
			case TOKimaginary64v:
			case TOKimaginary80v:
			case TOKnull:
			case TOKtrue:
			case TOKfalse:
			case TOKcharv:
			case TOKwcharv:
			case TOKdcharv:
			case TOKstring:
			case TOKfile:
			case TOKline:
			    goto L2;
			default:
			    goto Lfalse;
		    }
		}
		else
		    break;
	    }
	    break;

	case TOKdot:
	    goto Ldot;

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

	case TOKconst:
	case TOKinvariant:
	case TOKimmutable:
	    // const(type)  or  invariant(type)
	    t = peek(t);
	    if (t->value != TOKlparen)
		goto Lfalse;
	    t = peek(t);
	    if (!isDeclaration(t, 0, TOKrparen, &t))
		goto Lfalse;
	    t = peek(t);
	    break;

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

Lfalse:
    //printf("is not\n");
    return FALSE;
}

int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok)
{   // This code parallels parseDeclarator()
    Token *t = *pt;
    int parens;

    //printf("Parser::isDeclarator()\n");
    //t->print();
    if (t->value == TOKassign)
	return FALSE;

    while (1)
    {
	parens = FALSE;
	switch (t->value)
	{
	    case TOKmul:
//	    case TOKand:
		t = peek(t);
		continue;

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

	    case TOKidentifier:
		if (*haveId)
		    return FALSE;
		*haveId = TRUE;
		t = peek(t);
		break;

	    case TOKlparen:
		t = peek(t);

		if (t->value == TOKrparen)
		    return FALSE;		// () is not a declarator

		/* Regard ( identifier ) as not a declarator
		 * BUG: what about ( *identifier ) in
		 *	f(*p)(x);
		 * where f is a class instance with overloaded () ?
		 * Should we just disallow C-style function pointer declarations?
		 */
		if (t->value == TOKidentifier)
		{   Token *t2 = peek(t);
		    if (t2->value == TOKrparen)
			return FALSE;
		}


		if (!isDeclarator(&t, haveId, TOKrparen))
		    return FALSE;
		t = peek(t);
		parens = TRUE;
		break;

	    case TOKdelegate:
	    case TOKfunction:
		t = peek(t);
		if (!isParameters(&t))
		    return FALSE;
		continue;
	}
	break;
    }

    while (1)
    {
	switch (t->value)
	{
#if CARRAYDECL
	    case TOKlbracket:
		parens = FALSE;
		t = peek(t);
		if (t->value == TOKrbracket)
		{
		    t = peek(t);
		}
		else if (isDeclaration(t, 0, TOKrbracket, &t))
		{   // It's an associative array declaration
		    t = peek(t);
		}
		else
		{
		    // [ expression ]
		    if (!isExpression(&t))
			return FALSE;
		    if (t->value != TOKrbracket)
			return FALSE;
		    t = peek(t);
		}
		continue;
#endif

	    case TOKlparen:
		parens = FALSE;
		if (!isParameters(&t))
		    return FALSE;
		while (1)
		{
		    switch (t->value)
		    {
			case TOKconst:
			case TOKinvariant:
			case TOKimmutable:
			case TOKpure:
			case TOKnothrow:
			    t = peek(t);
			    continue;
			default:
			    break;
		    }
		    break;
		}
		continue;

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

	    default:
		return FALSE;
	}
    }
}


int Parser::isParameters(Token **pt)
{   // This code parallels parseParameters()
    Token *t = *pt;
    int tmp;

    //printf("isParameters()\n");
    if (t->value != TOKlparen)
	return FALSE;

    t = peek(t);
    for (;1; t = peek(t))
    {
	switch (t->value)
	{
	    case TOKrparen:
		break;

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

	    case TOKin:
	    case TOKout:
	    case TOKinout:
	    case TOKref:
	    case TOKlazy:
	    case TOKconst:
	    case TOKinvariant:
	    case TOKimmutable:
	    case TOKfinal:
		continue;

#if 0
	    case TOKstatic:
		continue;
	    case TOKauto:
	    case TOKalias:
		t = peek(t);
		if (t->value == TOKidentifier)
		    t = peek(t);
		if (t->value == TOKassign)
		{   t = peek(t);
		    if (!isExpression(&t))
			return FALSE;
		}
		goto L3;
#endif

	    default:
		if (!isBasicType(&t))
		    return FALSE;
		tmp = FALSE;
		if (t->value != TOKdotdotdot &&
		    !isDeclarator(&t, &tmp, TOKreserved))
		    return FALSE;
		if (t->value == TOKassign)
		{   t = peek(t);
		    if (!isExpression(&t))
			return FALSE;
		}
		if (t->value == TOKdotdotdot)
		{
		    t = peek(t);
		    break;
		}
	    L3:
		if (t->value == TOKcomma)
		{
		    continue;
		}
		break;
	}
	break;
    }
    if (t->value != TOKrparen)
	return FALSE;
    t = peek(t);
    *pt = t;
    return TRUE;
}

int Parser::isExpression(Token **pt)
{
    // This is supposed to determine if something is an expression.
    // What it actually does is scan until a closing right bracket
    // is found.

    Token *t = *pt;
    int brnest = 0;
    int panest = 0;
    int curlynest = 0;

    for (;; t = peek(t))
    {
	switch (t->value)
	{
	    case TOKlbracket:
		brnest++;
		continue;

	    case TOKrbracket:
		if (--brnest >= 0)
		    continue;
		break;

	    case TOKlparen:
		panest++;
		continue;

	    case TOKcomma:
		if (brnest || panest)
		    continue;
		break;

	    case TOKrparen:
		if (--panest >= 0)
		    continue;
		break;

	    case TOKlcurly:
		curlynest++;
		continue;

	    case TOKrcurly:
		if (--curlynest >= 0)
		    continue;
		return FALSE;

	    case TOKslice:
		if (brnest)
		    continue;
		break;

	    case TOKsemicolon:
		if (curlynest)
		    continue;
		return FALSE;

	    case TOKeof:
		return FALSE;

	    default:
		continue;
	}
	break;
    }

    *pt = t;
    return TRUE;
}

/**********************************************
 * Skip over
 *	instance foo.bar(parameters...)
 * Output:
 *	if (pt), *pt is set to the token following the closing )
 * Returns:
 *	1	it's valid instance syntax
 *	0	invalid instance syntax
 */

int Parser::isTemplateInstance(Token *t, Token **pt)
{
    t = peek(t);
    if (t->value != TOKdot)
    {
	if (t->value != TOKidentifier)
	    goto Lfalse;
	t = peek(t);
    }
    while (t->value == TOKdot)
    {
	t = peek(t);
	if (t->value != TOKidentifier)
	    goto Lfalse;
	t = peek(t);
    }
    if (t->value != TOKlparen)
	goto Lfalse;

    // Skip over the template arguments
    while (1)
    {
	while (1)
	{
	    t = peek(t);
	    switch (t->value)
	    {
		case TOKlparen:
		    if (!skipParens(t, &t))
			goto Lfalse;
		    continue;
		case TOKrparen:
		    break;
		case TOKcomma:
		    break;
		case TOKeof:
		case TOKsemicolon:
		    goto Lfalse;
		default:
		    continue;
	    }
	    break;
	}

	if (t->value != TOKcomma)
	    break;
    }
    if (t->value != TOKrparen)
	goto Lfalse;
    t = peek(t);
    if (pt)
	*pt = t;
    return 1;

Lfalse:
    return 0;
}

/*******************************************
 * Skip parens, brackets.
 * Input:
 *	t is on opening (
 * Output:
 *	*pt is set to closing token, which is ')' on success
 * Returns:
 *	!=0	successful
 *	0	some parsing error
 */

int Parser::skipParens(Token *t, Token **pt)
{
    int parens = 0;

    while (1)
    {
	switch (t->value)
	{
	    case TOKlparen:
		parens++;
		break;

	    case TOKrparen:
		parens--;
		if (parens < 0)
		    goto Lfalse;
		if (parens == 0)
		    goto Ldone;
		break;

	    case TOKeof:
	    case TOKsemicolon:
		goto Lfalse;

	     default:
		break;
	}
	t = peek(t);
    }

  Ldone:
    if (*pt)
	*pt = t;
    return 1;

  Lfalse:
    return 0;
}

/********************************* Expression Parser ***************************/

Expression *Parser::parsePrimaryExp()
{   Expression *e;
    Type *t;
    Identifier *id;
    enum TOK save;
    Loc loc = this->loc;

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

		tempinst = new TemplateInstance(loc, id);
		nextToken();
		if (token.value == TOKlparen)
		    // ident!(template_arguments)
		    tempinst->tiargs = parseTemplateArgumentList();
		else
		    // ident!template_argument
		    tempinst->tiargs = parseTemplateArgument();
		e = new ScopeExp(loc, tempinst);
	    }
	    else
		e = new IdentifierExp(loc, id);
	    break;

	case TOKdollar:
	    if (!inBrackets)
		error("'$' is valid only inside [] of index or slice");
	    e = new DollarExp(loc);
	    nextToken();
	    break;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#if DMDV2
	case TOKfile:
	{   char *s = loc.filename ? loc.filename : mod->ident->toChars();
	    e = new StringExp(loc, s, strlen(s), 0);
	    nextToken();
	    break;
	}

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

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

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

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

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

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

	case TOKstring:
	{   unsigned char *s;
	    unsigned len;
	    unsigned char postfix;

	    // cat adjacent strings
	    s = token.ustring;
	    len = token.len;
	    postfix = token.postfix;
	    while (1)
	    {
		nextToken();
		if (token.value == TOKstring)
		{   unsigned len1;
		    unsigned len2;
		    unsigned char *s2;

		    if (token.postfix)
		    {	if (token.postfix != postfix)
			    error("mismatched string literal postfixes '%c' and '%c'", postfix, token.postfix);
			postfix = token.postfix;
		    }

		    len1 = len;
		    len2 = token.len;
		    len = len1 + len2;
		    s2 = (unsigned char *)mem.malloc((len + 1) * sizeof(unsigned char));
		    memcpy(s2, s, len1 * sizeof(unsigned char));
		    memcpy(s2 + len1, token.ustring, (len2 + 1) * sizeof(unsigned char));
		    s = s2;
		}
		else
		    break;
	    }
	    e = new StringExp(loc, s, len, postfix);
	    break;
	}

	CASE_BASIC_TYPES_X(t):
	    nextToken();
	L1:
	    check(TOKdot, t->toChars());
	    if (token.value != TOKidentifier)
	    {   error("found '%s' when expecting identifier following '%s.'", token.toChars(), t->toChars());
		goto Lerr;
	    }
	    e = new TypeDotIdExp(loc, t, token.ident);
	    nextToken();
	    break;

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

	case TOKtypeid:
	{   Type *t;

	    nextToken();
	    check(TOKlparen, "typeid");
	    t = parseType();		// ( type )
	    check(TOKrparen);
	    e = new TypeidExp(loc, t);
	    break;
	}

#if DMDV2
	case TOKtraits:
	{   /* __traits(identifier, args...)
	     */
	    Identifier *ident;
	    Objects *args = NULL;

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

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

	case TOKis:
	{   Type *targ;
	    Identifier *ident = NULL;
	    Type *tspec = NULL;
	    enum TOK tok = TOKreserved;
	    enum TOK tok2 = TOKreserved;
	    TemplateParameters *tpl = NULL;
	    Loc loc = this->loc;

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

	case TOKassert:
	{   Expression *msg = NULL;

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

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

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

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

	case TOKlbracket:
	{   /* Parse array literals and associative array literals:
	     *	[ value, value, value ... ]
	     *	[ key:value, key:value, key:value ... ]
	     */
	    Expressions *values = new Expressions();
	    Expressions *keys = NULL;

	    nextToken();
	    if (token.value != TOKrbracket)
	    {
		while (token.value != TOKeof)
		{
		    Expression *e = parseAssignExp();
		    if (token.value == TOKcolon && (keys || values->dim == 0))
		    {	nextToken();
			if (!keys)
			    keys = new Expressions();
			keys->push(e);
			e = parseAssignExp();
		    }
		    else if (keys)
		    {	error("'key:value' expected for associative array literal");
			delete keys;
			keys = NULL;
		    }
		    values->push(e);
		    if (token.value == TOKrbracket)
			break;
		    check(TOKcomma);
		}
	    }
	    check(TOKrbracket);

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

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

	case TOKfunction:
	case TOKdelegate:
	    save = token.value;
	    nextToken();
	case_delegate:
	{
	    /* function type(parameters) { body } pure nothrow
	     * delegate type(parameters) { body } pure nothrow
	     * (parameters) { body }
	     * { body }
	     */
	    Arguments *arguments;
	    int varargs;
	    FuncLiteralDeclaration *fd;
	    Type *t;
	    bool isnothrow = false;
	    bool ispure = false;

	    if (token.value == TOKlcurly)
	    {
		t = NULL;
		varargs = 0;
		arguments = new Arguments();
	    }
	    else
	    {
		if (token.value == TOKlparen)
		    t = NULL;
		else
		{
		    t = parseBasicType();
		    t = parseBasicType2(t);	// function return type
		}
		arguments = parseParameters(&varargs);
		while (1)
		{
		    if (token.value == TOKpure)
			ispure = true;
		    else if (token.value == TOKnothrow)
			isnothrow = true;
		    else
			break;
		    nextToken();
		}
	    }
	    TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage);
	    tf->ispure = ispure;
	    tf->isnothrow = isnothrow;
	    fd = new FuncLiteralDeclaration(loc, 0, tf, save, NULL);
	    parseContracts(fd);
	    e = new FuncExp(loc, fd);
	    break;
	}

	default:
	    error("expression expected, not '%s'", token.toChars());
	Lerr:
	    // Anything for e, as long as it's not NULL
	    e = new IntegerExp(loc, 0, Type::tint32);
	    nextToken();
	    break;
    }
    return e;
}

Expression *Parser::parsePostExp(Expression *e)
{
    Loc loc;

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

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

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

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

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

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

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

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

	    default:
		return e;
	}
	nextToken();
    }
}

Expression *Parser::parseUnaryExp()
{   Expression *e;
    Loc loc = this->loc;

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

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

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

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

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

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

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

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

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

	case TOKnew:
	    e = parseNewExp(NULL);
	    break;

	case TOKcast:				// cast(type) expression
	{   Type *t;

	    nextToken();
	    check(TOKlparen);
	    /* Look for cast(const) and cast(invariant)
	     */
	    if ((token.value == TOKconst || token.value == TOKinvariant || token.value == TOKimmutable) &&
		peek(&token)->value == TOKrparen)
	    {	enum TOK tok = token.value;
		nextToken();
		nextToken();
		e = parseUnaryExp();
		e = new CastExp(loc, e, tok);
	    }
	    else
	    {
		t = parseType();		// ( type )
		check(TOKrparen);
		e = parseUnaryExp();
		e = new CastExp(loc, e, t);
	    }
	    break;
	}

	case TOKlparen:
	{   Token *tk;

	    tk = peek(&token);
#if CCASTSYNTAX
	    // If cast
	    if (isDeclaration(tk, 0, TOKrparen, &tk))
	    {
		tk = peek(tk);		// skip over right parenthesis
		switch (tk->value)
		{
		    case TOKnot:
			tk = peek(tk);
			if (tk->value == TOKis)	// !is
			    break;
		    case TOKdot:
		    case TOKplusplus:
		    case TOKminusminus:
		    case TOKdelete:
		    case TOKnew:
		    case TOKlparen:
		    case TOKidentifier:
		    case TOKthis:
		    case TOKsuper:
		    case TOKint32v:
		    case TOKuns32v:
		    case TOKint64v:
		    case TOKuns64v:
		    case TOKfloat32v:
		    case TOKfloat64v:
		    case TOKfloat80v:
		    case TOKimaginary32v:
		    case TOKimaginary64v:
		    case TOKimaginary80v:
		    case TOKnull:
		    case TOKtrue:
		    case TOKfalse:
		    case TOKcharv:
		    case TOKwcharv:
		    case TOKdcharv:
		    case TOKstring:
#if 0
		    case TOKtilde:
		    case TOKand:
		    case TOKmul:
		    case TOKmin:
		    case TOKadd:
#endif
		    case TOKfunction:
		    case TOKdelegate:
		    case TOKtypeof:
#if DMDV2
		    case TOKfile:
		    case TOKline:
#endif
		    CASE_BASIC_TYPES:		// (type)int.size
		    {	// (type) una_exp
			Type *t;

			nextToken();
			t = parseType();
			check(TOKrparen);

			// if .identifier
			if (token.value == TOKdot)
			{
			    nextToken();
			    if (token.value != TOKidentifier)
			    {   error("Identifier expected following (type).");
				return NULL;
			    }
			    e = new TypeDotIdExp(loc, t, token.ident);
			    nextToken();
			    e = parsePostExp(e);
			}
			else
			{
			    e = parseUnaryExp();
			    e = new CastExp(loc, e, t);
			    error("C style cast illegal, use %s", e->toChars());
			}
			return e;
		    }
		}
	    }
#endif
	    e = parsePrimaryExp();
	    e = parsePostExp(e);
	    break;
	}
	default:
	    e = parsePrimaryExp();
	    e = parsePostExp(e);
	    break;
    }
    assert(e);
    return e;
}

Expression *Parser::parseMulExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

	    default:
		break;
	}
	break;
    }
    return e;
}

Expression *Parser::parseAddExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

	    default:
		break;
	}
	break;
    }
    return e;
}

Expression *Parser::parseShiftExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

	    default:
		break;
	}
	break;
    }
    return e;
}

Expression *Parser::parseRelExp()
{   Expression *e;
    Expression *e2;
    enum TOK op;
    Loc loc = this->loc;

    e = parseShiftExp();
    while (1)
    {
	switch (token.value)
	{
	    case TOKlt:
	    case TOKle:
	    case TOKgt:
	    case TOKge:
	    case TOKunord:
	    case TOKlg:
	    case TOKleg:
	    case TOKule:
	    case TOKul:
	    case TOKuge:
	    case TOKug:
	    case TOKue:
		op = token.value;
		nextToken();
		e2 = parseShiftExp();
		e = new CmpExp(op, loc, e, e2);
		continue;

	    case TOKin:
		nextToken();
		e2 = parseShiftExp();
		e = new InExp(loc, e, e2);
		continue;

	    default:
		break;
	}
	break;
    }
    return e;
}

Expression *Parser::parseEqualExp()
{   Expression *e;
    Expression *e2;
    Token *t;
    Loc loc = this->loc;

    e = parseRelExp();
    while (1)
    {	enum TOK value = token.value;

	switch (value)
	{
	    case TOKequal:
	    case TOKnotequal:
		nextToken();
		e2 = parseRelExp();
		e = new EqualExp(value, loc, e, e2);
		continue;

	    case TOKidentity:
		error("'===' is no longer legal, use 'is' instead");
		goto L1;

	    case TOKnotidentity:
		error("'!==' is no longer legal, use '!is' instead");
		goto L1;

	    case TOKis:
		value = TOKidentity;
		goto L1;

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

	    L1:
		nextToken();
		e2 = parseRelExp();
		e = new IdentityExp(value, loc, e, e2);
		continue;

	    default:
		break;
	}
	break;
    }
    return e;
}

Expression *Parser::parseCmpExp()
{   Expression *e;
    Expression *e2;
    Token *t;
    Loc loc = this->loc;

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

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

	case TOKis:
	    op = TOKidentity;
	    goto L1;

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

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

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

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

	default:
	    break;
    }
    return e;
}

Expression *Parser::parseAndExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

Expression *Parser::parseXorExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

Expression *Parser::parseOrExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

Expression *Parser::parseAndAndExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

Expression *Parser::parseOrOrExp()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

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

Expression *Parser::parseCondExp()
{   Expression *e;
    Expression *e1;
    Expression *e2;
    Loc loc = this->loc;

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

Expression *Parser::parseAssignExp()
{   Expression *e;
    Expression *e2;
    Loc loc;

    e = parseCondExp();
    while (1)
    {
	loc = this->loc;
	switch (token.value)
	{
#define X(tok,ector) \
	    case tok:  nextToken(); e2 = parseAssignExp(); e = new ector(loc,e,e2); continue;

	    X(TOKassign,    AssignExp);
	    X(TOKaddass,    AddAssignExp);
	    X(TOKminass,    MinAssignExp);
	    X(TOKmulass,    MulAssignExp);
	    X(TOKdivass,    DivAssignExp);
	    X(TOKmodass,    ModAssignExp);
	    X(TOKandass,    AndAssignExp);
	    X(TOKorass,     OrAssignExp);
	    X(TOKxorass,    XorAssignExp);
	    X(TOKshlass,    ShlAssignExp);
	    X(TOKshrass,    ShrAssignExp);
	    X(TOKushrass,   UshrAssignExp);
	    X(TOKcatass,    CatAssignExp);

#undef X
	    default:
		break;
	}
	break;
    }
    return e;
}

Expression *Parser::parseExpression()
{   Expression *e;
    Expression *e2;
    Loc loc = this->loc;

    //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
    e = parseAssignExp();
    while (token.value == TOKcomma)
    {
	nextToken();
	e2 = parseAssignExp();
	e = new CommaExp(loc, e, e2);
	loc = this->loc;
    }
    return e;
}


/*************************
 * Collect argument list.
 * Assume current token is ',', '(' or '['.
 */

Expressions *Parser::parseArguments()
{   // function call
    Expressions *arguments;
    Expression *arg;
    enum TOK endtok;

    arguments = new Expressions();
    if (token.value == TOKlbracket)
	endtok = TOKrbracket;
    else
	endtok = TOKrparen;

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

/*******************************************
 */

Expression *Parser::parseNewExp(Expression *thisexp)
{   Type *t;
    Expressions *newargs;
    Expressions *arguments = NULL;
    Expression *e;
    Loc loc = this->loc;

    nextToken();
    newargs = NULL;
    if (token.value == TOKlparen)
    {
	newargs = parseArguments();
    }

    // An anonymous nested class starts with "class"
    if (token.value == TOKclass)
    {
	nextToken();
	if (token.value == TOKlparen)
	    arguments = parseArguments();

	BaseClasses *baseclasses = NULL;
	if (token.value != TOKlcurly)
	    baseclasses = parseBaseClasses();

	Identifier *id = NULL;
	ClassDeclaration *cd = new ClassDeclaration(loc, id, baseclasses);

	if (token.value != TOKlcurly)
	{   error("{ members } expected for anonymous class");
	    cd->members = NULL;
	}
	else
	{
	    nextToken();
	    Array *decl = parseDeclDefs(0);
	    if (token.value != TOKrcurly)
		error("class member expected");
	    nextToken();
	    cd->members = decl;
	}

	e = new NewAnonClassExp(loc, thisexp, newargs, cd, arguments);

	return e;
    }

    t = parseBasicType();
    t = parseBasicType2(t);
    if (t->ty == Taarray)
    {	TypeAArray *taa = (TypeAArray *)t;
	Type *index = taa->index;

	Expression *e = index->toExpression();
	if (e)
	{   arguments = new Expressions();
	    arguments->push(e);
	    t = new TypeDArray(taa->next);
	}
	else
	{
	    error("need size of rightmost array, not type %s", index->toChars());
	    return new NullExp(loc);
	}
    }
    else if (t->ty == Tsarray)
    {
	TypeSArray *tsa = (TypeSArray *)t;
	Expression *e = tsa->dim;

	arguments = new Expressions();
	arguments->push(e);
	t = new TypeDArray(tsa->next);
    }
    else if (token.value == TOKlparen)
    {
	arguments = parseArguments();
    }
    e = new NewExp(loc, thisexp, newargs, t, arguments);
    return e;
}

/**********************************************
 */

void Parser::addComment(Dsymbol *s, unsigned char *blockComment)
{
    s->addComment(combineComments(blockComment, token.lineComment));
    token.lineComment = NULL;
}


/********************************* ***************************/