Logo Search packages:      
Sourcecode: ldc version File versions

parse.c

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


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


Generated by  Doxygen 1.6.0   Back to index