
// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 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 "rmem.h"
#include "lexer.h"
#include "parse.h"
#include "init.h"
#include "attrib.h"
#include "cond.h"
#include "mtype.h"
#include "template.h"
#include "staticassert.h"
#include "expression.h"
#include "statement.h"
#include "module.h"
#include "dsymbol.h"
#include "import.h"
#include "declaration.h"
#include "aggregate.h"
#include "enum.h"
#include "id.h"
#include "version.h"
#include "aliasthis.h"

// 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

// Support left-to-right array declarations
#define LTORARRAYDECL	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;

	nextToken();
#if DMDV2
	if (token.value == TOKlparen)
	{
	    nextToken();
	    if (token.value != TOKidentifier)
	    {	error("module (system) 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);
	}
#endif

	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);

	    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;
    unsigned stc;
    Condition *condition;
    unsigned char *comment;

    //printf("Parser::parseDeclDefs()\n");
    decldefs = new Array();
    do
    {
	comment = token.blockComment;
	switch (token.value)
	{
	    case TOKenum:
		s = parseEnum();
		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();
		decldefs->append(a);
		continue;

	    case TOKthis:
		s = parseCtor();
		break;

	    case TOKtilde:
		s = parseDtor();
		break;

	    case TOKinvariant:
#if 1
		s = parseInvariant();
#else
		if (peek(&token)->value == TOKlcurly)
		    s = parseInvariant();
		else
		{
		    stc = STCinvariant;
		    goto Lstc;
		}
#endif
		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:	  stc = STCconst;	 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;
#if DMDV2
	    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;
#endif

	    Lstc:
		nextToken();
	    Lstc2:
		switch (token.value)
		{
		    case TOKconst:	  stc |= STCconst;	 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 TOKinvariant:    stc |= STCinvariant;   goto Lstc;
		    default:
			break;
		}

		/* Look for auto initializers:
		 *	storage_class identifier = initializer;
		 */
		if (token.value == TOKidentifier &&
		    peek(&token)->value == TOKassign)
		{
		    while (1)
		    {
			Identifier *ident = token.ident;
			nextToken();
			nextToken();
			Initializer *init = parseInitializer();
			VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
			v->storage_class = stc;
			s = v;
			if (token.value == TOKsemicolon)
			{
			    nextToken();
			}
			else if (token.value == TOKcomma)
			{
			    nextToken();
			    if (token.value == TOKidentifier &&
				peek(&token)->value == TOKassign)
			    {
				decldefs->push(s);
				addComment(s, comment);
				continue;
			    }
			    else
				error("Identifier expected following comma");
			}
			else
			    error("semicolon expected following auto declaration, not '%s'", token.toChars());
			break;
		    }
		}
		else
		{   a = parseBlock();
		    s = new StorageClassDeclaration(stc, 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;

		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(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 TOKeof:
	    error("declaration expected following attribute, not EOF");
	    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)
	{
#if _WIN32
	    link = LINKwindows;
#else
	    link = LINKc;
#endif
	}
	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 }
 * Current token is 'this'.
 */

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

    nextToken();
    arguments = parseParameters(&varargs);
    f = new CtorDeclaration(loc, 0, arguments, varargs);
    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;
	Expression *ae;

	storageClass = STCin;		// parameter is "in" by default
	switch (token.value)
	{
	    case TOKrparen:
		break;

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

	    case TOKin:
		storageClass = STCin;
		nextToken();
		goto L1;

	    case TOKout:
		storageClass = STCout;
		nextToken();
		goto L1;

	    case TOKinout:
	    case TOKref:
		storageClass = STCref;
		nextToken();
		goto L1;

	    case TOKlazy:
		storageClass = STClazy;
		nextToken();
		goto L1;

	    default:
	    L1:
		tb = parseBasicType();
		at = parseDeclarator(tb, &ai);
		ae = NULL;
		if (token.value == TOKassign)	// = defaultArg
		{   nextToken();
		    ae = parseAssignExp();
		    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;
		}
		a = new Argument(storageClass, at, ai, ae);
		arguments->push(a);
		if (token.value == TOKcomma)
		{   nextToken();
		    continue;
		}
		break;
	}
	break;
    }
    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();
    }
    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)
	{
	    if (token.value == TOKidentifier)
	    {	EnumMember *em;
		Expression *value;
		Identifier *ident;

		loc = this->loc;
		ident = token.ident;
		value = NULL;
		nextToken();
		if (token.value == TOKassign)
		{
		    nextToken();
		    value = parseAssignExp();
		}
		em = new EnumMember(loc, ident, value);
		e->members->push(em);
		if (token.value == TOKrcurly)
		    ;
		else
		{   addComment(em, comment);
		    comment = NULL;
		    check(TOKcomma);
		}
		addComment(em, comment);
		comment = token.blockComment;
	    }
	    else
	    {	error("enum member expected");
		nextToken();
	    }
	}
	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;

    //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();
	}
    }

    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)
    {	Array *decldefs;
	TemplateDeclaration *tempdecl;

	// Wrap a template around the aggregate declaration
	decldefs = new Array();
	decldefs->push(a);
	tempdecl = new TemplateDeclaration(loc, id, tpl, NULL, decldefs);
	return tempdecl;
    }

    return a;
}

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

