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


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

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

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

	nextToken();
#if 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, safe);

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

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

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

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

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

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

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

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

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

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

	    case TOKthis:
		s = parseCtor();
		break;

#if 0 // dead end, use this(this){} instead
	    case TOKassign:
		s = parsePostBlit();
		break;
#endif
	    case TOKtilde:
		s = parseDtor();
		break;

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

	    case TOKunittest:
		s = parseUnitTest();
		break;

	    case TOKnew:
		s = parseNew();
		break;

	    case TOKdelete:
		s = parseDelete();
		break;

	    case TOKeof:
	    case TOKrcurly:
		return decldefs;

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

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

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

	    case TOKshared:
		if (peek(&token)->value == TOKlparen)
		    goto Ldeclaration;
		stc = STCshared;
		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 TOKgshared:      stc = STCgshared;	 goto Lstc;
	    //case TOKmanifest:	  stc = STCmanifest;	 goto Lstc;
#endif

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

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

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

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

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

	    case TOKalign:
	    {	unsigned n;

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

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

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

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

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

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

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

		condition = parseDebugCondition();
		goto Lcondition;

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

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

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

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

/*********************************************
 * Give error on conflicting storage classes.
 */

void Parser::composeStorageClass(unsigned stc)
{
    unsigned u = stc;
    u &= STCconst | STCimmutable | STCmanifest;
    if (u & (u - 1))
	error("conflicting storage class %s", Token::toChars(token.value));
    u = stc;
    u &= STCgshared | STCshared | STCtls;
    if (u & (u - 1))
	error("conflicting storage class %s", Token::toChars(token.value));
}

/********************************************
 * 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)
	{
        // LDC we configure target at runtime
        if (global.params.os == OSWindows)
            link = LINKwindows;
        else
            link = LINKc;
	}
	else
	{
	    error("valid linkage identifiers are D, C, C++, Pascal, Windows, System");
	    link = LINKd;
	}
    }
    else
    {
	link = LINKd;		// default
    }
    check(TOKrparen);
    return link;
}

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

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

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

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

}

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

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

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

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

}

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

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

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


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

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

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

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

	int varargs;
	Arguments *arguments = parseParameters(&varargs);

	Expression *constraint = NULL;
	if (tpl)
	    constraint = parseConstraint();

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

	// Wrap a template around it
	Array *decldefs = new Array();
	decldefs->push(f);
	TemplateDeclaration *tempdecl =
	    new TemplateDeclaration(loc, f->ident, tpl, constraint, decldefs);
	return tempdecl;
    }

    /* Just a regular constructor
     */
    int varargs;
    Arguments *arguments = parseParameters(&varargs);
    CtorDeclaration *f = new CtorDeclaration(loc, 0, arguments, varargs);
    parseContracts(f);
    return f;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    nextToken();

    body = parseStatement(PScurly);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

	    loc = this->loc;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return a;
}

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

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

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

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

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

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

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

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

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

    constraint = parseConstraint();

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

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

Lerr:
    return NULL;
}

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

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

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

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

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

	    // Get TemplateParameter

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

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

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

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

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

	if (token.value != TOKdot)
	    break;

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

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

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

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

    return tm;
}

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

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