BaseClasses *Parser::parseBaseClasses()
{
    enum PROT protection = PROTpublic;
    BaseClasses *baseclasses = new BaseClasses();

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

    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, NULL, decldefs);
    return tempdecl;

Lerr:
    return NULL;
}

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

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

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

    // Get array of TemplateParameters
    if (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();
		if (token.value != TOKidentifier)
		{   error("Identifier expected for template parameter");
		    goto Lerr;
		}
		tp_ident = token.ident;
		nextToken();
		if (token.value == TOKcolon)	// : Type
		{
		    nextToken();
		    tp_spectype = parseBasicType();
		    tp_spectype = parseDeclarator(tp_spectype, NULL);
		}
		if (token.value == TOKassign)	// = Type
		{
		    nextToken();
		    tp_defaulttype = parseBasicType();
		    tp_defaulttype = parseDeclarator(tp_defaulttype, NULL);
		}
		tp = new TemplateAliasParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
	    }
	    else if (t->value == TOKcolon || t->value == TOKassign ||
		     t->value == TOKcomma || t->value == TOKrparen)
	    {	// TypeParameter
		if (token.value != TOKidentifier)
		{   error("Identifier expected for template parameter");
		    goto Lerr;
		}
		tp_ident = token.ident;
		nextToken();
		if (token.value == TOKcolon)	// : Type
		{
		    nextToken();
		    tp_spectype = parseBasicType();
		    tp_spectype = parseDeclarator(tp_spectype, NULL);
		}
		if (token.value == TOKassign)	// = Type
		{
		    nextToken();
		    tp_defaulttype = parseBasicType();
		    tp_defaulttype = parseDeclarator(tp_defaulttype, NULL);
		}
		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 = parseBasicType();
		tp_valtype = parseDeclarator(tp_valtype, &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 = parseCondExp();
		}
		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)
	{   Expression *exp;

	    nextToken();
	    check(TOKlparen);
	    exp = parseExpression();
	    check(TOKrparen);
	    tqual = new TypeTypeof(loc, exp);
	    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();
	    tiargs = parseTemplateArgumentList();
	}

	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");
    Objects *tiargs = new Objects();
    if (token.value != TOKlparen)
    {   error("!(TemplateArgumentList) expected following TemplateIdentifier");
	return tiargs;
    }
    nextToken();

    // Get TemplateArgumentList
    if (token.value != TOKrparen)
    {
	while (1)
	{
	    // See if it is an Expression or a Type
	    if (isDeclaration(&token, 0, TOKreserved, NULL))
	    {	// Type
		Type *ta;

		// Get TemplateArgument
		ta = parseBasicType();
		ta = parseDeclarator(ta, NULL);
		tiargs->push(ta);
	    }
	    else
	    {	// Expression
		Expression *ea;

		ea = parseAssignExp();
		tiargs->push(ea);
	    }
	    if (token.value != TOKcomma)
		break;
	    nextToken();
	}
    }
    check(TOKrparen, "template argument list");
    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, id, 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;
}

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

    /* Take care of the storage class prefixes that
     * serve as type attributes:
     *  const shared, shared const, const, invariant, shared
     */
    if (token.value == TOKconst && peekNext() == TOKshared && peekNext2() != TOKlparen ||
	token.value == TOKshared && peekNext() == TOKconst && peekNext2() != TOKlparen)
    {
	nextToken();
	nextToken();
	/* shared const type
	 */
	t = parseType(pident, tpl);
	t = t->makeSharedConst();
	return t;
    }
    else if (token.value == TOKconst && peekNext() != TOKlparen)
    {
	nextToken();
	/* const type
	 */
	t = parseType(pident, tpl);
	t = t->makeConst();
	return t;
    }
    else if ((token.value == TOKinvariant || token.value == TOKimmutable) &&
             peekNext() != TOKlparen)
    {
	nextToken();
	/* invariant type
	 */
	t = parseType(pident, tpl);
	t = t->makeInvariant();
	return t;
    }
    else if (token.value == TOKshared && peekNext() != TOKlparen)
    {
	nextToken();
	/* shared type
	 */
	t = parseType(pident, tpl);
	t = t->makeShared();
	return t;
    }
    else
	t = parseBasicType();
    t = parseDeclarator(t, pident, tpl);
    return t;
}
#endif

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

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

	case TOKidentifier:
	    id = token.ident;
	    nextToken();
	    if (token.value == TOKnot)
	    {
		nextToken();
		tempinst = new TemplateInstance(loc, id);
		tempinst->tiargs = parseTemplateArgumentList();
		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)
		{
		    nextToken();
		    tempinst = new TemplateInstance(loc, id);
		    tempinst->tiargs = parseTemplateArgumentList();
		    tid->addIdent((Identifier *)tempinst);
		}
		else
		    tid->addIdent(id);
	    }
	    t = tid;
	    break;

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

	case TOKtypeof:
	{   Expression *exp;

	    nextToken();
	    check(TOKlparen);
	    exp = parseExpression();
	    check(TOKrparen);
	    tid = new TypeTypeof(loc, exp);
	    goto Lident2;
	}

	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)
{
    Type *ts;
    Type *ta;

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

	    case TOKlbracket:
#if LTORARRAYDECL
		// 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
		    Type *index;

		    //printf("it's an associative array\n");
		    index = parseBasicType();
		    index = parseDeclarator(index, NULL);	// [ type ]
		    t = new TypeAArray(t, index);
		    check(TOKrbracket);
		}
		else
		{
		    //printf("it's [expression]\n");
		    inBrackets++;
		    Expression *e = parseExpression();		// [ expression ]
		    if (token.value == TOKslice)
		    {	Expression *e2;

			nextToken();
			e2 = parseExpression();			// [ exp .. exp ]
			t = new TypeSlice(t, e, e2);
		    }
		    else
			t = new TypeSArray(t,e);
		    inBrackets--;
		    check(TOKrbracket);
		}
		continue;