Objects *Parser::parseTemplateArgumentList2()
{
    //printf("Parser::parseTemplateArgumentList2()\n");
    Objects *tiargs = new Objects();
    enum TOK endtok = TOKrparen;
    nextToken();

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

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

			if (tpl)
			{   // Wrap a template around function fd
			    Array *decldefs = new Array();
			    decldefs->push(fd);
			    TemplateDeclaration *tempdecl =
				new TemplateDeclaration(fd->loc, fd->ident, tpl, NULL, decldefs);
			    tempdecl->literal = 1;	// it's a template 'literal'
			    tiargs->push(tempdecl);
			    goto L1;
			}
		    }
		}

		tiargs->push(ea);
	    }
	 L1:
	    if (token.value != TOKcomma)
		break;
	    nextToken();
	}
    }
    check(endtok, "template argument list");
    return tiargs;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	case TOKshared:
	    // shared(type)
	    nextToken();
	    check(TOKlparen);
	    t = parseType();
	    check(TOKrparen);
	    if (t->isConst())
		t = t->makeSharedConst();
	    else
		t = t->makeShared();
	    break;

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

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

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

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

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

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

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

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

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

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

    switch (token.value)
    {

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

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

	default:
	    ts = t;
	    break;
    }

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

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

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

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

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

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

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

			case TOKshared:
			    if (tf->isConst())
				tf = tf->makeSharedConst();
			    else
				tf = tf->makeShared();
			    nextToken();
			    continue;

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

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

			case TOKat:
			    nextToken();
			    if (token.value != TOKidentifier)
			    {   error("attribute identifier expected");
				nextToken();
				continue;
			    }
			    Identifier *id = token.ident;
			    if (id == Id::property)
				((TypeFunction *)tf)->ispure = 1;
			    else
				error("valid attribute identifiers are property, not %s", id->toChars());
			    nextToken();
			    continue;

		    }
		    break;
		}

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

    return ts;
}

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

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

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

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

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

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

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

	    case TOKstatic:	stc = STCstatic;	 goto L1;
	    case TOKfinal:	stc = STCfinal;		 goto L1;
	    case TOKauto:	stc = STCauto;		 goto L1;
	    case TOKscope:	stc = STCscope;		 goto L1;
	    case TOKoverride:	stc = STCoverride;	 goto L1;
	    case TOKabstract:	stc = STCabstract;	 goto L1;
	    case TOKsynchronized: stc = STCsynchronized; goto L1;
	    case TOKdeprecated: stc = STCdeprecated;	 goto L1;
#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 TOKgshared:    stc = STCgshared;	 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);
		composeStorageClass(storage_class);
		nextToken();
		continue;

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

		link = parseLinkage();
		continue;

	    default:
		break;
	}
	break;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		    case TOKlcurly:
			braces++;
			continue;

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

		    case TOKeof:
			break;

		    default:
			continue;
		}
		break;
	    }

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

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

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

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

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

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

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

		    case TOKeof:
			break;

		    default:
			continue;
		}
		break;
	    }

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

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

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

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

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

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

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

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

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

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

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

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

Statement *Parser::parseStatement(int flags)
{   Statement *s;
    Token *t;
    Condition *condition;
    Statement *ifbody;
    Statement *elsebody;
    bool isfinal;
    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 TOKfinal:
	    if (peekNext() == TOKswitch)
	    {
		nextToken();
		isfinal = TRUE;
		goto Lswitch;
	    }
	    goto Ldeclaration;

	CASE_BASIC_TYPES:
	case TOKtypedef:
	case TOKalias:
	case TOKconst:
	case TOKauto:
	case TOKextern:
	case TOKinvariant:
#if DMDV2
	case TOKimmutable:
	case TOKshared:
	case TOKnothrow:
	case TOKpure:
	case TOKtls:
	case TOKgshared:
#endif
//	case TOKtypeof:
	Ldeclaration:
	{   Array *a;

	    a = parseDeclarations(STCundefined);
	    if (a->dim > 1)
	    {
		Statements *as = new Statements();
		as->reserve(a->dim);
		for (int i = 0; i < a->dim; i++)
		{
		    Dsymbol *d = (Dsymbol *)a->data[i];
		    s = new DeclarationStatement(loc, d);
		    as->push(s);
		}
		s = new 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:
	{   /* Determine if this is a manifest constant declaration,
	     * or a conventional enum.
	     */
	    Dsymbol *d;
	    Token *t = peek(&token);
	    if (t->value == TOKlcurly || t->value == TOKcolon)
		d = parseEnum();
	    else if (t->value != TOKidentifier)
		goto Ldeclaration;
	    else
	    {
		t = peek(t);
		if (t->value == TOKlcurly || t->value == TOKcolon ||
		    t->value == TOKsemicolon)
		    d = parseEnum();
		else
		    goto Ldeclaration;
	    }
	    s = new DeclarationStatement(loc, d);
	    break;
	}

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

	case TOKlcurly:
	{   Statements *statements;

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

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

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

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

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

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

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

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

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

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

	    nextToken();
	    check(TOKlparen);

	    arguments = new Arguments();

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

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

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

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

	    nextToken();
	    check(TOKlparen);

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

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

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

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

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

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

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

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

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

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

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

	case TOKswitch:
	    isfinal = FALSE;
	    goto Lswitch;

	Lswitch:
	{
	    nextToken();
	    check(TOKlparen);
	    Expression *condition = parseExpression();
	    check(TOKrparen);
	    Statement *body = parseStatement(PSscope);
	    s = new SwitchStatement(loc, condition, body, isfinal);
	    break;
	}

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

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

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

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

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

	case TOKdefault:
	{
	    Statements *statements;

	    nextToken();
	    check(TOKcolon);

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

	case TOKreturn:
	{   Expression *exp;

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

	case TOKbreak:
	{   Identifier *ident;

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

	case TOKcontinue:
	{   Identifier *ident;

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

	case TOKgoto:
	{   Identifier *ident;

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

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

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

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

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

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

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

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

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

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

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

	case TOKthrow:
	{   Expression *exp;

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

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

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

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

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

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

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

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

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

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

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

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

    return s;
}

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

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

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

/************************************
 * Determine if the scanner is sitting on the start of a declaration.
 * Input:
 *	needId	0	no identifier
 *		1	identifier optional
 *		2	must have identifier
 * Output:
 *	if *pt is not NULL, it is set to the ending token, which would be endtok
 */

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 ||
	 t->value == TOKimmutable ||
	 t->value == TOKshared) &&
	peek(t)->value != TOKlparen)
    {	/* const type
	 * immutable type
	 * shared type
	 */
	t = peek(t);
    }
#endif

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

Lis:
    //printf("\tis declaration, t = %s\n", t->toChars());
    return TRUE;

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

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

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

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

	case TOKdot:
	    goto Ldot;

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

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

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

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

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

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

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

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

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

	    case TOKlparen:
		t = peek(t);

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

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


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

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

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

	    case TOKlparen:
		parens = FALSE;
		if (!isParameters(&t))
		    return FALSE;
#if DMDV2
		while (1)
		{
		    switch (t->value)
		    {
			case TOKconst:
			case TOKinvariant:
			case TOKimmutable:
			case TOKshared:
			case TOKpure:
			case TOKnothrow:
			    t = peek(t);
			    continue;
			case TOKat:
			    t = peek(t);	// skip '@'
			    t = peek(t);	// skip identifier
			    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;

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

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

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

	    case TOKin:
	    case TOKout:
	    case TOKinout:
	    case TOKref:
	    case TOKlazy:
	    case TOKfinal:
		continue;

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

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

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

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

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

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

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

	    case TOKlparen:
		panest++;
		continue;

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

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

	    case TOKlcurly:
		curlynest++;
		continue;

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

	    case TOKslice:
		if (brnest)
		    continue;
		break;

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

	    case TOKeof:
		return FALSE;

	    default:
		continue;
	}
	break;
    }

    *pt = t;
    return TRUE;
}

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

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

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

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

Lfalse:
    return 0;
}

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

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

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

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

	    case TOKeof:
	    case TOKsemicolon:
		goto Lfalse;

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

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

  Lfalse:
    return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

	case TOKfloat32v:
	    e = new RealExp(loc, token.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:
	{   const char *s = loc.filename ? loc.filename : mod->ident->toChars();
	    e = new StringExp(loc, (char *)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, (d_uns8)token.uns64value, Type::tchar);
	    nextToken();
	    break;

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

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

	case TOKstring:
	{   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:
	{
	    t = parseTypeof();
	    e = new TypeExp(loc, t);
	    break;
	}

	case TOKtypeid:
	{   Type *t;

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

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

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

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

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

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

	case TOKassert:
	{   Expression *msg = NULL;

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

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

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

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

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

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

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

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

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

	    if (token.value == TOKlcurly)
	    {
		t = NULL;
		varargs = 0;
		arguments = new Arguments();
	    }
	    else
	    {
		if (token.value == TOKlparen)
		    t = NULL;
		else
		{
		    t = parseBasicType();
		    t = parseBasicType2(t);	// function return type
		}
		arguments = parseParameters(&varargs);
		while (1)
		{
		    if (token.value == TOKpure)
			ispure = true;
		    else if (token.value == TOKnothrow)
			isnothrow = true;
		    else
			break;
		    nextToken();
		}
	    }

	    TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage);
	    tf->ispure = ispure;
	    tf->isnothrow = isnothrow;
	    fd = new FuncLiteralDeclaration(loc, 0, tf, save, NULL);
	    parseContracts(fd);
	    e = new FuncExp(loc, fd);
	    break;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	case TOKlparen:
	{   Token *tk;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	    case TOKis:
		value = TOKidentity;
		goto L1;

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

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

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

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

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

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

	case TOKis:
	    op = TOKidentity;
	    goto L1;

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

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

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

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

	default:
	    break;
    }
    return e;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

	return e;
    }

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

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

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

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

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


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