#else
		// Handle []. Make sure things like
		//     int[3][1] a;
		// is (array[3] of array[1] of int)
		ts = t;
		while (token.value == TOKlbracket)
		{
		    nextToken();
		    if (token.value == TOKrbracket)
		    {
			ta = new TypeDArray(t);			// []
			nextToken();
		    }
		    else if (isDeclaration(&token, 0, TOKrbracket, NULL))
		    {   // It's an associative array declaration
			Type *index;

			//printf("it's an associative array\n");
			index = parseBasicType();
			index = parseDeclarator(index, NULL);	// [ type ]
			check(TOKrbracket);
			ta = new TypeAArray(t, index);
		    }
		    else
		    {
			//printf("it's [expression]\n");
			Expression *e = parseExpression();	// [ expression ]
			ta = new TypeSArray(t,e);
			check(TOKrbracket);
		    }
		    Type **pt;
		    for (pt = &ts; *pt != t; pt = &(*pt)->next)
			;
		    *pt = ta;
		}
		t = ts;
		continue;
#endif

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

		nextToken();
		arguments = parseParameters(&varargs);
		t = new TypeFunction(arguments, t, varargs, linkage);
		if (save == TOKdelegate)
		    t = new TypeDelegate(t);
		else
		    t = new TypePointer(t);	// pointer to function
		continue;
	    }

	    default:
		ts = t;
		break;
	}
	break;
    }
    return ts;
}

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

    //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.
		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 declaration
		    Type *index;

		    //printf("it's an associative array\n");
		    index = parseBasicType();
		    index = parseDeclarator(index, NULL);	// [ type ]
		    check(TOKrbracket);
		    ta = new TypeAArray(t, index);
		}
		else
		{
		    //printf("it's [expression]\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 = &(*pt)->next)
		    ;
		*pt = ta;
		continue;
	    }
#endif
	    case TOKlparen:
	    {	Arguments *arguments;
		int varargs;

		if (tpl)
		{
		    /* Look ahead to see if this is (...)(...),
		     * i.e. a function template declaration
		     */
		    if (peekPastParen(&token)->value == TOKlparen)
		    {   // It's a function template declaration
			//printf("function template declaration\n");

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

		arguments = parseParameters(&varargs);
		Type *ta = new TypeFunction(arguments, t, varargs, linkage);
		Type **pt;
		for (pt = &ts; *pt != t; pt = &(*pt)->next)
		    ;
		*pt = ta;
		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()
{
    enum STC 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()\n");
    switch (token.value)
    {
	case TOKtypedef:
	case TOKalias:
	    tok = token.value;
	    nextToken();
	    break;

	default:
	    tok = TOKreserved;
	    break;
    }

    storage_class = STCundefined;
    while (1)
    {
	switch (token.value)
	{
	    case TOKconst:	stc = STCconst;		 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;
#if DMDV2
	    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;
#endif
	    L1:
		if (storage_class & stc)
		    error("redundant storage class '%s'", token.toChars());
		storage_class = (STC) (storage_class | stc);
		nextToken();
		continue;

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

		link = parseLinkage();
		continue;

	    default:
		break;
	}
	break;
    }

    a = new Array();

    /* Look for auto initializers:
     *	storage_class identifier = initializer;
     */
    while (storage_class &&
	token.value == TOKidentifier &&
	peek(&token)->value == TOKassign)
    {
	ident = token.ident;
	nextToken();
	nextToken();
	Initializer *init = parseInitializer();
	VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
	v->storage_class = storage_class;
	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))
	    {
		error("Identifier expected following comma");
	    }
	    else
		continue;
	}
	else
	    error("semicolon expected following auto declaration, not '%s'", token.toChars());
	return a;
    }

    if (token.value == TOKclass)
    {	AggregateDeclaration *s;

	s = (AggregateDeclaration *)parseAggregate();
	s->storage_class |= storage_class;
	a->push(s);
	addComment(s, comment);
	return a;
    }

    ts = parseBasicType();
    ts = parseBasicType2(ts);
    tfirst = NULL;

    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)
	{   FuncDeclaration *f =
		new FuncDeclaration(loc, 0, ident, storage_class, t);
	    addComment(f, comment);
	    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);
	    }
	    if (tpl)			// it's a function template
	    {   Array *decldefs;
		TemplateDeclaration *tempdecl;

		// Wrap a template around the aggregate declaration
		decldefs = new Array();
		decldefs->push(s);
		tempdecl = new TemplateDeclaration(loc, s->ident, tpl, NULL, 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 ';', 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:
#if DMDV2
	case TOKimmutable:
	case TOKshared:
#endif
//	case TOKtypeof:
	Ldeclaration:
	{   Array *a;

	    a = parseDeclarations();
	    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 CompoundDeclarationStatement(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:
	{   Dsymbol *d;

	    d = parseEnum();
	    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 = STCin;
		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;
		    }
		}
		tb = parseBasicType();
		at = parseDeclarator(tb, &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();
	    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(STCin, 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 *tb;
		Type *at;
		Identifier *ai;

		tb = parseBasicType();
		at = parseDeclarator(tb, &ai);
		check(TOKassign);
		arg = new Argument(STCin, 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(STCin, 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);
		    t = parseBasicType();
		    id = NULL;
		    t = parseDeclarator(t, &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 CompoundStatement(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) &&
	peek(t)->value != TOKlparen)
    {	/* const type
	 * invariant type
	 */
	t = peek(t);
    }
#endif

    if (!isBasicType(&t))
	return FALSE;
    if (!isDeclarator(&t, &haveId, endtok))
	return FALSE;
    if ( needId == 1 ||
	(needId == 0 && !haveId) ||
	(needId == 2 &&  haveId))
    {	if (pt)
	    *pt = t;
	return TRUE;
    }
    else
	return FALSE;
}

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

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

	case TOKidentifier:
	    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:
		    t = peek(t);
		    if (t->value != TOKlparen)
			goto Lfalse;
		    if (!skipParens(t, &t))
			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;

	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;
#if DMDV2
		while (1)
		{
		    switch (t->value)
		    {
			case TOKconst:
			case TOKinvariant:
			case TOKimmutable:
			case TOKshared:
			case TOKpure:
			case TOKnothrow:
			    t = peek(t);
			    continue;
			default:
			    break;
		    }
		    break;
		}
#endif
		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);
    while (1)
    {
	switch (t->value)
	{
	    case TOKrparen:
		break;

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

	    case TOKin:
	    case TOKout:
	    case TOKinout:
	    case TOKref:
	    case TOKlazy:
		t = peek(t);
	    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;
		}
		if (t->value == TOKcomma)
		{   t = peek(t);
		    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;

    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 TOKslice:
		if (brnest)
		    continue;
		break;

	    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 && peek(&token)->value == TOKlparen)
	    {	// identifier!(template-argument-list)
		TemplateInstance *tempinst;

		tempinst = new TemplateInstance(loc, id);
		nextToken();
		tempinst->tiargs = parseTemplateArgumentList();
		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 = typeDotIdExp(loc, t, token.ident);
	    nextToken();
	    break;

	case TOKtypeof:
	{   Expression *exp;

	    nextToken();
	    check(TOKlparen);
	    exp = parseExpression();
	    check(TOKrparen);
	    t = new TypeTypeof(loc, exp);
	    e = new TypeExp(loc, t);
	    break;
	}

	case TOKtypeid:
	{   Type *t;

	    nextToken();
	    check(TOKlparen, "typeid");
	    t = parseBasicType();
	    t = parseDeclarator(t,NULL);	// ( 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;
	    Loc loc = this->loc;

	    nextToken();
	    if (token.value == TOKlparen)
	    {
		nextToken();
		targ = parseBasicType();
		targ = parseDeclarator(targ, &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 ||
#if DMDV2
			 token.value == TOKconst && peek(&token)->value == TOKrparen ||
			 token.value == TOKinvariant && peek(&token)->value == TOKrparen ||
			 token.value == TOKimmutable && peek(&token)->value == TOKrparen ||
			 token.value == TOKshared && peek(&token)->value == TOKrparen ||
#endif
			 token.value == TOKfunction ||
			 token.value == TOKdelegate ||
			 token.value == TOKreturn))
		    {
			tok2 = token.value;
			nextToken();
		    }
		    else
		    {
			tspec = parseBasicType();
			tspec = parseDeclarator(tspec, NULL);
		    }
		}
		check(TOKrparen);
	    }
	    else
	    {   error("(type identifier : specialization) expected following is");
		goto Lerr;
	    }
	    e = new IsExp(loc, targ, ident, tok, tspec, tok2);
	    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 }
	     * delegate type(parameters) { body }
	     */
	    Arguments *arguments;
	    int varargs;
	    FuncLiteralDeclaration *fd;
	    Type *t;

	    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);
	    }
	    t = new TypeFunction(arguments, t, varargs, linkage);
	    fd = new FuncLiteralDeclaration(loc, 0, t, 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 parsePostExp(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 && peek(&token)->value == TOKlparen)
		    {   // identifier!(template-argument-list)
			TemplateInstance *tempinst;

			tempinst = new TemplateInstance(loc, id);
			nextToken();
			tempinst->tiargs = parseTemplateArgumentList();
			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);
	    t = parseBasicType();
	    t = parseDeclarator(t,NULL);	// ( 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 = parseBasicType();
			t = parseDeclarator(t,NULL);
			check(TOKrparen);

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

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

	Expression *e = index->toExpression();
	if (e)
	{   arguments = new Expressions();
	    arguments->push(e);
	    t = new TypeDArray(t->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(t->next);
    }
    else if (token.value == TOKlparen)
    {
	arguments = parseArguments();
    }
#else
    t = parseBasicType();
    while (token.value == TOKmul)
    {   t = new TypePointer(t);
	nextToken();
    }
    if (token.value == TOKlbracket)
    {
	Expression *e;

	nextToken();
	e = parseAssignExp();
	arguments = new Array();
	arguments->push(e);
	check(TOKrbracket);
	t = parseDeclarator(t, NULL);
	t = new TypeDArray(t);
    }
    else if (token.value == TOKlparen)
	arguments = parseArguments();
#endif
    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;
}


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

