/* (
-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All Rights Reserved.
// Copyright by the contributors to the Dafny Project
// SPDX-License-Identifier: MIT
//
//-----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
// Dafny
// Rustan Leino, first created 25 January 2008
//--------------------------------------------------------------------------*/
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Boogie;
using System.IO;
using System.Text;
using System.Linq;
using Microsoft.Dafny;
using System.CommandLine;
using static Microsoft.Dafny.ParseErrors;
COMPILER Dafny

/*--------------------------------------------------------------------------*/
CHARACTERS
  letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
  digit = "0123456789".
  posDigit = "123456789".
  posDigitFrom2 = "23456789".
  hexdigit = "0123456789ABCDEFabcdef".
  special = "'_?".
  highSurrogate = '\ud800' .. '\udbff'.
  lowSurrogate = '\udc00' .. '\udfff'.
  // These symbols are used but not needed as a production: "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\".
  cr        = '\r'.
  lf        = '\n'.
  tab       = '\t'.
  space = ' '.
  nondigit = letter + special.
  idchar = nondigit + digit.
  nonidchar = ANY - idchar.
  /* exclude the characters in 'array' and 'bv' and '\'' */
  nondigitMinusABTick = nondigit - 'a' - 'b' - '\''.
  nondigitMinusQuery = nondigit - '?'.
  idcharMinusA = idchar - 'a'.
  idcharMinusR = idchar - 'r'.
  idcharMinusY = idchar - 'y'.
  idcharMinusV = idchar - 'v'.
  idcharMinusPosDigitMinusQuery = idchar - posDigit - '?'.
  idcharMinusTick = idchar - '\''.
  /* string literals */
  charChar = ANY - '\'' - '\\' - cr - lf - highSurrogate - lowSurrogate.
  stringChar = ANY - '"' - '\\' - cr - lf.
  verbatimStringChar = ANY - '"'.

/*------------------------------------------------------------------------*/
TOKENS
  ident =  nondigitMinusABTick {idchar}       /* if char 0 is not an 'a' or 'b' or '\'', then anything else is fine */
        |  'a' [ idcharMinusR {idchar} ]      /* if char 0 is an 'a', then either there is no char 1 or char 1 is not an 'r' */
        |  'a' 'r' [ idcharMinusR {idchar} ]  /* etc. */
        |  'a' 'r' 'r' [ idcharMinusA {idchar} ]
        |  'a' 'r' 'r' 'a' [ idcharMinusY {idchar} ]
        |  'a' 'r' 'r' 'a' 'y' idcharMinusPosDigitMinusQuery {idchar}
        |  'a' 'r' 'r' 'a' 'y' '1' [ '?' ]
        |  'a' 'r' 'r' 'a' 'y' '?' idchar {idchar}
        |  'a' 'r' 'r' 'a' 'y' posDigit {digit} nondigitMinusQuery {idchar}
        |  'a' 'r' 'r' 'a' 'y' posDigit {digit} '?' idchar {idchar}
        |  'b' [ idcharMinusV {idchar} ]
        |  'b' 'v' [ nondigit {idchar} ]
        |  'b' 'v' '0' idchar {idchar}
        |  'b' 'v' posDigit {idchar} nondigit {idchar}
        |  "'" [ idchar ]                        /* if char 0 is a '\'' and length is 1 or 2, then it is an identifier */
        |  "'" idchar idcharMinusTick            /* if char 0 is '\'' and length is 3, then it is an identifier provided char 2 is not '\'' */
        |  "'" idchar idchar idchar { idchar }   /* if char 0 is '\'' and length exceeds 3, then it is an identifier */
        .
  digits = digit {['_'] digit}.
  hexdigits = "0x" hexdigit {['_'] hexdigit}.
  decimaldigits = digit {['_'] digit} '.' digit {['_'] digit}.
  // NOTE: all alphabetic strings used in the grammar become reserved words automatically
  // The reason to include a definition here is so that a token can be referred to by its 'kind',
  // as in la.kind == _bool
  arrayToken = "array" [('1' digit | posDigitFrom2 ) {digit}] ['?'].
  bvToken = "bv" ( '0' | posDigit {digit} ).
  bool = "bool".
  char = "char".
  int = "int".
  nat = "nat".
  real = "real".
  ORDINAL = "ORDINAL".
  object = "object". // Keeping object and object? as literals simplifies ident
  object_q = "object?".
  string = "string".
  set = "set".
  iset = "iset".
  multiset = "multiset".
  seq = "seq".
  map = "map".
  imap = "imap".
  charToken =
      "'"
      ( charChar
        // Coco parses at the level of C# chars, meaning UTF-16 code units.
        // (See https://github.com/SSW-CocoR/CocoR-CSharp/issues/4)
        // We're working around that by inserting surrogate pairs
        // as needed after reading the UTF-8 bytes.
        // But that means character literals can appear to have more than one "character"
        // in them. Normally we would just allow more than one charChar here and reject
        // invalid sequences of them later, but because we allow single quotes in the
        // "ident" rule above, that would then overlap with identifiers.
        // Instead we only allow a surrogate pair here.
        | highSurrogate lowSurrogate
        | "\\\'" | "\\\"" | "\\\\" | "\\0" | "\\n" | "\\r" | "\\t"
        | "\\u" hexdigit hexdigit hexdigit hexdigit
        | "\\U{" hexdigit { ['_'] hexdigit } "}"
      )
      "'".
  stringToken =
      '"'
      { stringChar
        | "\\\'" | "\\\"" | "\\\\" | "\\0" | "\\n" | "\\r" | "\\t"
        | "\\u" hexdigit hexdigit hexdigit hexdigit
        | "\\U{" hexdigit { ['_'] hexdigit } "}"
      }
      '"'
    | '@' '"' { verbatimStringChar | "\"\"" } '"'.
  colon = ':'.
  comma = ','.
  verticalbar = '|'.
  doublecolon = "::".
  gets = ":=".
  boredSmiley = ":|".
  dot = '.'.
  backtick = "`".
  semicolon = ';'.
  darrow = "=>".
  assume = "assume".
  assert = "assert".
  calc = "calc".
  case = "case".
  then = "then".
  else = "else".
  as = "as".
  is = "is".
  by = "by".
  in = "in".
  decreases = "decreases".
  nonincreases = "nonincreases".
  invariant = "invariant".
  function = "function".
  predicate = "predicate".
  least = "least". /* not a keyword by itself, but part of a keyword phrase */
  greatest = "greatest". /* not a keyword by itself, but part of a keyword phrase */
  opaque = "opaque".
  inductive = "inductive".
  twostate = "twostate".
  copredicate = "copredicate".
  lemma = "lemma".
  static = "static".
  import = "import".
  export = "export".
  class = "class".
  trait = "trait".
  datatype = "datatype".
  codatatype = "codatatype".
  var = "var".
  const = "const".
  newtype = "newtype".
  type = "type".
  iterator = "iterator".
  method = "method".
  colemma = "colemma".
  constructor = "constructor".
  modifies = "modifies".
  reads = "reads".
  requires = "requires".
  ensures = "ensures".
  ghost = "ghost".
  provides = "provides".
  reveals = "reveals".
  extends = "extends".
  new = "new".
  nameonly = "nameonly".
  older = "older". /* a keyword only when it appears as a modifier to a parameter of a non-extreme function */
  witness = "witness".
  lbracecolon = "{:".
  lbrace = '{'.
  rbrace = '}'.
  lbracket = '['.
  rbracket = ']'.
  openparen = '('.
  closeparen = ')'.
  openAngleBracket = '<'.
  closeAngleBracket = '>'.
  singleeq = "=".
  eq = "==".
  neq = "!=".
  star = '*'.
  at = '@'.
  notIn = "!in" CONTEXT (nonidchar).
  ellipsis = "...".
  reveal = "reveal".
  hide = "hide".
  expect = "expect".
  sarrow = "->".
  qarrow = "~>".
  larrow = "-->".
  minus = "-".
COMMENTS FROM "/*" TO "*/" NESTED
COMMENTS FROM "//" TO lf
IGNORE cr + lf + tab
/*------------------------------------------------------------------------*/
PRODUCTIONS
Dafny
= (. Token includeStartToken;
     Token fileStartToken = t;
     Attributes attrs = null;
  .)
  { "include"                 (. includeStartToken = t; .)
    stringToken               (. {
                                 Uri parsedFile = scanner.Uri;
                                 bool isVerbatimString;
                                 string includedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
                                 Util.ValidateEscaping(theOptions, t, includedFile, isVerbatimString, errors);
                                 includedFile = Util.RemoveEscaping(theOptions, includedFile, isVerbatimString);
                                 if (!Path.IsPathRooted(includedFile)) {
                                   string basePath = parsedFile.Scheme == "stdin" ? "" : Path.GetDirectoryName(parsedFile.LocalPath);
                                   includedFile = Path.Combine(basePath, includedFile);
                                 }
                                 var oneInclude = new Include(t, parsedFile, new Uri(Path.GetFullPath(includedFile)), theOptions);
                                 oneInclude.Origin = new SourceOrigin(includeStartToken, t);
                                 theModule.Includes.Add(oneInclude);
                               }
                            .)
  }
  { AtAttributes<ref attrs>
    TopDecl<theModule, /* isTopLevel */ true, ref attrs> }
  (. CheckNoAttributes(ref attrs); .)
  (. 
    theModule.Origin = new SourceOrigin(fileStartToken.Next, t);
  .)
  SYNC
  EOF
  .

/*------------------------------------------------------------------------*/
DeclModifier<ref DeclModifierData dmod>
= ( "abstract"                             (. dmod.IsAbstract = true;  CheckAndSetToken(ref dmod.AbstractToken); CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  | "replaceable"                          (. dmod.IsReplaceable = true;  CheckAndSetToken(ref dmod.ReplaceableToken); CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  | "ghost"                                (. dmod.IsGhost = true;  CheckAndSetToken(ref dmod.GhostToken); CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  | "static"                               (. dmod.IsStatic = true; CheckAndSetToken(ref dmod.StaticToken); CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  | "opaque"                               (. dmod.IsOpaque = true; CheckAndSetToken(ref dmod.OpaqueToken); CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  )
  .

/*------------------------------------------------------------------------*/
TopDecl<ModuleDefinition module, bool isTopLevel, ref Attributes attrs>
= (. DeclModifierData dmod = new DeclModifierData() {
       Attributes = Consume(ref attrs)
     };
     ModuleDecl submodule;
     DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter;
     TraitDecl/*!*/ trait;
  .)
  { DeclModifier<ref dmod> }
  ( SubModuleDecl<dmod, module, out submodule, isTopLevel>
                                               (. var litmod = submodule as LiteralModuleDecl;
                                                  if (litmod != null && litmod.ModuleDef.PrefixIds.Count != 0) {
                                                    var prefixModule = new PrefixNameModule(theOptions, litmod.ModuleDef.PrefixIds, litmod);
                                                    module.PrefixNamedModules.Add(prefixModule);
                                                  } else {
                                                    if (submodule != null) {
                                                      module.SourceDecls.Add(submodule);
                                                    }
                                                  }
                                               .)
  | ClassDecl<dmod, module, out td>            (. if (td != null) {
                                                    module.SourceDecls.Add(td);
                                                  } 
                                               .)
  | DatatypeDecl<dmod, module, out dt>         (. if (dt != null) {
                                                    module.SourceDecls.Add(dt);
                                                  } 
                                               .)
  | NewtypeDecl<dmod, module, out td>          (. if (td != null) {
                                                    module.SourceDecls.Add(td);
                                                  } 
                                               .)
  | SynonymTypeDecl<dmod, module, out td>      (. if (td != null) {
                                                    module.SourceDecls.Add(td);
                                                  } 
                                               .)
  | IteratorDecl<dmod, module, out iter>       (. if (iter != null) {
                                                    module.SourceDecls.Add(iter);
                                                  } 
                                               .)
  | TraitDecl<dmod, module, out trait>         (. if (trait != null) {
                                                    module.SourceDecls.Add(trait);
                                                  } 
                                               .)
  | ClassMemberDecl<dmod, module.DefaultClass.Members, false, true> (. module.DefaultClass.SetMembersBeforeResolution(); .)
  )
  .

/*------------------------------------------------------------------------*/
SubModuleDecl<DeclModifierData dmod, ModuleDefinition parent, out ModuleDecl submodule, bool isTopLevel>
=
  (. submodule = null; .)
  ( ModuleDefinition<dmod, parent, out submodule>
  | ModuleImport<parent, out submodule>
  | ModuleExport<parent, out submodule>
    (. if (isTopLevel) SemErr(ErrorId.p_superfluous_export, submodule.Origin,
          "There is no point to an export declaration at the top level");
    .)
  )
  .

/*------------------------------------------------------------------------*/
ModuleDefinition<DeclModifierData dmod, ModuleDefinition parent, out ModuleDecl submodule>
=  "module"
   (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes);
     Attributes attrs = null;
     Token/*!*/ iderr;
     var prefixIds = new List<IOrigin>();
     List<Name> idRefined = null;
     ModuleDefinition module;
     ImplementationKind implementationKind = ImplementationKind.Refinement;
     submodule = null; // appease compiler
     var dafnyOptionsBackup = theOptions;
     CheckDeclModifiers(ref dmod, "module", AllowedDeclModifiers.Abstract | AllowedDeclModifiers.Replaceable);
   .)
   { Attribute<ref dmod.Attributes> }            (. ApplyOptionsFromAttributes(dmod.Attributes); .)
   ModuleQualifiedName<out var names>
   (. var name = names[^1];
      prefixIds = names.GetRange(0,names.Count-1).Select(n => (IOrigin)n.StartToken).ToList();
   .)

   [ "refines" ModuleQualifiedName<out idRefined> (. implementationKind = ImplementationKind.Refinement; .)
   | "replaces" ModuleQualifiedName<out idRefined> (. implementationKind = ImplementationKind.Replacement; .)
   | Ident<out iderr> (. SemErr(ErrorId.p_bad_module_decl, t, $"expected either a '{{' or a 'refines' keyword here, found {iderr.val}"); .)
   ]
   (. module = new ModuleDefinition(SourceOrigin.NoToken, name, prefixIds, GetModuleKind(dmod), false,
                                        idRefined == null ? null : new Implements(implementationKind, new ModuleQualifiedId(idRefined)), parent, dmod.Attributes);
   .)
   SYNC
   "{"                                 (. module.BodyStartTok = t; .)
     { AtAttributes<ref attrs>
       TopDecl<module, /* isTopLevel */ false, ref attrs>}
   "}"
   (. CheckNoAttributes(ref attrs); .)
    (.
      module.Origin = new SourceOrigin(dmod.FirstToken, t);
      submodule = new LiteralModuleDecl(theOptions, module, parent, Guid.NewGuid());
      submodule.Origin = module.Origin;
      theOptions = dafnyOptionsBackup;
    .)
  .

/*------------------------------------------------------------------------*/
ModuleImport<ModuleDefinition parent, out ModuleDecl submodule>
=  "import"                          (.bool opened = false;
                                      var idExports = new List<IOrigin>();
                                      var startToken = t;
                                      submodule = null;
                                     .)
    ["opened"                        (. opened = true; .) ]
    (. int lookAhead = scanner.Peek().kind; .)
    ( IF(lookAhead == _colon)
      ModuleName<out var name>
      ":"
      QualifiedModuleExport<out var namePath, out idExports>
          (. submodule = new AbstractModuleDecl(theOptions, new SourceOrigin(startToken, t), new ModuleQualifiedId(namePath),
                                               name, parent, opened, idExports, Guid.NewGuid()); .)
    | IF(lookAhead == _singleeq)
      ModuleName<out var name>
      "="
      QualifiedModuleExport<out var namePath, out idExports>
          (. submodule = new AliasModuleDecl(theOptions, new SourceOrigin(startToken, t), new ModuleQualifiedId(namePath),
                                               name, parent, opened, idExports, Guid.NewGuid()); .)
    |
      QualifiedModuleExport<out var namePath, out idExports>
          (. submodule = new AliasModuleDecl(theOptions, new SourceOrigin(startToken, t), new ModuleQualifiedId(namePath),
                                         new Name(SourceOrigin.NoToken, namePath[^1].Value), parent, opened, idExports, Guid.NewGuid());
          .)
    )
    .

/*------------------------------------------------------------------------*/
ExportId<out Token id> = NoUSIdentOrDigits<out id> .

ModuleExport<ModuleDefinition parent, out ModuleDecl submodule>
= "export"
  (.
    Token exportId = t;
    Token/*!*/ id;
    List<ExportSignature> exports = new List<ExportSignature>();;
    var extends = new List<IOrigin>();
    var startToken = t;
    bool provideAll = false;
    bool revealAll = false;
    bool isDefault = false;
    bool isRefining = false;
    ExportSignature exsig;
    Token laa;
  .)
  [ IF(IsIdentifier(la.kind) || la.kind==_digits) ExportId<out exportId>   // If the next token is 'least' or 'greatest' use it as the export id, 
                                                                           // not the beginning of a subsequent extreme predicate declaration
    (. if (exportId.line == la.line && (exportId.val == "least" || exportId.val == "greatest") && la.kind == _predicate)
           errors.Warning(ErrorId.p_misplaced_least_or_greatest, exportId, 
               $"the {exportId.val} token is the identifier for the export set, not an adjective for an extreme predicate");
    .)
  ]
  [ ellipsis  (. isRefining = true; .) ]
  {
  (  "provides"
    (
      ( ExportSignature<true, out exsig>        (. exports.Add(exsig); .)
        (. scanner.ResetPeek(); laa = scanner.Peek(); .)
        { 
          IF( la.kind == _comma && laa.kind != _provides && laa.kind != _reveals && laa.kind != _extends )
          "," 
          ExportSignature<true, out exsig>  (. exports.Add(exsig); .)
        (. scanner.ResetPeek(); laa = scanner.Peek(); .)
        }
      )
    | "*" (. provideAll = true; .)
    )
  | "reveals"
    (
      ExportSignature<false, out exsig>       (. exports.Add(exsig); .)
      (. scanner.ResetPeek(); laa = scanner.Peek(); .)
      { IF( la.kind == _comma && laa.kind != _provides && laa.kind != _reveals && laa.kind != _extends )
        "," 
        ExportSignature<false, out exsig>  (. exports.Add(exsig); .)
        (. scanner.ResetPeek(); laa = scanner.Peek(); .)
      }
    | "*" (. revealAll = true; .)
    )
  | "extends"
    ExportId<out id>       (. extends.Add(id); .)
    (. scanner.ResetPeek(); laa = scanner.Peek(); .)
    { IF( la.kind == _comma && laa.kind != _provides && laa.kind != _reveals && laa.kind != _extends )
      "," 
      ExportId<out id>  (. extends.Add(id); .) 
      (. scanner.ResetPeek(); laa = scanner.Peek(); .)
    }
  )
  [ "," (. SemErr(ErrorId.p_extraneous_comma_in_export, t, "no comma is allowed between provides and reveals and extends clauses in an export declaration"); .) ]
  }
  (. 
     Name name;
     if (exportId.val == "export" || exportId.val == parent.Name) {
       isDefault = true;
       name = new Name(exportId, parent.Name);
     } else {
       name = new Name(exportId);
     }
     submodule = new ModuleExportDecl(theOptions, new SourceOrigin(startToken, t), name, parent, exports, extends, provideAll, revealAll, isDefault, isRefining, Guid.NewGuid());
  .)
  // LL Warning: 'least' and 'greatest' are not keywords. They can be the identifier that is the last ExportId of the ModuleExport.
  // Those words can also be the first word of a declaration (e.g. least predicate) that follows the MOduleExport.
  // Hence the LL warning. The ambiguity always resolve in favor of the identifier being the last ExportID because it
  // follows a comma and satisfies the semantic IF predicate. But I have not found a non-intrusive way of shutting up the warning.
  .

/*------------------------------------------------------------------------*/
// Note - before the "." only Type names are permitted (no 'digits'), but name resolution sorts that
// out, since the parser does not know (without adding lookahead) when it has seen the last dot
// matching any permitted member name

ExportSignature<bool opaque, out ExportSignature exsig>
= (. Token prefix; Token suffix = null; Token startToken = null;
  .)
  TypeNameOrCtorSuffix<out prefix> (. startToken = t; .)
  [ "."
    TypeNameOrCtorSuffix<out suffix>]
  (. if (suffix != null) {
       exsig = new ExportSignature(prefix, prefix.val, suffix, suffix.val, opaque);
     } else {
       exsig = new ExportSignature(prefix, prefix.val, opaque);
     }
     exsig.Origin = new SourceOrigin(startToken, t);
  .)
  .

/*------------------------------------------------------------------------*/
ModuleName<out Name name> = Name<out name> .

ModuleQualifiedName<.out List<Name> names.>
= (. names = new List<Name>();
     Name name;
  .)
  ModuleName<out name> (. names.Add(name); .)
  { "." ModuleName<out name> (. names.Add(name); .) }
  .

/*------------------------------------------------------------------------*/
QualifiedModuleExport<.out List<Name> namePath, out List<IOrigin> exports.>
= (. exports = new List<IOrigin>();
  .)
  ModuleQualifiedName<out namePath>
  [ "`" ModuleExportSuffix<exports> ]
  .

/*------------------------------------------------------------------------*/
ModuleExportSuffix<. List<IOrigin> exports.>
=                              (. Token id; .)
    ( ExportId<out id>       (. exports.Add(id); .)
    | "{" ExportId<out id>   (. exports.Add(id); .)
       { "," ExportId<out id> (. exports.Add(id); .) }
      "}"
    )
  .

/*------------------------------------------------------------------------*/
ClassName<out Name name> = Name<out name> .

ClassDecl<DeclModifierData dmodClass, ModuleDefinition/*!*/ module, out TopLevelDecl/*!*/ c>
= (. Contract.Requires(module != null);
     Contract.Ensures(Contract.ValueAtReturn(out c) != null);
     Name/*!*/ name;
     List<Type> parentTraits = new List<Type>();
     bool isRefining = false;
     List<TypeParameter> typeArgs = new List<TypeParameter>();
     List<MemberDecl> members = new List<MemberDecl>();
     Token bodyStart;
     CheckDeclModifiers(ref dmodClass, "class", AllowedDeclModifiers.None);
     DeclModifierData dmod;
  .)
  SYNC
  "class" (. CheckAndSetTokenOnce(ref dmodClass.FirstTokenExceptAttributes); .)
  { Attribute<ref dmodClass.Attributes> }
  ClassName<out name>
  [ GenericParameters<typeArgs, true> ]
  [ ExtendsClause<parentTraits, null>
  | ellipsis                    (. isRefining = true; .)
  ]
  SYNC
  "{"                                            (. bodyStart = t;  .)
    {
      (. dmod = new DeclModifierData(); .)
      AtAttributes<ref dmod.Attributes>
      { DeclModifier<ref dmod> }
      ClassMemberDecl<dmod, members, true, false>
    }
  "}"
  (. c = new ClassDecl(new SourceOrigin(dmodClass.FirstToken, t), name, module, typeArgs, members, dmodClass.Attributes, isRefining, parentTraits);
     c.BodyStartTok = bodyStart;
  .)
  .

ExtendsClause<. List<Type> parentTraits, string requiresNonReferenceTraitsFor .>
= (. Type parentTrait;
  .)
  "extends"
    (. if (requiresNonReferenceTraitsFor == "newtype" && theOptions.Get(CommonOptionBag.GeneralTraits) != CommonOptionBag.GeneralTraitsOptions.Full) {
       SemErr(ErrorId.p_general_traits_full, t,
         $"{requiresNonReferenceTraitsFor} extending traits is not fully supported (specifically, compilation of such types is not supported); " +
         "to use them for verification only, use --general-traits=full");
     } else if (requiresNonReferenceTraitsFor != null && theOptions.Get(CommonOptionBag.GeneralTraits) == CommonOptionBag.GeneralTraitsOptions.Legacy) {
       SemErr(ErrorId.p_general_traits_datatype, t,
         $"{requiresNonReferenceTraitsFor} extending traits is not yet enabled by default; use --general-traits=datatype to enable it");
     }
    .)
  Type<out parentTrait>       (. parentTraits.Add(parentTrait); .)
  {"," Type<out parentTrait>  (. parentTraits.Add(parentTrait); .) }
  .

/*------------------------------------------------------------------------*/
TraitDecl<DeclModifierData dmodIn, ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait>
= (. Contract.Requires(module != null);
     Contract.Ensures(Contract.ValueAtReturn(out trait) != null);
     CheckDeclModifiers(ref dmodIn, "trait", AllowedDeclModifiers.None);
     List<Type> parentTraits = new List<Type>();
     bool isRefining = false;
     List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>(); //traits should not support type parameters at the moment
     List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
     Token bodyStart;
     DeclModifierData dmod;
  .)
  SYNC
  "trait"                       (. CheckAndSetTokenOnce(ref dmodIn.FirstTokenExceptAttributes); .)
  { Attribute<ref dmodIn.Attributes> }
  ClassName<out var name>
  [ GenericParameters<typeArgs, true> ]
  [ ExtendsClause<parentTraits, null>
  | ellipsis                    (. isRefining = true; .)
  ]
  "{"                                            (. bodyStart = t; .)
    { (. dmod = new DeclModifierData(); .)
      AtAttributes<ref dmod.Attributes>
      { DeclModifier<ref dmod> }
      ClassMemberDecl<dmod, members, true, false>
    }
  "}"
  (. trait = new TraitDecl(new SourceOrigin(dmodIn.FirstToken, t), name, module, typeArgs, members, dmodIn.Attributes, isRefining, parentTraits);
     trait.BodyStartTok = bodyStart;
    .)
  .

/*------------------------------------------------------------------------*/
ClassMemberDecl<. DeclModifierData dmod, List<MemberDecl> mm, bool allowConstructors, bool moduleLevelDecl.>
= (. Contract.Requires(cce.NonNullElements(mm));
     Method/*!*/ m;
     Function/*!*/ f;
  .)
  ( (. if (moduleLevelDecl) {
         SemErr(ErrorId.p_top_level_field, la, "fields are not allowed to be declared at the module level; instead, wrap the field in a 'class' declaration");
         dmod.IsStatic = false;
       }
    .)
    FieldDecl<dmod, mm> /* The !isModuleLevelDecl is just to prevent cascading errors */
  | ConstantFieldDecl<dmod, mm, moduleLevelDecl>
  | IF(IsFunctionDecl())
    (. if (moduleLevelDecl && dmod.StaticToken != null) {
         errors.Warning(ErrorId.p_module_level_function_always_static, dmod.StaticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here");
         dmod.IsStatic = false;
       }
    .)
    FunctionDecl<dmod, out f>                   (. mm.Add(f); .)
  | (. if (moduleLevelDecl && dmod.StaticToken != null) {
         errors.Warning(ErrorId.p_module_level_method_always_static, dmod.StaticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here");
         dmod.IsStatic = false;
       }
    .)
    MethodDecl<dmod, allowConstructors, out m>  (. mm.Add(m); .)
  )
  .

/*------------------------------------------------------------------------*/
DatatypeName<out Name name> = Name<out name> .

DatatypeDecl<DeclModifierData dmod, ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt>
= (. Contract.Requires(module != null);
     Contract.Ensures(Contract.ValueAtReturn(out dt)!=null);
     Name/*!*/ name;
     List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
     List<Type> parentTraits = new List<Type>();
     List<DatatypeCtor/*!*/> ctors = new List<DatatypeCtor/*!*/>();
     Token bodyStart = Token.NoToken;  // dummy assignment
     bool isRefining = false;
     bool co = false;
     CheckDeclModifiers(ref dmod, "datatype or codatatype", AllowedDeclModifiers.None);
     var members = new List<MemberDecl>();
  .)
  SYNC
  ( "datatype"
  | "codatatype"     (. co = true; .)
  )                                        (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  { Attribute<ref dmod.Attributes> }
  DatatypeName<out name>
  [ GenericParameters<typeArgs, true> ]
  [ ExtendsClause<parentTraits, co ? "codatatype" : "datatype"> ]
  (
  "="                                      (. bodyStart = t; .)
  [ ellipsis                               (. SemErr(ErrorId.p_bad_datatype_refinement, t, // Help users adjust to the new syntax
      $"in refining a datatype, the '...' replaces the '=' token and everything up to a left brace starting the declaration of the body; only members of the body may be changed in a datatype refinement"); .)
  ]
  [ "|" ] DatatypeMemberDecl<ctors>
  { "|" DatatypeMemberDecl<ctors> }
  | ellipsis                               (. isRefining = true; bodyStart = t; .)
  )
  [ TypeMembers<module, members> ]
  (. if (co) {
       dt = new CoDatatypeDecl(new SourceOrigin(dmod.FirstToken, t), name, module, typeArgs, ctors, parentTraits, members, dmod.Attributes, isRefining);
     } else {
       dt = new IndDatatypeDecl(new SourceOrigin(dmod.FirstToken, t), name, module, typeArgs, ctors, parentTraits, members, dmod.Attributes, isRefining);
     }
     dt.BodyStartTok = bodyStart;
  .)
  .

/*------------------------------------------------------------------------*/
DatatypeMemberName<out Token id> = NoUSIdentOrDigits<out id> .

DatatypeMemberDecl<.List<DatatypeCtor/*!*/>/*!*/ ctors.>
= (. Contract.Requires(cce.NonNullElements(ctors));
     Attributes attrs = null;
     Token/*!*/ id;
     List<Formal/*!*/> formals = new List<Formal/*!*/>();
     var isGhost = false;
  .)
  // Note, "ghost" is parsed before any attributes. This means that the
  // attributes are parsed before the "id", which is consistent with other
  // declarations.
  [ "ghost"   (. isGhost = true; .) ]
  { Attribute<ref attrs> }
  DatatypeMemberName<out id>
  [ FormalsOptionalIds<formals> ]
  (. var ctor = new DatatypeCtor(new SourceOrigin(id, t), new Name(id), isGhost, formals, attrs);
     ctors.Add(ctor); .)
  .

/*------------------------------------------------------------------------*/
TypeMembers<. ModuleDefinition/*!*/ module, List<MemberDecl> members .>
= (. DeclModifierData dmod;
  .)
  "{"
  { (. dmod = new DeclModifierData();
    .)
    AtAttributes<ref dmod.Attributes>
    { DeclModifier<ref dmod> }
    ClassMemberDecl<dmod, members, false, false>
  }
  "}"
  .

/*------------------------------------------------------------------------*/
FieldDecl<.DeclModifierData dmod, List<MemberDecl> mm.>
= (. Contract.Requires(cce.NonNullElements(mm));
     Attributes attrs = null;
     Type/*!*/ ty;
     Name name;
     CheckDeclModifiers(ref dmod, "field", AllowedDeclModifiers.Ghost);
     var startToken = dmod.FirstToken;
  .)
  SYNC
  "var"                                (. startToken = startToken ?? t; .)
  { Attribute<ref attrs> }
  FIdentType<out name, out ty>         (. var f = new Field(new SourceOrigin(startToken, t), name, dmod.IsGhost, ty, attrs);
                                          mm.Add(f);
                                       .)
  { ","                                (. startToken = t; .)
    FIdentType<out name, out ty>       (. f = new Field(new SourceOrigin(startToken, t), name, dmod.IsGhost, ty, attrs);
                                          mm.Add(f);
                                       .)
  }
  OldSemi
  .

/*------------------------------------------------------------------------*/
ConstantFieldDecl<.DeclModifierData dmod, List<MemberDecl/*!*/>/*!*/ mm, bool moduleLevelDecl.>
= (. Contract.Requires(cce.NonNullElements(mm));
     Type/*!*/ ty;
     Expression e = null;
     if (moduleLevelDecl && dmod.StaticToken != null) {
       errors.Warning(ErrorId.p_module_level_const_always_static, dmod.StaticToken, "module-level const declarations are always non-instance, so the 'static' keyword is not allowed here");
       dmod.IsStatic = false;
     }
     CheckDeclModifiers(ref dmod, "field", AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Opaque);
  .)
  SYNC
  "const"                                    (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  { Attribute<ref dmod.Attributes> }
  ( IF(!IsIdentifier(la.kind) && la.kind != _digits)
    (. SemErr(ErrorId.p_const_decl_missing_identifier, la, "expected an identifier after 'const' and any attributes"); .)
  |
    CIdentType<out var name, out ty>         (. if (ty == null) { ty = new InferredTypeProxy(); } .)
    [ ellipsis ]
    [ ( ":=" | "=" (. SemErr(ErrorId.p_bad_const_initialize_op, t, "a const field should be initialized using ':=', not '='"); .) )
    Expression<out e, false, true> ]
                                             (. if (e == null && ty is InferredTypeProxy) {
                                                  SemErr(ErrorId.p_const_is_missing_type_or_init, name.StartToken, "a const declaration must have a type or a RHS value");
                                                }
                                                var c = new ConstantField(new SourceOrigin(dmod.FirstToken, t), name, e, dmod.IsStatic, dmod.IsGhost, dmod.IsOpaque, ty, dmod.Attributes);
                                                mm.Add(c);
                                             .)
    OldSemi
  )
  .

/*------------------------------------------------------------------------*/
NewtypeName<out Name name> = Name<out name> .
LocalVarName<out Token id> = NoUSIdent<out id> .
  
NewtypeDecl<DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td>
= (. Name name;
     Token bvId;
     td = null;
     Type baseType = null;
     Expression constraint;
     Expression witness = null;
     CheckDeclModifiers(ref dmod, "newtype", AllowedDeclModifiers.None);
     List<Type> parentTraits = new List<Type>();
     var members = new List<MemberDecl>();
     var typeParameters = new List<TypeParameter>();
  .)
  "newtype" (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  { Attribute<ref dmod.Attributes> }
  NewtypeName<out name>
  [ GenericParameters<typeParameters, true> ]
  [ ExtendsClause<parentTraits, "newtype"> ]
  (
  "="
  [ ellipsis                               (. SemErr(ErrorId.p_misplaced_ellipsis_in_newtype, t, // Help users adjust to the new syntax
      $"in refining a newtype, the '...' replaces the '=' token and everything up to the left brace starting the declaration of the newtype body (if any); a newtype refinement may not change the base type of the newtype"); .)
  ]
  ( IF(IsIdentColonOrBar())
    LocalVarName<out bvId>
    [ ":" Type<out baseType> ]
    "|"
    Expression<out constraint, false, true>
    WitnessClause<out var witnessKind, out witness>
    [ TypeMembers<module, members> ]
    (. td = new NewtypeDecl(new SourceOrigin(dmod.FirstToken, t), name, typeParameters, module,
         new BoundVar(bvId, bvId.val, baseType),
         constraint, witnessKind, witness, parentTraits, members, dmod.Attributes, isRefining: false);
    .)
  | Type<out baseType>
    WitnessClause<out var witnessKind, out witness>
    [ TypeMembers<module, members> ]
    (. td = new NewtypeDecl(new SourceOrigin(dmod.FirstToken, t), name, typeParameters, module,
         baseType, witnessKind, witness, parentTraits, members, dmod.Attributes, isRefining: false);
    .)
  )
  | ellipsis
    [ TypeMembers<module, members> ]
    (. baseType = null; // Base type is not known yet
       td = new NewtypeDecl(new SourceOrigin(dmod.FirstToken, t), name, typeParameters, module, baseType,
         SubsetTypeDecl.WKind.CompiledZero, null,
         parentTraits, members, dmod.Attributes, isRefining: true);
    .)
  )
  .

/*------------------------------------------------------------------------*/
SynonymTypeName<out Name name> = Name<out name> .

// The following includes Opaque type definitions
SynonymTypeDecl<DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td>
= (. Token bvId;
     var characteristics = new TypeParameter.TypeParameterCharacteristics(false);
     var typeArgs = new List<TypeParameter>();
     td = null;
     Type ty = null;
     Expression constraint;
     Expression witness = null;
     var kind = "abstract type";
     List<Type> parentTraits = new List<Type>();
     var members = new List<MemberDecl>();
     var isRefining = false;
  .)
  "type"                      (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  { Attribute<ref dmod.Attributes> }
  SynonymTypeName<out var name>
  { TypeParameterCharacteristics<ref characteristics> }
  [ GenericParameters<typeArgs, true> ]
  [ "="
    ( IF(IsIdentColonOrBar())
      LocalVarName<out bvId>
      [ ":" Type<out ty> ]
      "|"
      Expression<out constraint, false, true>
      WitnessClause<out var witnessKind, out witness>
      (. td = new SubsetTypeDecl(new SourceOrigin(dmod.FirstToken, t), name, characteristics, typeArgs, module, 
            new BoundVar(bvId, bvId.val, ty), constraint, witnessKind, witness, dmod.Attributes);
         kind = "subset type";
      .)
    |
      Type<out ty>
      (. td = new TypeSynonymDecl(new SourceOrigin(dmod.FirstToken, t), name, characteristics, typeArgs, module, ty, dmod.Attributes);
         kind = "type synonym";
      .)
    )
  | ellipsis                               (. isRefining = true; .)
    [ TypeMembers<module, members> ]
  | ExtendsClause<parentTraits, "abstract type">
    [ TypeMembers<module, members> ]
  | TypeMembers<module, members>
  ]
  (. if (td == null) {
       if (module is DefaultModuleDefinition or FileModuleDefinition) {
         // abstract type declarations at the very outermost program scope get an automatic (!new)
         characteristics.ContainsNoReferenceTypes = true;
       }
       td = new AbstractTypeDecl(new SourceOrigin(dmod.FirstToken, t), name, module, characteristics, typeArgs, parentTraits, members, dmod.Attributes, isRefining);
     }
  .)
  (. CheckDeclModifiers(ref dmod, kind, AllowedDeclModifiers.None); .)
  .

WitnessClause<out SubsetTypeDecl.WKind witnessKind, out Expression witness>
= (. witnessKind = SubsetTypeDecl.WKind.CompiledZero;
     witness = null;
  .)
  [ IF(IsWitness())
    ( "ghost" "witness"                            (. witnessKind = SubsetTypeDecl.WKind.Ghost; .)
      Expression<out witness, false, true>
    | "witness"
      ( "*"                                        (. witnessKind = SubsetTypeDecl.WKind.OptOut; .)
      | Expression<out witness, false, true>       (. witnessKind = SubsetTypeDecl.WKind.Compiled; .)
      )
    )
  ]
  .

/*------------------------------------------------------------------------*/
GIdentType<bool allowGhostKeyword, bool allowNewKeyword, bool allowNameOnlyKeyword, bool allowOlderKeyword,
           out SourceOrigin range, out Name/*!*/ name, out Type/*!*/ ty, out bool isGhost, out bool isOld, out bool isNameOnly, out bool isOlder>
/* isGhost always returns as false if allowGhostKeyword is false */
= (. Contract.Ensures(Contract.ValueAtReturn(out name)!=null);
     Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
     isGhost = false; isOld = allowNewKeyword; isNameOnly = false; isOlder = false;
     Token startToken = t.Next;
  .)
  { IF(IsKeywordForFormal())
    ( "ghost"                    (. if (allowGhostKeyword) { isGhost = true; } else { SemErr(ErrorId.p_output_of_function_not_ghost, t, "formal cannot be declared 'ghost' in this context"); } .)
    | "new"                      (. if (allowNewKeyword) { isOld = false; } else { SemErr(ErrorId.p_no_new_on_output_formals, t, "formal cannot be declared 'new' in this context"); } .)
    | "nameonly"                 (. if (allowNameOnlyKeyword) { isNameOnly = true; } else { SemErr(ErrorId.p_no_nameonly_on_output_formals, t, "formal cannot be declared 'nameonly' in this context"); } .)
    | "older"                    (. if (allowOlderKeyword) { isOlder = true; } else { SemErr(ErrorId.p_no_older_on_output_formals, t, "formal cannot be declared 'older' in this context"); } .)
    )
  }
  IdentType<out var id, out var originalId, out ty, false> 
                                (. range = new SourceOrigin(startToken, ty.EndToken);
                                   name = new Name(id);
                                .)
  .

FIdentType<out Name/*!*/ name, out Type/*!*/ ty>
= NoDigitName<out name> (. ty = null; .)
  ( ":" Type<out ty> 
  | (. SemErr(ErrorId.p_var_decl_must_have_type, t, "a mutable field must be declared with a type"); .)
  )
  [ ( ":=" | "=" ) (. var optoken = t; Expression e; .)
    Expression<out e, true, true> 
      (.              var erange = new SourceOrigin(optoken, e.EndToken);
                      SemErr(ErrorId.p_no_init_for_var_field, erange, "a mutable field may not have an initializer"); 
      .) 
  ]
  .

CIdentType<out Name/*!*/ name, out Type ty>
= (.Contract.Ensures(Contract.ValueAtReturn(out name) != null);
    ty = null;
  .)
  NoDigitName<out name>
  [ ":"
    Type<out ty>
  ]
  .

IdentType<out Token/*!*/ id, out Token originalId, out Type/*!*/ ty, bool allowWildcardId>
= (.Contract.Ensures(Contract.ValueAtReturn(out id) != null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null);.)
  WildIdent<out id, allowWildcardId> (. originalId = t; .)
  ":"
  Type<out ty>
  .

LocalIdentTypeOptional<out LocalVariable var, bool isGhost, bool allowWild=true>
= (. Token id;  Type ty = null;
     Token startToken = null;
  .)
  WildIdent<out id, allowWild> (. startToken = t; .)
  [ ":" Type<out ty>
  ]
  (. var = new LocalVariable(new SourceOrigin(startToken, t), id.val, ty, isGhost);
  .)
  .

IdentTypeOptional<out BoundVar var>
= (. Contract.Ensures(Contract.ValueAtReturn(out var) != null);
     Type ty = null;
  .)
  WildIdentN<out var name, true>
  [ IF( la.kind == _colon) ":" Type<out ty>
     // CoCo warns about this optional ':', because it is ambiguous with a ':' that follows the IdentTypeOptional.
     // An IdentTypeOptional can be the last thing in an Expression (a SetComprehension)
     // Also, a ':' can follow an Expression in a slices-by-length construct. So a ':' here might be the start
     // of the ': type' just above or it might be the ':' in the slices-by-length. As the expression can always tbe parenthesized to 
     // disambiguate, it makes sense to force the colon to consider the ': type' here. Hence the semantic predicate.
     // Also a SetComprehension would be the wrong type in a slices-by-length construct.
  ]
  (. var = new BoundVar(name.Tok, name.Value, ty) {
       Origin = new SourceOrigin(name.StartToken, t)
     }; .)
  .

TypeIdentOptional<out SourceOrigin/*!*/ range, out Name/*!*/ identName, out Type/*!*/ ty, 
  out bool isGhost, out Expression defaultValue, out bool isNameOnly, out Attributes attributes>
= (.Contract.Ensures(Contract.ValueAtReturn(out range)!=null);
     Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
     Contract.Ensures(Contract.ValueAtReturn(out identName)!=null);
     Token nameToken = null; ty = new BoolType()/*dummy*/; isGhost = false;
     Token nameonlyToken = null;
     attributes = null;
     identName = null;
     defaultValue = null;
  .)
  (. var beforeStartToken = t; .)
  { Attribute<ref attributes> }
  { "ghost"                            (. isGhost = true; .)
  | "nameonly"                         (. nameonlyToken = t; .)
  }
  ( TypeAndToken<out var firstToken, out ty, false>
    [ ":"
      (. /* try to convert ty to an identifier */
         UserDefinedType udt = ty as UserDefinedType;
         if (udt != null && udt.TypeArgs.Count == 0) {
           nameToken = firstToken;
         } else {
           SemErr(ErrorId.p_datatype_formal_is_not_id, firstToken, "invalid formal-parameter name in datatype constructor");
         }
      .)
      Type<out ty>
      ParameterDefaultValue<true, out defaultValue>
    ]
  | digits         (. nameToken = t; .)
    ":"
    Type<out ty>
    ParameterDefaultValue<true, out defaultValue>
  )
  (. range = new SourceOrigin(beforeStartToken.Next, t);
     if (nameToken != null) {
       identName = new Name(nameToken);
       isNameOnly = nameonlyToken != null;
     } else {
       identName = new Name(range, "#" + anonymousIds++);
       if (nameonlyToken != null) {
         SemErr(ErrorId.p_nameonly_must_have_parameter_name, nameonlyToken, "use of the 'nameonly' modifier must be accompanied with a parameter name");
       }
       isNameOnly = false;
     }
  .)
  .

/*------------------------------------------------------------------------*/
IteratorName<out Name name> = Name<out name> .

IteratorDecl<DeclModifierData dmod, ModuleDefinition module, out IteratorDecl/*!*/ iter>
= (. Contract.Ensures(Contract.ValueAtReturn(out iter) != null);
     Attributes attrs = null;
     List<TypeParameter/*!*/>/*!*/ typeArgs = new List<TypeParameter/*!*/>();
     List<Formal/*!*/> ins = new List<Formal/*!*/>();
     List<Formal/*!*/> outs = new List<Formal/*!*/>();
     List<FrameExpression/*!*/> reads = new List<FrameExpression/*!*/>();
     List<FrameExpression/*!*/> mod = new List<FrameExpression/*!*/>();
     List<Expression/*!*/> decreases = new List<Expression>();
     List<AttributedExpression/*!*/> req = new List<AttributedExpression/*!*/>();
     List<AttributedExpression/*!*/> ens = new List<AttributedExpression/*!*/>();
     List<AttributedExpression/*!*/> yieldReq = new List<AttributedExpression/*!*/>();
     List<AttributedExpression/*!*/> yieldEns = new List<AttributedExpression/*!*/>();
     List<Expression/*!*/> dec = new List<Expression/*!*/>();
     Attributes readsAttrs = null;
     Attributes modAttrs = null;
     Attributes decrAttrs = null;
     BlockStmt body = null;
     Token signatureEllipsis = null;
     Token bodyStart = Token.NoToken;
     Token bodyEnd = Token.NoToken;
     CheckDeclModifiers(ref dmod, "iterator", AllowedDeclModifiers.None);
  .)
  SYNC
  "iterator"                (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  { Attribute<ref dmod.Attributes> }    (. MergeInto(ref attrs, ref dmod.Attributes); .)
  IteratorName<out var name>
  (
    [ GenericParameters<typeArgs, true> ]
    Formals<true, true, false, false, ins>
    [ ( "yields"
      | "returns"           (. SemErr(ErrorId.p_should_be_yields_instead_of_returns, t, "iterators don't have a 'returns' clause; did you mean 'yields'?"); .)
      )
      Formals<false, true, false, false, outs>
    ]
  | ellipsis                (. signatureEllipsis = t; .)
  )
  IteratorSpec<reads, mod, decreases, req, ens, yieldReq, yieldEns, ref readsAttrs, ref modAttrs, ref decrAttrs>
  [ BlockStmt<out body, out bodyStart, out bodyEnd>
  ]
  (. iter = new IteratorDecl(new SourceOrigin(dmod.FirstToken, t), name, module, typeArgs, ins, outs,
                             new Specification<FrameExpression>(reads, readsAttrs),
                             new Specification<FrameExpression>(mod, modAttrs),
                             new Specification<Expression>(decreases, decrAttrs),
                             req, ens, yieldReq, yieldEns,
                             body, dmod.Attributes, signatureEllipsis);
     iter.BodyStartTok = bodyStart;
 .)
  .

/*------------------------------------------------------------------------*/
TypeVariableName<out Name name> = Name<out name> .

GenericParameters<.List<TypeParameter> typeParameters, bool allowVariance.>
= (. Contract.Requires(cce.NonNullElements(typeParameters)); .)
  // If a "<" combined with a Variance symbol could be a new token, then the parser here will need to be more complex,
  // since, for example, a < followed immediately by a Variance symbol would scan as the wrong token.
  // Fortunately that is not currently the case.
  // (Only because we parse the new "<-" symbol as separate "<" "-" tokens precisely to avoid this issue :)
  "<"
  OneTypeParameter<typeParameters, allowVariance>
  { ","
    OneTypeParameter<typeParameters, allowVariance>
  }
  ">"
  .

OneTypeParameter<.List<TypeParameter> typeParameters, bool allowVariance.>
= (. var variance = TypeParameter.TPVarianceSyntax.NonVariant_Strict; // assignment is to please compiler
     var characteristics = new TypeParameter.TypeParameterCharacteristics(false);
     Name name;
     Type typeBound;
     var typeBounds = new List<Type>();
     var startToken = t.Next;
  .)
  [ Variance<out variance>
    (. if (!allowVariance) {
         SemErr(ErrorId.p_type_parameter_variance_forbidden, t, "type-parameter variance is not allowed to be specified in this context");
       }
    .)
  ]
  TypeVariableName<out name>
  { TypeParameterCharacteristics<ref characteristics> }
  { "extends"
    Type<out typeBound>
    (. typeBounds.Add(typeBound); .)
  }
  (. typeParameters.Add(new TypeParameter(new SourceOrigin(startToken, t), name, variance, characteristics, typeBounds)); .)
  .

/*------------------------------------------------------------------------*/
Variance<out TypeParameter.TPVarianceSyntax variance>
= (. variance = TypeParameter.TPVarianceSyntax.NonVariant_Strict;  // never used; here just to please the C# compiler
  .)
  ( "*"  (. variance = TypeParameter.TPVarianceSyntax.Covariant_Permissive; .)
  | "+"  (. variance = TypeParameter.TPVarianceSyntax.Covariant_Strict; .)
  | "!"  (. variance = TypeParameter.TPVarianceSyntax.NonVariant_Permissive; .)
  | "-"  (. variance = TypeParameter.TPVarianceSyntax.Contravariance; .)
  )
  .

/*------------------------------------------------------------------------*/
TypeParameterCharacteristics<ref TypeParameter.TypeParameterCharacteristics characteristics>
= "(" (. var startToken = t; .)
  TPCharOption<ref characteristics>
  { ","
    TPCharOption<ref characteristics>
  }
  ")" (. characteristics.SourceOrigin = new SourceOrigin(startToken, t); .)
  .

TPCharOption<ref TypeParameter.TypeParameterCharacteristics characteristics>
= ( "=="       (. characteristics.EqualitySupport = TypeParameter.EqualitySupportValue.Required; .)
  | digits     (. if (t.val == "0") {
                    characteristics.AutoInit = Microsoft.Dafny.Type.AutoInitInfo.CompilableValue;
                  } else if (t.val == "00") {
                    if (characteristics.AutoInit != Microsoft.Dafny.Type.AutoInitInfo.CompilableValue) {
                      characteristics.AutoInit = Microsoft.Dafny.Type.AutoInitInfo.Nonempty;
                    }
                  } else {
                    SemErr(ErrorId.p_unexpected_type_characteristic, t, $"unexpected type characteristic: '{t.val}' should be one of == or 0 or 00 or !new");
                  }
               .)
  | "!" "new"  (. characteristics.ContainsNoReferenceTypes = true; .)
  | ANY        (. if (t.kind == _closeparen || t.kind == _comma) 
                      SemErr(ErrorId.p_missing_type_characteristic, t, $"extra comma or missing type characteristic: should be one of == or 0 or 00 or !new");
                  else SemErr(ErrorId.p_illegal_type_characteristic, t, $"illegal type characteristic: '{t.val}' should be one of == or 0 or 00 or !new");
                .)
  )
  .

/*------------------------------------------------------------------------*/
MethodDecl<DeclModifierData dmod, bool allowConstructor, out Method/*!*/ m>
= (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null);
     bool hasName = false;
     Name name = new Name("<noNameGiven>");  
     Token keywordToken;
     List<TypeParameter/*!*/>/*!*/ typeArgs = new List<TypeParameter/*!*/>();
     List<Formal/*!*/> ins = new List<Formal/*!*/>();
     List<Formal/*!*/> outs = new List<Formal/*!*/>();
     List<AttributedExpression/*!*/> req = new List<AttributedExpression/*!*/>();
     List<FrameExpression/*!*/> reads = new List<FrameExpression/*!*/>();
     List<FrameExpression/*!*/> mod = new List<FrameExpression/*!*/>();
     List<AttributedExpression/*!*/> ens = new List<AttributedExpression/*!*/>();
     List<Expression/*!*/> dec = new List<Expression/*!*/>();
     Attributes decAttrs = null;
     Attributes modAttrs = null;
     Attributes readsAttrs = null;
     BlockStmt body = null;
     bool isPlainOlMethod = false;
     bool isLemma = false;
     bool isTwoStateLemma = false;
     bool isConstructor = false;
     bool isLeastLemma = false;
     bool isGreatestLemma = false;
     Token signatureEllipsis = null;
     Token bodyStart = Token.NoToken;
     Token bodyEnd = Token.NoToken;
     AllowedDeclModifiers allowed = AllowedDeclModifiers.None;
     string caption = "";
     ExtremePredicate.KType kType = ExtremePredicate.KType.Unspecified;
  .)
  SYNC
  ( "method"                        (. isPlainOlMethod = true; caption = "method";
                                       CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes);
                                       allowed = AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static; .)
  | "lemma"                         (. isLemma = true; caption = "lemma";
                                       CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes);
                                       allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static; .)
  | ( "greatest"                    (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
      "lemma"
    | "colemma"                     (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); 
                                       errors.Deprecated(ErrorId.p_deprecated_colemma, t, "the old keyword 'colemma' has been renamed to the keyword phrase 'greatest lemma'"); 
                                    .)
    )
                                    (. isGreatestLemma = true; caption = "greatest lemma";
                                       allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static; .)
  | ( "least"                       (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
    | "inductive"                   (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes);
                                       errors.Deprecated(ErrorId.p_deprecated_inductive_lemma, t, "the old keyword phrase 'inductive lemma' has been renamed to 'least lemma'"); 
                                    .)
    )
    "lemma"
                                    (. isLeastLemma = true;  caption = "least lemma";
                                       allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static;.)
  | "twostate"                      (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
    "lemma"                         (. isTwoStateLemma = true; caption = "two-state lemma";
                                       allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static; .)
  | "constructor"                   (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes);
                                       if (allowConstructor) {
                                         isConstructor = true;
                                       } else {
                                         SemErr(ErrorId.p_constructor_not_in_class, t, "constructors are allowed only in classes");
                                       }
                                       caption = "constructor";
                                       allowed = AllowedDeclModifiers.Ghost;
                                    .)
  )                                 (. keywordToken = t;
                                       CheckDeclModifiers(ref dmod, caption, allowed); .)
  { Attribute<ref dmod.Attributes> }
  [ MethodFunctionName<out name>               (. hasName = true; .)
  ]
  (. if (!hasName) {
       if (!isConstructor) {
         SemErr(ErrorId.p_method_missing_name, la, "a method must be given a name (expecting identifier)");
       }
     }
  .)
  (
    [ GenericParameters<typeArgs, false> ]
    [ KType<ref kType, out Token openBracket, out Token closeBracket>              
                                    (. if (!(isGreatestLemma || isLeastLemma)) { 
                                         // Note: When ranges are refactored, make the first use of openBracket a range
                                         // that extends through closeBracket
                                         SemErr(ErrorId.p_extraneous_k, 
                                                new SourceOrigin(openBracket, closeBracket), 
                                                "type of _k can only be specified for least and greatest lemmas"); 
                                       } 
                                    .)
    ]
    (. var isCompilable = (isPlainOlMethod || isConstructor) && !dmod.IsGhost; .)
    Formals<true, isCompilable, isTwoStateLemma, false, ins>
    [ "returns"                                 (. var returnsToken = t; .)
      Formals<false, isCompilable, false, false, outs>
      (. if (isConstructor) { SemErr(ErrorId.p_constructors_have_no_out_parameters, new SourceOrigin(returnsToken, t), "constructors cannot have out-parameters"); } .)
    ]
  | ellipsis                                    (. signatureEllipsis = t; .)
  )
  MethodSpec<dmod.IsGhost || isLemma || isTwoStateLemma || isLeastLemma || isGreatestLemma,
             req, reads, mod, ens, dec, ref decAttrs, ref modAttrs, ref readsAttrs, caption, isConstructor>
  [ IF(isConstructor)
    (. DividedBlockStmt dividedBody; .)
    DividedBlockStmt<out dividedBody, out bodyStart, out bodyEnd>
    (. body = dividedBody; .)
  | BlockStmt<out body, out bodyStart, out bodyEnd>
  ]
  (. var range = new SourceOrigin(dmod.FirstToken, t);
     if (isConstructor) {
       m = new Constructor(range, hasName ? name : new Name(dmod.FirstToken, "_ctor"), dmod.IsGhost, typeArgs, ins,
                           req, new Specification<FrameExpression>(reads, readsAttrs), 
                           new Specification<FrameExpression>(mod, modAttrs), ens, 
                           new Specification<Expression>(dec, decAttrs), 
                           (DividedBlockStmt)body, dmod.Attributes, signatureEllipsis);
     } else if (isLeastLemma) {
       m = new LeastLemma(range, name, dmod.IsStatic, kType, typeArgs, ins, outs,
                          req, new Specification<FrameExpression>(reads, readsAttrs), new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, dmod.Attributes, signatureEllipsis);
     } else if (isGreatestLemma) {
       m = new GreatestLemma(range, name, dmod.IsStatic, kType, typeArgs, ins, outs,
                             req, new Specification<FrameExpression>(reads, readsAttrs), new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, dmod.Attributes, signatureEllipsis);
     } else if (isLemma) {
       m = new Lemma(range, name, dmod.IsStatic, typeArgs, ins, outs,
                     req, new Specification<FrameExpression>(reads, readsAttrs), new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, dmod.Attributes, signatureEllipsis);
     } else if (isTwoStateLemma) {
       m = new TwoStateLemma(range, name, dmod.IsStatic, typeArgs, ins, outs,
                             req, new Specification<FrameExpression>(reads, readsAttrs), new Specification<FrameExpression>(mod, modAttrs),
                             ens, new Specification<Expression>(dec, decAttrs), body, dmod.Attributes, signatureEllipsis);
     } else {
       m = new Method(range, name, dmod.IsStatic, dmod.IsGhost, typeArgs, ins, outs,
                      req, new Specification<FrameExpression>(reads, readsAttrs), 
                      new Specification<FrameExpression>(mod, modAttrs), ens, 
                      new Specification<Expression>(dec, decAttrs), body, dmod.Attributes, signatureEllipsis);
     }
     m.BodyStartTok = bodyStart;
 .)
  .

/*------------------------------------------------------------------------*/
KType<ref ExtremePredicate.KType kType, out Token openBracket, out Token closeBracket>
= "["           (. openBracket = t; .)
  ( "nat"       (. kType = ExtremePredicate.KType.Nat; .)
  | "ORDINAL"   (. kType = ExtremePredicate.KType.ORDINAL; .)
  )
  "]"           (. closeBracket = t; .)
  .

/*------------------------------------------------------------------------*/
RequiresClause<.List<AttributedExpression> req, bool allowLabel.>
= "requires"    (. Token lbl = null;
                   Token first = t;
                   Attributes attrs = null;
                   Expression e;
                 .)
  { Attribute<ref attrs> }
  [ IF(IsLabel(allowLabel))
    LabelName<out lbl> ":"
  ]
  Expression<out e, false, false>
  OldSemi       (. req.Add(new AttributedExpression(e, lbl == null ? null : new AssertLabel(lbl, lbl.val), attrs)); .)
  .

/*------------------------------------------------------------------------*/
EnsuresClause<.List<AttributedExpression> ens, bool allowLambda.>
= "ensures"     (. Expression e;
                   Attributes attrs = null;
                 .)
  { Attribute<ref attrs> }
  Expression<out e, false, allowLambda>
    OldSemi       (. ens.Add(new AttributedExpression(e, attrs)); .)
  .

/*------------------------------------------------------------------------*/
ModifiesClause<.ref List<FrameExpression> mod, ref Attributes attrs,
                bool allowLambda, bool performThisDeprecatedCheck.>
= "modifies"                                    (. FrameExpression fe;
                                                   mod = mod ?? new List<FrameExpression>();
                                                .)
  { Attribute<ref attrs> }
  FrameExpression<out fe, false, allowLambda>         (. Util.AddFrameExpression(mod, fe, performThisDeprecatedCheck, errors); .)
  { "," FrameExpression<out fe, false, allowLambda>   (. Util.AddFrameExpression(mod, fe, performThisDeprecatedCheck, errors); .)
  }
  OldSemi
  .

/*------------------------------------------------------------------------*/
DecreasesClause<.List<Expression> decreases, ref Attributes attrs,
                 bool allowWildcard, bool allowLambda.>
= "decreases"
  { Attribute<ref attrs> }
  DecreasesList<decreases, allowWildcard, allowLambda>
  OldSemi
  .

/*------------------------------------------------------------------------*/
ReadsClause<.List<FrameExpression/*!*/>/*!*/ reads, ref Attributes attrs,
                  bool allowLemma, bool allowLambda, bool allowWild.>
= "reads"
  (. FrameExpression fe; .)
  { Attribute<ref attrs> }
  PossiblyWildFrameExpression<out fe, allowLemma, allowLambda, allowWild>          (. reads.Add(fe); .)
  { "," PossiblyWildFrameExpression<out fe, allowLemma, allowLambda, allowWild>    (. reads.Add(fe); .)
  }
  (. if (allowWild && reads.Count > 1 && reads.Exists(fe => fe.E is WildcardExpr)) {
       SemErr(ErrorId.p_reads_star_must_be_alone, reads.First(fe => fe.E is WildcardExpr).Tok, "A 'reads' clause that contains '*' is not allowed to contain any other expressions");
     }
  .)
  OldSemi
  .

/*------------------------------------------------------------------------*/
InvariantClause<. List<AttributedExpression> invariants.> =
  "invariant"                                   (. Attributes attrs = null;
                                                   Expression e;
                                                .)
  { Attribute<ref attrs> }
  Expression<out e, false, true>                (. invariants.Add(new AttributedExpression(e, attrs)); .)
  OldSemi
  .

/*------------------------------------------------------------------------*/
MethodSpec<.bool isGhost, List<AttributedExpression> req, List<FrameExpression> reads, List<FrameExpression> mod, List<AttributedExpression> ens,
            List<Expression> decreases, ref Attributes decAttrs, ref Attributes modAttrs, ref Attributes readsAttrs, string caption, bool performThisDeprecatedCheck.>
= (. Contract.Requires(cce.NonNullElements(req));
     Contract.Requires(cce.NonNullElements(reads));
     Contract.Requires(cce.NonNullElements(mod));
     Contract.Requires(cce.NonNullElements(ens));
     Contract.Requires(cce.NonNullElements(decreases));
  .)
  SYNC
  { ReadsClause<reads, ref readsAttrs, false, false, true>
  | ModifiesClause<ref mod, ref modAttrs, false, performThisDeprecatedCheck>
  | RequiresClause<req, true>
  | EnsuresClause<ens, false>
  | DecreasesClause<decreases, ref decAttrs, !isGhost, false>
  }
  .

/*------------------------------------------------------------------------*/
IteratorSpec<.List<FrameExpression/*!*/>/*!*/ reads, List<FrameExpression/*!*/>/*!*/ mod, List<Expression/*!*/> decreases,
              List<AttributedExpression/*!*/>/*!*/ req, List<AttributedExpression/*!*/>/*!*/ ens,
              List<AttributedExpression/*!*/>/*!*/ yieldReq, List<AttributedExpression/*!*/>/*!*/ yieldEns,
              ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs.>
=
  SYNC
  { ReadsClause<reads, ref readsAttrs, false, false, false>
  | ModifiesClause<ref mod, ref modAttrs, false, false>
  | (. bool isYield = false; .)
    [ "yield"                                                (. isYield = true; .)
    ]
    ( RequiresClause<(isYield?yieldReq:req), !isYield>
    | EnsuresClause<(isYield?yieldEns:ens), false>
    )
  | DecreasesClause<decreases, ref decrAttrs, false, false>
  }
  .

/*------------------------------------------------------------------------*/
Formals<.bool incoming, bool allowGhostKeyword, bool allowNewKeyword, bool allowOlderKeyword, List<Formal> formals.>
= (. Contract.Requires(cce.NonNullElements(formals));
     Type ty;
     bool isGhost;
     bool isOld;
     Expression defaultValue;
     bool isNameOnly;
     bool isOlder;
     Attributes attrs = null;
     SourceOrigin range;
     Name name;
  .)
  "("
  [
    { Attribute<ref attrs> }
    GIdentType<allowGhostKeyword, allowNewKeyword, incoming, allowOlderKeyword, out range, out name, out ty, out isGhost, out isOld, out isNameOnly, out isOlder>
    ParameterDefaultValue<incoming, out defaultValue>
                 (. formals.Add(new Formal(name.Tok, name, ty, incoming, isGhost, defaultValue, attrs, isOld, isNameOnly, isOlder)
                        { Origin = defaultValue != null ? new SourceOrigin(range.StartToken, defaultValue.EndToken) : range }
                    ); .)
    { ","
      { Attribute<ref attrs> }
      GIdentType<allowGhostKeyword, allowNewKeyword, incoming, allowOlderKeyword, out range, out name, out ty, out isGhost, out isOld, out isNameOnly, out isOlder>
      ParameterDefaultValue<incoming, out defaultValue>
                 (. formals.Add(new Formal(name.Tok, name, ty, incoming, isGhost, defaultValue, attrs, isOld, isNameOnly, isOlder)
                        { Origin = defaultValue != null ? new SourceOrigin(range.StartToken, defaultValue.EndToken) : range }
                    ); .)
    }
  ]
  ")"
  .

ParameterDefaultValue<bool incoming, out Expression defaultValue>
= (. defaultValue = null; Token tok;
  .)
  [ ":="        (. tok = t; .)
    Expression<out defaultValue, true, true>
    (. if (!incoming) {
         SemErr(ErrorId.p_no_defaults_for_out_parameters, new SourceOrigin(tok,t), "out-parameters cannot have default-value expressions");
         defaultValue = null;
       }
    .)
  ]
  .

/*------------------------------------------------------------------------*/
FormalsOptionalIds<.List<Formal/*!*/>/*!*/ formals.>
= (. Contract.Requires(cce.NonNullElements(formals));
     SourceOrigin/*!*/ range;  Type/*!*/ ty;  Name/*!*/ name;  bool isGhost;  Expression/*?*/ defaultValue;
     bool isNameOnly;
     Attributes attributes;
  .)
  "("
  [
    TypeIdentOptional<out range, out name, out ty, out isGhost, out defaultValue, out isNameOnly, out attributes>
            (. formals.Add(new Formal(name.Tok, name, ty, true, isGhost, defaultValue, attributes, false, isNameOnly) 
                    { Origin = range }
               ); 
            .)
    { "," TypeIdentOptional<out range, out name, out ty, out isGhost, out defaultValue, out isNameOnly, out attributes>
            (. formals.Add(new Formal(name.Tok, name, ty, true, isGhost, defaultValue, attributes, false, isNameOnly)
                    { Origin = range }
               ); 
            .)
    }
  ]
  ")"
  .

/*------------------------------------------------------------------------*/
Type<out Type ty>
= (. Contract.Ensures(Contract.ValueAtReturn(out ty) != null); Token/*!*/ tok; .)
  TypeAndToken<out tok, out ty, false>
  .

TypeAndToken<out Token tok, out Type ty, bool inExpressionContext>
= (. Contract.Ensures(Contract.ValueAtReturn(out tok)!=null);
     Contract.Ensures(Contract.ValueAtReturn(out ty) != null);
     tok = Token.NoToken;
     ty = new BoolType();  /*keep compiler happy*/
     Token startToken = null;
     List<Type> gt;
     List<Type> tupleArgTypes = null;
     List<Token> argumentGhostTokens = null;
  .)
  ( "bool"                          (. tok = t; startToken = t; .)
  | "char"                          (. tok = t; startToken = t;  ty = new CharType(); .)
  | "int"                           (. tok = t; startToken = t;  ty = new IntType(); .)
  | "nat"                           (. tok = t; startToken = t;  ty = new UserDefinedType(tok, tok.val, null); .)
  | "real"                          (. tok = t; startToken = t;  ty = new RealType(); .)
  | "ORDINAL"                       (. tok = t; startToken = t;  ty = new BigOrdinalType(); .)
  | bvToken                         (. tok = t; startToken = t;
                                       int w = StringToInt(tok.val.Substring(2), 0, "bitvectors that wide", startToken);
                                       ty = new BitvectorType(theOptions, w);
                                    .)
  | "set"                           (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. if (gt != null && gt.Count > 1) {
                                         SemErr(ErrorId.p_set_only_one_type_parameter, 
                                           new SourceOrigin(startToken.Next, t), "set type expects only one type argument");
                                       }
                                       ty = new SetType(true, gt != null ?gt[0] : null);
                                    .)
  | "iset"                          (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. if (gt != null && gt.Count > 1) {
                                         SemErr(ErrorId.p_iset_only_one_type_parameter, 
                                           new SourceOrigin(startToken.Next, t), "iset type expects only one type argument");
                                       }
                                       ty = new SetType(false, gt != null ? gt[0] : null);
                                    .)
  | "multiset"                      (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. if (gt != null && gt.Count > 1) {
                                         SemErr(ErrorId.p_multiset_only_one_type_parameter, 
                                           new SourceOrigin(startToken.Next, t), "multiset type expects only one type argument");
                                       }
                                       ty = new MultiSetType(gt != null ? gt[0] : null);
                                    .)
  | "seq"                           (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. if (gt != null && gt.Count > 1) {
                                         SemErr(ErrorId.p_seq_only_one_type_parameter, 
                                           new SourceOrigin(startToken.Next, t), "seq type expects only one type argument");
                                       }
                                       ty = new SeqType(gt != null ? gt[0] : null);
                                    .)
  | "string"                        (. tok = t; startToken = t;  ty = new UserDefinedType(tok, tok.val, null); .)
  | "object"                        (. tok = t; startToken = t;  ty = new UserDefinedType(tok, tok.val, null); .)
  | "object?"                       (. tok = t; startToken = t;  ty = new UserDefinedType(tok, tok.val, null); .)
  | "map"                           (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. if (gt == null) {
                                         ty = new MapType(true, null, null);
                                       } else if (gt.Count != 2) {
                                         SemErr(ErrorId.p_map_needs_two_type_parameters, 
                                           new SourceOrigin(startToken.Next, t), "map type expects two type arguments");
                                         ty = new MapType(true, gt[0], gt.Count == 1 ? new InferredTypeProxy() : gt[1]);
                                       } else {
                                         ty = new MapType(true, gt[0], gt[1]);
                                       }
                                    .)
  | "imap"                          (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. if (gt == null) {
                                         ty = new MapType(false, null, null);
                                       } else if (gt.Count != 2) {
                                         SemErr(ErrorId.p_imap_needs_two_type_parameters, 
                                           new SourceOrigin(startToken.Next, t), "imap type expects two type arguments");
                                         ty = new MapType(false, gt[0], gt.Count == 1 ? new InferredTypeProxy() : gt[1]);
                                       } else {
                                         ty = new MapType(false, gt[0], gt[1]);
                                       }
                                    .)
  | arrayToken                      (. tok = t; startToken = t; .)
    OptGenericInstantiation<out gt, inExpressionContext>
                                    (. var tokString = tok.val;
                                       bool q = tokString[tokString.Length-1] == '?';
                                       // Extracting the dimension out of array2 or array10?
                                       var dimString = tokString.Substring(5, tokString.Length - (q?6:5)); // 5 is length of "array"
                                       int dims = StringToInt(dimString, 1, "arrays of that many dimensions", startToken);
                                       (ty, var bMod) = SystemModuleManager.ArrayType(tok, dims, gt, true, q);
                                       SystemModuleModifiers.Add(bMod);
                                    .)
  | TupleType<out ty, out tok, out tupleArgTypes, out argumentGhostTokens>
  | NamedType<out ty, out tok, inExpressionContext>
  )
  (. startToken = startToken ?? ty.StartToken ?? tok;
     ty.Origin = new SourceOrigin(startToken, t);
  .)
  [ (. int arrowKind = 0; /* 0: any, 1: partial, 2: total */
       Type t2;
    .)
    ( "~>"           (. arrowKind = 0; .)
    | "-->"          (. arrowKind = 1; .)
    | "->"           (. arrowKind = 2; .)
    )                (. tok = t; .)
    Type<out t2>
    (. if (tupleArgTypes == null) {
         gt = new List<Type>{ ty };
       } else {
         // make sure no "ghost" keyword was used in the tuple-looking type
         foreach (var ghostToken in argumentGhostTokens.Where(ghostToken => ghostToken != null)) {
           SemErr(ErrorId.p_no_ghost_arrow_type_arguments, ghostToken, $"arrow-type arguments may not be declared as 'ghost'");
         }
         gt = tupleArgTypes;
       }
       var arity = gt.Count;
       SystemModuleModifiers.Add(b => b.CreateArrowTypeDecl(arity));
       if (arrowKind == 0) {
         ty = new ArrowType(tok, gt, t2);
       } else {
         gt.Add(t2);
         if (arrowKind == 1) {
           ty = new UserDefinedType(tok, ArrowType.PartialArrowTypeName(arity), gt);
         } else {
           ty = new UserDefinedType(tok, ArrowType.TotalArrowTypeName(arity), gt);
         }
       }
       ty.Origin = new SourceOrigin(startToken, t);
    .)
  ]
  .

/*------------------------------------------------------------------------*/

TupleType<.out Type ty, out Token tok, out List<Type> tupleArgTypes, out List<Token> argumentGhostTokens.> =
  "("                             (. tok = t;
                                     ty = null; // To keep compiler happy
                                     tupleArgTypes = new List<Type>();
                                     Token ghostToken = null;
                                     argumentGhostTokens = new List<Token>();
                                  .)
  [ [ "ghost"                     (. ghostToken = t; .)
    ]
    Type<out ty>                  (. tupleArgTypes.Add(ty); argumentGhostTokens.Add(ghostToken); .)
    { ","                         (. ghostToken = null; .)
      [ "ghost"                   (. ghostToken = t; .)
      ]
      Type<out ty>                (. tupleArgTypes.Add(ty); argumentGhostTokens.Add(ghostToken); .)
    }
  ]
  ")"                             (. if (tupleArgTypes.Count == 1 && argumentGhostTokens[0] == null) {
                                       // just return the type 'ty'
                                     } else {
                                       var dims = tupleArgTypes.Count;
                                       var argumentGhostness = argumentGhostTokens.ConvertAll(tok => tok != null);
                                       var tokCopy = tok;
                                       SystemModuleModifiers.Add(b => b.TupleType(tokCopy, dims, true, argumentGhostness));  // make sure the tuple type exists
                                       ty = new UserDefinedType(tok, SystemModuleManager.TupleTypeName(argumentGhostness), dims == 0 ? null : tupleArgTypes);
                                     }
                                  .)
  .

/*------------------------------------------------------------------------*/
NamedType<.out Type ty, out Token tok, bool inExpressionContext.> =
  (. Expression e; Token startToken = null; .)
  NameSegmentForTypeName<out e, inExpressionContext>  (. tok = t; startToken = e.StartToken; .)
  { "."
    TypeNameOrCtorSuffix<out tok>       (. List<Type> typeArgs; .)
    OptGenericInstantiation<out typeArgs, inExpressionContext>
    (. e = new ExprDotName(tok, e, new Name(tok), typeArgs); .)
  }
  (. ty = new UserDefinedType(e.Tok, e);
     ty.Origin = new SourceOrigin(startToken, t);
   .)
  .

/*------------------------------------------------------------------------*/
OptGenericInstantiation<.out List<Type> gt, bool inExpressionContext.>  /* NOTE: Coco complains about "OptGenericInstantiation deletable". That's okay. */
= (. gt = null; .)
  [ IF(IsGenericInstantiation(inExpressionContext))  /* be greedy -- if it looks like a type instantiation, take it */
    (. gt = new List<Type>(); .)
    GenericInstantiation<gt>
  ]
  .

/*------------------------------------------------------------------------*/
GenericInstantiation<.List<Type> gt.>
= (. Contract.Requires(cce.NonNullElements(gt)); Type/*!*/ ty; .)
  "<"
  (
    ">" (. SemErr(ErrorId.p_no_empty_type_parameter_list, t, "empty type parameter lists are not permitted"); .)
  |
    Type<out ty>                     (. gt.Add(ty); .)
    { "," Type<out ty>               (. gt.Add(ty); .)
    }
    ">"
  )
  .

/*------------------------------------------------------------------------*/
FunctionDecl<DeclModifierData dmod, out Function/*!*/ f>
= (. Contract.Ensures(Contract.ValueAtReturn(out f) != null);
     Name name = null; // To please compiler
     List<TypeParameter> typeArgs = new List<TypeParameter>();
     List<Formal> formals = new List<Formal>();
     Formal/*!*/ result = null;
     Type/*!*/ returnType = new BoolType();
     List<AttributedExpression> reqs = new List<AttributedExpression>();
     List<AttributedExpression> ens = new List<AttributedExpression>();
     List<FrameExpression> reads = new List<FrameExpression>();
     List<Expression> decreases;
     Attributes decAttrs = null;
     Attributes readsAttrs = null;
     Expression body = null;
     bool isPredicate = false; bool isLeastPredicate = false; bool isGreatestPredicate = false;
     Token/*?*/ headToken = null; // used only for a basic "function" or "predicate"
     Token/*?*/ functionMethodToken = null; // used only for a basic "function" or "predicate"
     Token bodyStart = Token.NoToken;
     Token bodyEnd = Token.NoToken;
     Token signatureEllipsis = null;
     bool isTwoState = false;
     ExtremePredicate.KType kType = ExtremePredicate.KType.Unspecified;
  .)
  /* ========================================
   * This production parses variations of functions. By the time control reaches this point, modifiers like
   * "static", "ghost", and "abstract" have already been parsed; these are recorded into parameter "dmod" and
   * get checked by one of the calls to "CheckDeclModifiers" below. The "ghost" keyword (which was already parsed,
   * if present in the input) and the next grammar elements to be parsed fall into one of the
   * following 4 schemas:
   *
   *   0:   [ ghost ] [ twostate ] function [ method ]    signature  [ "{" Expr "}" [ by method BlockStmt ] ]
   *   1:   [ ghost ] [ twostate ] predicate [ method ]   signature  [ "{" Expr "}" [ by method BlockStmt ] ]
   *   2:   [ ghost ] least predicate                     signature  [ "{" Expr "}" [ by method BlockStmt ] ]
   *   3:   [ ghost ] greatest predicate                  signature  [ "{" Expr "}" [ by method BlockStmt ] ]
   *
   * Parsed below, but not shown in these 4 schemas, are the "..." token, which may be part of the "signature",
   * and various deprecated synonyms for the least/greatest variations.
   *
   * A quick look at the code below suggests that "twostate" may be parsed in all 4 of these schemas, but
   * the lookahead that has brought control here to the "FunctionDecl" production has already ruled out the
   * "twostate least" and "twostate greatest" combinations. That's why schemas 2 and 3 above do not mention
   * the possibility of "twostate".
   *
   * Several of the combinations shown in the 4 schemas above are not allowed. In particular:
   *
   *     - "ghost" is never allowed together with "twostate", "least", or "greatest"
   *     - "method" is never allowed together with "twostate", "least", or "greatest"
   *     - "by method" is never allowed together with "twostate", "least", or "greatest"
   *     - "by method" is not allowed with either "ghost" or "method"
   *     - "ghost" and "method" are never allowed together, and which of the combinations
   *           ghost function/predicate
   *                 function/predicate
   *                 function/predicate method
   *       are allowed depends on the value of command-line option "--function-syntax".
   *
   * Because of when the various keywords and grammar elements are parsed, the parser reads one of the 4 schemas
   * above in its entirety before the code checks for legal combinations. So, up next is the parsing of each of
   * the 4 schemas, up until and including "signature".         
   */

  /* ----- function ----- */
  [ "twostate"   (. isTwoState = true; CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
  ]
  ( "function"   (. headToken = t; CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
    [ "method"   (. functionMethodToken = t; .)
    ]
    { Attribute<ref dmod.Attributes> }
    MethodFunctionName<out name>
    (
      [ GenericParameters<typeArgs, false> ]
      Formals<true, true, isTwoState, true, formals>
      ":"
      (  IF(IsParenIdentsColon())
         "("
           GIdentType<false, false, false, false, out var range2, out var resultName, out var ty, out var resultIsGhost, out var isOld, out var isNameOnly, out var isOlder>
           (. Contract.Assert(!resultIsGhost && !isOld && !isNameOnly && !isOlder);
              result = new Formal(resultName.Tok, resultName, ty, false, false, null, null, false)
                { Origin = range2 };
           .)
         ")"
         | Type<out returnType>
      )
    | ellipsis   (. signatureEllipsis = t; .)
    )

  /* ----- predicate ----- */
  | "predicate"  (. headToken = t; isPredicate = true; CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
    [ "method"   (. functionMethodToken = t; .)
    ]
    { Attribute<ref dmod.Attributes> }
    MethodFunctionName<out name>
    (
      [ GenericParameters<typeArgs, false> ]
      [ KType<ref kType, out Token openBracket, out Token closeBracket> 
        (. SemErr(ErrorId.p_formal_ktype_only_in_least_and_greatest_predicates,
                  new SourceOrigin(openBracket, t), 
                  "a formal [ ] declaration is only allowed for least and greatest predicates");
        .)
      ]
      Formals<true, true, isTwoState, true, formals>
      [ PredicateResult<"predicate", out result> ]
    | ellipsis   (. signatureEllipsis = t; .)
    )

  /* ----- least predicate ----- */
  | (. Contract.Assert(!isTwoState);  // the IsFunctionDecl check checks that "twostate" is not followed by "least"
    .)
    ( "least"                  (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
    | "inductive"              (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); errors.Deprecated(ErrorId.p_deprecated_inductive_predicate, t, "the old keyword phrase 'inductive predicate' has been renamed to 'least predicate'"); .)
    )
    "predicate"
    (. isLeastPredicate = true; .)
    { Attribute<ref dmod.Attributes> }
    MethodFunctionName<out name>
    (
      [ GenericParameters<typeArgs, false> ]
      [ KType<ref kType, out Token openBracket, out Token closeBracket> ]
      Formals<true, false, false, false, formals>
      [ PredicateResult<"least predicate", out result> ]
    | ellipsis                 (. signatureEllipsis = t; .)
    )

  /* ----- greatest predicate ----- */
  | (. Contract.Assert(!isTwoState);  // the IsFunctionDecl check checks that "twostate" is not followed by "greatest"
    .)
    ( "greatest"                 (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); .)
      "predicate"     
    | "copredicate"              (. CheckAndSetTokenOnce(ref dmod.FirstTokenExceptAttributes); errors.Deprecated(ErrorId.p_deprecated_copredicate, t, "the old keyword 'copredicate' has been renamed to the keyword phrase 'greatest predicate'"); .)
    )
    (. isGreatestPredicate = true; .)
    { Attribute<ref dmod.Attributes> }
    MethodFunctionName<out name>
    (
      [ GenericParameters<typeArgs, false> ]
      [ KType<ref kType, out Token openBracket, out Token closeBracket> ]
      Formals<true, false, false, false, formals>
      [ PredicateResult<"greatest predicate", out result> ]
    | ellipsis                 (. signatureEllipsis = t; .)
    )
  )

  (. decreases = isLeastPredicate || isGreatestPredicate ? null : new List<Expression/*!*/>();
  .)
  FunctionSpec<reqs, reads, ens, decreases, ref decAttrs, ref readsAttrs>
  (. Token byMethodTok = null; BlockStmt byMethodBody = null; .)
  [ FunctionBody<out body, out bodyStart, out bodyEnd, out byMethodTok, out byMethodBody> ]

  (. /* ========================================
      * Check if the keywords parsed above are allowed. We divide these checks into two broad categories,
      * depending on whether or not "by method" was parsed. (For reference, see the 4 schemas above.)
      */

     if (byMethodBody != null) {
       /* ----- with "by method" ----- */
       var what = isPredicate || isLeastPredicate || isGreatestPredicate ? "predicate" : "function";
 
       if (isTwoState) {
         var byrange = new SourceOrigin(byMethodTok, byMethodBody.EndToken);
         SemErr(ErrorId.p_no_by_method_in_twostate, byrange, $"a 'by method' implementation is not allowed on a twostate {what}");
         byMethodBody = null;
         functionMethodToken = null; // to avoid a confusing error message below, drop the "method" keyword, if present

       } else if (isLeastPredicate || isGreatestPredicate) {
         var byrange = new SourceOrigin(byMethodTok, byMethodBody.EndToken);
         SemErr(ErrorId.p_no_by_method_in_extreme_predicate, byrange, "a 'by method' implementation is not allowed on an extreme predicate");
         byMethodBody = null;
         functionMethodToken = null; // to avoid a confusing error message below, drop the "method" keyword, if present
       }

       /* Neither "ghost" nor "method" is allowed. After generating the appropriate error messages, update the
        * "functionMethodToken" and "dmod" variables to erase having seen any illegal tokens.
        */
       if (functionMethodToken != null) {
         SemErr(ErrorId.p_no_by_method_for_ghost_function, functionMethodToken,
           $"to use a 'by method' implementation with a {what}, declare '{name.Value}' using '{what}', not '{what} method'");
         functionMethodToken = null;
       }
       AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Opaque;
       CheckDeclModifiers(ref dmod, what + "-by-method", allowed);

     } else {
       /* ----- without "by method" ----- */

       var what = isPredicate ? "predicate" : "function";
       if (isTwoState || isLeastPredicate || isGreatestPredicate) {
         var adjective = isTwoState ? "twostate" : isLeastPredicate ? "least" : "greatest";

         if (functionMethodToken != null) {
           SemErr(ErrorId.p_twostate_and_extreme_are_always_ghost, functionMethodToken, $"a {adjective} {what} is supported only as ghost, not as a compiled {what}");
           functionMethodToken = null;
         }
         AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Opaque;
         CheckDeclModifiers(ref dmod, $"{adjective} {what}", allowed);

       } else {
         // basic function or predicate
         Contract.Assert(headToken != null);
         if (functionMethodToken != null) {
           if (isPredicate && theOptions.FunctionSyntax == FunctionSyntaxOptions.ExperimentalPredicateAlwaysGhost) {
             SemErr(ErrorId.p_old_ghost_syntax, functionMethodToken, $"a {what} is always ghost and is declared with '{what}'");
             functionMethodToken = null;
             dmod.IsGhost = false; // don't report errors about 'ghost', too
           } else if (theOptions.FunctionSyntax == FunctionSyntaxOptions.Version4 ||
                      theOptions.FunctionSyntax == FunctionSyntaxOptions.ExperimentalPredicateAlwaysGhost) {
             var erange = new SourceOrigin(functionMethodToken.Prev, functionMethodToken);
             if (isPredicate) {
              SemErr(ErrorId.p_deprecating_predicate_method, erange, $"the phrase '{what} method' is not allowed when using --function-syntax:4; to declare a compiled {what}, use just '{what}'");
             } else {
              SemErr(ErrorId.p_deprecating_function_method, erange, $"the phrase '{what} method' is not allowed when using --function-syntax:4; to declare a compiled {what}, use just '{what}'");
             }
             functionMethodToken = null;
           } else if (dmod.IsGhost && theOptions.FunctionSyntax != FunctionSyntaxOptions.Version3) {
             var erange = new SourceOrigin(headToken, functionMethodToken);
             if (isPredicate) {
              SemErr(ErrorId.p_no_ghost_predicate_method, erange, $"there is no such thing as a 'ghost {what} method'");
             } else {
              SemErr(ErrorId.p_no_ghost_function_method, erange, $"there is no such thing as a 'ghost {what} method'");
             }
             functionMethodToken = null;
           }
         } else if (!dmod.IsGhost && theOptions.FunctionSyntax == FunctionSyntaxOptions.Migration3To4) {
           SemErr(ErrorId.p_migration_syntax, headToken, $"a {what} must be declared as either 'ghost {what}' or '{what} method' when using --function-syntax:migration3to4");
         }
         AllowedDeclModifiers allowed = AllowedDeclModifiers.Static | AllowedDeclModifiers.Opaque;
         if (isPredicate && theOptions.FunctionSyntax == FunctionSyntaxOptions.ExperimentalPredicateAlwaysGhost) {
           allowed |= AllowedDeclModifiers.AlreadyGhost;
         } else if (theOptions.FunctionSyntax == FunctionSyntaxOptions.Version3) {
           allowed |= AllowedDeclModifiers.AlreadyGhost;
         } else {
           allowed |= AllowedDeclModifiers.Ghost;
         }
         CheckDeclModifiers(ref dmod, what, allowed);
       }
     }

     /* ========================================
      * The 4 schemas have now been checked for legal combinations. In preparation for creating an AST node for
      * what was parsed, we determine if the function is considered ghost.
      * For our purposes here, a function-by-method is considered non-ghost.
      */

     bool isGhost;
     if (isTwoState || isLeastPredicate || isGreatestPredicate) {
       isGhost = true;
     } else if (byMethodBody != null) {
       isGhost = false;
     } else {
       switch (theOptions.FunctionSyntax) {
         case FunctionSyntaxOptions.Version3:
           isGhost = functionMethodToken == null;
           break;
         case FunctionSyntaxOptions.Migration3To4:
         case FunctionSyntaxOptions.Version4:
         case FunctionSyntaxOptions.ExperimentalTreatUnspecifiedAsCompiled:
           isGhost = dmod.IsGhost;
           break;
         case FunctionSyntaxOptions.ExperimentalTreatUnspecifiedAsGhost:
           isGhost = dmod.IsGhost || functionMethodToken == null;
           break;
         case FunctionSyntaxOptions.ExperimentalPredicateAlwaysGhost:
           isGhost = dmod.IsGhost || isPredicate;
           break;
         default:
           Contract.Assert(false); // unexpected FunctionSyntaxOptions
           isGhost = false; // to please the compiler
           break;
       }
     }
     // Some sanity checks
     Contract.Assert(theOptions.FunctionSyntax != FunctionSyntaxOptions.Version4 || functionMethodToken == null);
     Contract.Assert(theOptions.FunctionSyntax != FunctionSyntaxOptions.Version3 || !dmod.IsGhost);
     Contract.Assert(byMethodBody == null || (functionMethodToken == null && !dmod.IsGhost));
     Contract.Assert(byMethodBody == null || !isGhost);
 
     /* ========================================
      * Having computed "isGhost" for whatever declaration we have parsed, we know whether or not to
      * allow formal parameters to be "ghost". So, as a last round of checking, we inspect the formal in-parameters
      * given in the signature. 
      */

     if (isGhost) {
       foreach (var formal in formals) {
         if (formal.IsGhost) {
           IOrigin t = formal.Tok;
           IOrigin ghostToken = null;
           while (t != null && t.val != "," && t.val != "(") {
            if (t.val == "ghost") {
              ghostToken = t;
              break;
            }
            t = t.Prev;
           }
           SemErr(ErrorId.p_no_ghost_formal, ghostToken ?? formal.Tok, "formal cannot be declared 'ghost' in this context");
         }
       }
     }

     /* =========================================
      * Finally, we create the AST node for the function declaration we parsed.
      */
     var range = new SourceOrigin(dmod.FirstToken, t);
     if (isTwoState && isPredicate) {
       Contract.Assert(functionMethodToken == null && !dmod.IsGhost);
       f = new TwoStatePredicate(range, name, dmod.IsStatic, dmod.IsOpaque, typeArgs, formals, result,
                                 reqs, new Specification<FrameExpression>(reads, readsAttrs), ens, 
                                 new Specification<Expression>(decreases, decAttrs), body, dmod.Attributes, signatureEllipsis);
     } else if (isTwoState) {
       Contract.Assert(functionMethodToken == null && !dmod.IsGhost);
       f = new TwoStateFunction(range, name, dmod.IsStatic, dmod.IsOpaque, typeArgs, formals, result, returnType,
                                reqs, new Specification<FrameExpression>(reads, readsAttrs), ens, 
                                new Specification<Expression>(decreases, decAttrs), body, dmod.Attributes, signatureEllipsis);
     } else if (isPredicate) {
       Contract.Assert(functionMethodToken == null || !dmod.IsGhost);
       f = new Predicate(range, name, dmod.IsStatic, isGhost, dmod.IsOpaque, typeArgs, formals, result,
                         reqs, new Specification<FrameExpression>(reads, readsAttrs), ens, 
                         new Specification<Expression>(decreases, decAttrs), body, Predicate.BodyOriginKind.OriginalOrInherited,
                         byMethodTok, byMethodBody, dmod.Attributes, signatureEllipsis);
     } else if (isLeastPredicate) {
       Contract.Assert(functionMethodToken == null && !dmod.IsGhost);
       f = new LeastPredicate(range, name, dmod.IsStatic, dmod.IsOpaque, kType, typeArgs, formals, result,
                              reqs, new Specification<FrameExpression>(reads, readsAttrs), ens, body, dmod.Attributes, signatureEllipsis);
     } else if (isGreatestPredicate) {
       Contract.Assert(functionMethodToken == null && !dmod.IsGhost);
       f = new GreatestPredicate(range, name, dmod.IsStatic, dmod.IsOpaque, kType, typeArgs, formals, result,
                                 reqs, new Specification<FrameExpression>(reads, readsAttrs), ens, body, dmod.Attributes, signatureEllipsis);
     } else {
       Contract.Assert(functionMethodToken == null || !dmod.IsGhost);
       f = new Function(range, name, dmod.IsStatic, isGhost, dmod.IsOpaque,
                        typeArgs, formals, result, returnType,
                        reqs, new Specification<FrameExpression>(reads, readsAttrs), ens, new Specification<Expression>(decreases, decAttrs), body,
                        byMethodTok, byMethodBody,
                        dmod.Attributes, signatureEllipsis);
     }
     f.BodyStartTok = bodyStart;
     SystemModuleModifiers.Add(b => b.CreateArrowTypeDecl(formals.Count));
     if (isLeastPredicate || isGreatestPredicate) {
       // also create an arrow type for the corresponding prefix predicate
       SystemModuleModifiers.Add(b => b.CreateArrowTypeDecl(formals.Count + 1));
     }
  .)
  .

/*------------------------------------------------------------------------*/
FunctionSpec<.List<AttributedExpression> reqs, List<FrameExpression> reads, List<AttributedExpression> ens, List<Expression> decreases, ref Attributes decAttrs, ref Attributes readsAttrs.>
= (. Contract.Requires(cce.NonNullElements(reqs));
     Contract.Requires(cce.NonNullElements(reads));
     Contract.Requires(decreases == null || cce.NonNullElements(decreases));
  .)
  SYNC
  { RequiresClause<reqs, true>
  | ReadsClause<reads, ref readsAttrs, false, false, true>
  | EnsuresClause<ens, false>
  | (. var decreasesToken = la;
       var decreasesForbidden = decreases == null;
       if (decreases == null) {
         decreases = new List<Expression/*!*/>();
       }
    .)
    DecreasesClause<decreases, ref decAttrs, false, false>
    (. if (decreasesForbidden) {
       var erange = new SourceOrigin(decreasesToken, t);
       SemErr(ErrorId.p_no_decreases_for_extreme_predicates, erange, "'decreases' clauses are meaningless for least and greatest predicates, so they are not allowed");
    }.)
  }
  .

/*------------------------------------------------------------------------*/
PredicateResult<string name, out Formal result>
= (. Type/*!*/ returnType = new BoolType();
     result = null;
     Attributes attributes = null;
  .)
  ":"
  (  IF(IsParenIdentsColon())
     "("
       { Attribute<ref attributes> }
       GIdentType<false, false, false, false, out var range, out var nameId, out var ty, out var resultIsGhost, out var isOld, out var isNameOnly, out var isOlder>
       (.
          if (ty is not BoolType) {
            SemErr(ErrorId.p_predicate_return_type_must_be_bool, new SourceOrigin(ty.StartToken, t), $"{name} return type should be bool, got {ty}");
          }
          result = new Formal(nameId.Tok, nameId, ty, false, false, null, attributes, false)
            { Origin = range };
       .)
     ")"
     | Type<out returnType> (. SemErr(ErrorId.p_no_return_type_for_predicate, new SourceOrigin(returnType.StartToken, t), $"{name}s do not have an explicitly declared return type; it is always bool. Unless you want to name the result: ': (result: bool)'"); .)
  )
  .

/*------------------------------------------------------------------------*/
PossiblyWildExpression<out Expression e, bool allowLambda, bool allowWild>
= (. Contract.Ensures(Contract.ValueAtReturn(out e)!=null);
     e = dummyExpr; .)
  /* A decreases-* clause on a loop asks that no termination check be performed.
   * Use of this feature is sound only with respect to partial correctness.
   */
  ( "*"                        (. e = new WildcardExpr(t);
                                  if (!allowWild) {
                                    SemErr(ErrorId.p_no_wild_expression, e.Tok, "A '*' expression is not allowed here");
                                  }
                               .)
  | Expression<out e, false, allowLambda>
  )
  .

/*------------------------------------------------------------------------*/
PossiblyWildFrameExpression<out FrameExpression fe, bool allowLemma,
                            bool allowLambda, bool allowWild>
= (. Contract.Ensures(Contract.ValueAtReturn(out fe) != null); fe = dummyFrameExpr; .)
  /* A reads clause can list a wildcard, which allows the enclosing function to
   * read anything.  In many cases, and in particular in all cases where
   * the function is defined recursively, this makes it next to impossible to make
   * any use of the function.  Nevertheless, as an experimental feature, the
   * language allows it (and it is sound).
   */
  ( "*"                        (. fe = new FrameExpression(t, new WildcardExpr(t), null);
                                  if (!allowWild) {
                                     SemErr(ErrorId.p_no_wild_frame_expression, t, "A '*' frame expression is not permitted here");
                                  }
                               .)
  | FrameExpression<out fe, allowLemma, allowLambda>
  )
  .

/*------------------------------------------------------------------------*/
FrameField<out Token id> = "`" IdentOrDigits<out id>.

FrameExpression<out FrameExpression fe, bool allowLemma, bool allowLambda>
= (. Contract.Ensures(Contract.ValueAtReturn(out fe) != null);
     Expression/*!*/ e;
     Token/*!*/ id;
     string fieldName = null;
     IOrigin feTok = null;
     fe = dummyFrameExpr;
  .)
  ( Expression<out e, allowLemma, allowLambda>   (. feTok = e.Tok; .)
    [ FrameField<out id>        (. fieldName = id.val;  feTok = id; .)
    ]                           (. fe = new FrameExpression(feTok, e, fieldName); .)
  |
    FrameField<out id>          (. fieldName = id.val; .)
                                (. fe = new FrameExpression(id, new ImplicitThisExpr(id), fieldName); .)
  )
  .

/*------------------------------------------------------------------------*/
FunctionBody<out Expression/*!*/ e, out Token bodyStart, out Token bodyEnd, out Token/*?*/ byMethodTok, out BlockStmt/*?*/ byMethodBody>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr;
     byMethodTok = null; byMethodBody = null;
  .)
  "{"                         (. bodyStart = t; .)
  Expression<out e, true, true>
  "}"                         (. bodyEnd = t; .)
  [ "by"                      (. byMethodTok = t; .)
    "method"
    BlockStmt<out byMethodBody, out _, out _>
  ]
  .


/*------------------------------------------------------------------------*/
BlockStmt<out BlockStmt/*!*/ block, out Token bodyStart, out Token bodyEnd>
= (. Contract.Ensures(Contract.ValueAtReturn(out block) != null);
     List<Statement/*!*/> body = new List<Statement/*!*/>();
  .)
  "{"                                  (. bodyStart = t; .)
  { Stmt<body> }
  "}"                                  (. bodyEnd = t;
                                          block = new BlockStmt(new SourceOrigin(bodyStart, bodyEnd), body);
                                          .)
  .

OpaqueBlock<out Statement s>
= (.
    List<Statement/*!*/> body = new List<Statement/*!*/>();
    List<AttributedExpression> ensures = new();
    List<FrameExpression/*!*/> modifies = new List<FrameExpression/*!*/>();
    Attributes modifiesAttributes = null;
    Token start;
  .)
  "opaque" (. start = t; .)
  { ModifiesClause<ref modifies, ref modifiesAttributes, false, false>
  | EnsuresClause<ensures, false>
  }
  "{"
  { Stmt<body> }
  "}"
  (. 
    s = new OpaqueBlock(new SourceOrigin(start, t), body, ensures, 
      new Specification<FrameExpression>(modifies, modifiesAttributes));
  .)
  .

/*------------------------------------------------------------------------*/
DividedBlockStmt<out DividedBlockStmt body, out Token bodyStart, out Token bodyEnd>
= (. Contract.Ensures(Contract.ValueAtReturn(out body) != null);
     List<Statement> bodyInit = new List<Statement>();
     Token separatorTok = null;
     List<Statement> bodyProper = new List<Statement>();
  .)
  "{"                                  (. bodyStart = t; .)
  { Stmt<bodyInit> }
  [ "new"                              (. separatorTok = t; .)
    ";"
    { Stmt<bodyProper> }
  ]
  "}"                                  (. bodyEnd = t; .)
  (. body = new DividedBlockStmt(new SourceOrigin(bodyStart, t), bodyInit, separatorTok, bodyProper); .)
  .

/*------------------------------------------------------------------------*/
Stmt<.List<Statement/*!*/>/*!*/ ss.>
= (. Statement/*!*/ s;
  .)
  OneStmt<out s>                                (. ss.Add(s); .)
  .

/*------------------------------------------------------------------------*/
OneStmt<out Statement/*!*/ s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
     s = dummyStmt;  /* to please the compiler */
     BlockStmt bs;
     Token bodyStart, bodyEnd;
  .)
  SYNC
  ( BlockStmt<out bs, out bodyStart, out bodyEnd>  (. s = bs; .)
  | OpaqueBlock<out s>
  | IF(IsReveal(scanner.Peek())) RevealStmt<out s>
  | AssignStatement<out s>     // includes UpdateFailure
  | VarDeclStatement<out s>
  | ReturnStmt<out s>
  | IfStmt<out s>
  | WhileStmt<out s>
  | ForLoopStmt<out s>
  | AssertStmt<out s>
  | AssumeStmt<out s>
  | BreakOrContinueStmt<out s>
  | CalcStmt<out s>
  | ExpectStmt<out s>
  | ForallStmt<out s>
  | LabeledStmt<out s>
  | MatchStmt<out s>
  | ModifyStmt<out s>
  | PrintStmt<out s>
  | SkeletonStmt<out s>
  | YieldStmt<out s>
  )
  .

/*------------------------------------------------------------------------*/
LabeledStmt<out Statement s>
= (. Token colonToken;
     Token labelToken;
  .)
  "label"  (. Token id; labelToken = t; .)
  LabelName<out id> ":" (. colonToken = t; .)
  OneStmt<out s>
  (. s.Labels = new LList<Label>(new Label(id, id.val), s.Labels);
     s.PostLabelToken = s.StartToken;
     s.Origin = new SourceOrigin(labelToken, t);
  .)
  .

/*------------------------------------------------------------------------*/
SkeletonStmt<out Statement s>
= (. Token dotdotdot; .)
  ellipsis                             (. dotdotdot = t; .)
  ";"
  (. s = new SkeletonStatement(new SourceOrigin(dotdotdot, t));
     errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
  .)
  .

/*------------------------------------------------------------------------*/
BreakOrContinueStmt<out Statement/*!*/ s>
= (. var isContinue = false;
     Token start = Token.NoToken;
     Token label = null;
     int breakAndContinueCount = 1;
     Attributes attributes = null;
  .)
  ( "continue"              (. start = t; isContinue = true; .)
    [ Attribute<ref attributes> ]
    [ LabelName<out label> ]
  | "break"                 (. start = t; .)
    ( LabelName<out label>
    | { "break"             (. breakAndContinueCount++; .)
      }
      [ "continue"          (. breakAndContinueCount++; isContinue = true; .)
      ]
    )
  )
  SYNC
  ";"
  (. Contract.Assert(label == null || breakAndContinueCount == 1);
     s = label != null ?
       new BreakOrContinueStmt(new SourceOrigin(start, t), label, isContinue) :
       new BreakOrContinueStmt(new SourceOrigin(start, t), breakAndContinueCount, isContinue, attributes);
  .)
  .

/*------------------------------------------------------------------------*/
ReturnStmt<out Statement/*!*/ s>
= "return"
       (.
         Token returnTok = t;
         List<AssignmentRhs> rhss = null;
         AssignmentRhs r;
         Attributes attributes = null;
       .)
  [ Attribute<ref attributes> ]
  [ Rhs<out r>                       (. rhss = new List<AssignmentRhs>();
                                        rhss.Add(r);
                                     .)
    { "," Rhs<out r>                 (. rhss.Add(r); .)
    }
  ]
  ";"                                (. s = new ReturnStmt(new SourceOrigin(returnTok, t), rhss, attributes);
                                     .)
  .

/*------------------------------------------------------------------------*/
YieldStmt<out Statement/*!*/ s>
= "yield"
       (.
         Token yieldTok = t;
         List<AssignmentRhs> rhss = null;
         AssignmentRhs r;
       .)
  [ Rhs<out r>                       (. rhss = new List<AssignmentRhs>();
                                        rhss.Add(r);
                                     .)
    { "," Rhs<out r>                 (. rhss.Add(r); .)
    }
  ]
  ";"                                (. s = new YieldStmt(new SourceOrigin(yieldTok, t), rhss);
                                     .)
  .

/*------------------------------------------------------------------------*/
AssignStatement<out Statement/*!*/ s>
= (. List<Expression> lhss = new List<Expression>();
     List<AssignmentRhs> rhss = new List<AssignmentRhs>();
     Expression e;
     AssignmentRhs r;
     BlockStmt proof = null;
     IOrigin x = Token.NoToken;
     Token endTok = Token.NoToken;
     Token startToken = Token.NoToken;
     Attributes attrs = null;
     Attributes tokenAttrs = null;
     AttributedToken suchThatAssume = null;
     Expression suchThat = null;
     AttributedToken keywordToken = null;
     Expression exceptionExpr = null;
     ExprRhs exceptionRhs = null;
     Token endToken = Token.NoToken;
  .)
( Lhs<out e>                       (. x = e.Tok; startToken = e.StartToken; endToken = t; .)
  ( 
                                   (. lhss.Add(e); .)
    { "," Lhs<out e>               (. lhss.Add(e); .)
    }
    ( ":="                         (. x = t; .)
      Rhs<out r>                   (. rhss.Add(r); .)
      { "," Rhs<out r>             (. rhss.Add(r); .)
      }
    | ":|"                         (. x = t; .)
      [ IF(la.kind == _assume)     /* an Expression can also begin with an "assume", so this says to resolve it to pick up any "assume" here */
        "assume"                      (. var tok = t; .)
        { Attribute<ref tokenAttrs> } (. suchThatAssume = new AttributedToken(tok, tokenAttrs); .)
      ]
      Expression<out suchThat, false, true>
    | ":-"                         (. x = t; .)
      [ IF(IsAssumeTypeKeyword(la))   /* an Expression can also begin with these keywords, so this says to resolve it to pick up the keyword here */
        ("expect"|"assert"|"assume")  (. var tok = t; .)
        { Attribute<ref tokenAttrs> } (. keywordToken = new AttributedToken(tok, tokenAttrs); .)
      ]
      Expression<out exceptionExpr, false, false>
      { Attribute<ref attrs> }     (. exceptionRhs = new ExprRhs(exceptionExpr, attrs); .)
      { "," Rhs<out r>             (. rhss.Add(r); .)
      }
    )
    (
      "by" BlockStmt<out proof, out _, out _>
    | ";"
    )
  | ":"                            (. SemErr(ErrorId.p_invalid_colon, new SourceOrigin(startToken, t), "invalid statement beginning here (is a 'label' keyword missing? or a 'const' or 'var' keyword?)"); .)
  | { Attribute<ref attrs> }       (. endToken = t; .)
    (
      "by" BlockStmt<out proof, out _, out _>
    | ";"
    )                              (. endTok = t; rhss.Add(new ExprRhs(e, attrs) { Origin = new SourceOrigin(e.StartToken, endToken) }); .)
  | { Attribute<ref attrs> }       (. endToken = t; .)
                                   (. endTok = t; rhss.Add(new ExprRhs(e, attrs) { Origin = new SourceOrigin(e.StartToken, endToken) });
                                      SemErr(ErrorId.p_missing_semicolon, new SourceOrigin(startToken, t), "missing semicolon at end of statement");
                                   .)   
  )
| ":-"                             (. x = t; startToken = t; .)
   [ IF(IsAssumeTypeKeyword(la))      /* an Expression can also begin with these keywords, so this says to resolve it to pick up the keyword here */
      ("expect"|"assert"|"assume")    (. var tok = t; .)
      { Attribute<ref tokenAttrs> }   (. keywordToken = new AttributedToken(tok, tokenAttrs); .)
   ]
   Expression<out exceptionExpr, false, false>
   { Attribute<ref attrs> }        (. exceptionRhs = new ExprRhs(exceptionExpr, attrs); .)
   { "," Rhs<out r>                (. rhss.Add(r); .)
   }
   (
     "by" BlockStmt<out proof, out _, out _>
   | ";"                           (. endTok = t; .)
   )                           
)
  (. var rangeToken = new SourceOrigin(startToken, t);
     if (suchThat != null) {
       s = new AssignSuchThatStmt(rangeToken, lhss, suchThat, suchThatAssume, null);
     } else if (exceptionRhs != null) {
       s = new AssignOrReturnStmt(rangeToken, lhss, exceptionRhs, keywordToken, rhss);
       if (proof != null) {
         s = new BlockByProofStmt(rangeToken, proof, s);
       }
     } else {
       if (lhss.Count == 0 && rhss.Count == 0) {
         s = new BlockStmt(rangeToken, new List<Statement>()); // error, give empty statement
       } else {
         s = new AssignStatement(rangeToken, lhss, rhss);
         if (proof != null) {
           s = new BlockByProofStmt(rangeToken, proof, s);
         }
       }
     }
  .)
  .

/*------------------------------------------------------------------------*/
Rhs<out AssignmentRhs r>
= (. Contract.Ensures(Contract.ValueAtReturn<AssignmentRhs>(out r) != null);
     Token/*!*/ x, newToken;  Expression/*!*/ e;
     Type ty = new InferredTypeProxy();
     List<Expression> ee = null;
     List<ActualBinding> args = null;
     Expression arrayElementInit = null;
     List<Expression> display = null;
     r = dummyRhs;  // to please compiler
     Attributes attrs = null;
     Token startToken = null;
  .)
  ( IF(la.val == "new")
    "new"                              (. newToken = t; startToken = t; .)
    ( NewArray<out ee, out arrayElementInit, out display>  // "ty" is set to InferredTypeProxy above
    | TypeAndToken<out x, out ty, false>
      [ NewArray<out ee, out arrayElementInit, out display>
      |                                (. x = null; args = new List<ActualBinding>(); .)
        "("
          [ ActualBindings<args> ]
        ")"
      ]
    )
    (. if (ee != null) {
         if (display != null) {
           r = new TypeRhs(newToken, ty, ee[0], display);
         } else {
           r = new TypeRhs(newToken, ty, ee, arrayElementInit);
         }
       } else if (args != null) {
         r = new TypeRhs(newToken, ty, args);
       } else {
         r = new TypeRhs(newToken, ty);
       }
    .)
  | "*"                                (. r = new HavocRhs(t); startToken = t; .)
  | Expression<out e, false, true>     (. r = new ExprRhs(e); startToken = e.StartToken; .)
  )
  { Attribute<ref attrs> }             (. r.Attributes = attrs;
                                          r.Origin = new SourceOrigin(startToken ?? t, t); .)
  .

/*------------------------------------------------------------------------*/
NewArray<. out List<Expression> ee, out Expression arrayElementInit, out List<Expression> display .>
= (. ee = new List<Expression>();
     arrayElementInit = null;
     display = null;
     Token x;
  .)
  "["                                (. x = t; .)
  ( "]"                              /* no size is given; this is allowed as long as an initialization display is given */
    "["                              (. display = new List<Expression>(); .)
    [ Expressions<display> ]
    "]"                              (. // we fill in the size
                                        ee.Add(new LiteralExpr(new AutoGeneratedOrigin(x), display.Count));
                                     .)
  | Expressions<ee>
    "]"                              (. // make sure an array class with this dimensionality exists
                                        var eeCopy = ee;
                                        SystemModuleModifiers.Add(b => b.ArrayType(eeCopy.Count, new IntType(), true));
                                     .)
    [ "(" Expression<out arrayElementInit, true, true>
      ")"
    | "["                            (. if (ee.Count > 1) {
                                          SemErr(ErrorId.p_initializing_display_only_for_1D_arrays, t, "An initializing element display is allowed only for 1-dimensional arrays");
                                        }
                                        display = new List<Expression>();
                                     .)
      [ Expressions<display> ]
      "]"
    ]
  )
  (. if (ee.Count == 0) {
       // an error occurred while parsing, but we still want to make sure to return a nonempty "ee"
       ee.Add(new LiteralExpr(new AutoGeneratedOrigin(x), 0));
     }
  .)
  .

/*------------------------------------------------------------------------*/
VarDeclStatement<.out Statement/*!*/ s.>
= (. Token x = null, assignTok = null;  bool isGhost = false;
     LocalVariable d;
     AssignmentRhs r;
     List<LocalVariable> lhss = new List<LocalVariable>();
     List<AssignmentRhs> rhss = new List<AssignmentRhs>();
     AttributedToken suchThatAssume = null;
     Expression suchThat = null;
     AttributedToken keywordToken = null;
     Expression exceptionExpr = null;
     ExprRhs exceptionRhs = null;
     Attributes attrs = null;
     Attributes tokenAttrs = null;
     BlockStmt proof = null;
     Token endTok;
     Token startToken = null;
     s = dummyStmt;
  .)
  [ "ghost"                                 (. isGhost = true;  x = t; startToken = t; .)
  ]
  "var"                                     (. if (!isGhost) { x = t; startToken = t; } .)
  ( IF(!IsPatternDecl())
    { Attribute<ref attrs> }
    LocalIdentTypeOptional<out d, isGhost>    (. lhss.Add(d); d.Attributes = attrs; attrs = null; .)
    { ","
      { Attribute<ref attrs> }
      LocalIdentTypeOptional<out d, isGhost>  (. lhss.Add(d); d.Attributes = attrs; attrs = null; .)
    }
    [ ( ":=" | "=" (. SemErr(ErrorId.p_no_equal_for_initializing, t, "a local variable should be initialized using ':=', ':-', or ':|', not '='"); .) )                         
                                     (. assignTok = t; .)
      Rhs<out r>                     (. rhss.Add(r); .)
      { "," Rhs<out r>               (. rhss.Add(r); .)
      }
    | { Attribute<ref attrs> }
      ":|"                           (. assignTok = t; .)
      [ IF(la.kind == _assume)       /* an Expression can also begin with an "assume", so this says to resolve it to pick up any "assume" here */
        "assume"                      (. var tok = t; .)
        { Attribute<ref tokenAttrs> } (. suchThatAssume = new AttributedToken(tok, tokenAttrs); .)
      ]
      Expression<out suchThat, false, true>
    | ":-"                           (. assignTok = t; .)
      [ IF(IsAssumeTypeKeyword(la))     /* an Expression can also begin with these keywords, so this says to resolve it to pick up the keyword here */
         ("expect"|"assert"|"assume")  (. var tok = t; .)
         { Attribute<ref tokenAttrs> } (. keywordToken = new AttributedToken(tok, tokenAttrs); .)
      ]
      Expression<out exceptionExpr, false, false>
      { Attribute<ref attrs> }       (. exceptionRhs = new ExprRhs(exceptionExpr, attrs); .)
      { "," Rhs<out r>               (. rhss.Add(r); .)
      }
    ]
    SYNC ( "by"
      BlockStmt<out proof, out _, out _>
    | ";"                            (. endTok = t; .)
    )
    (. ConcreteAssignStatement assignment;
       var lhsExprs = new List<Expression>();
       if (isGhost || (rhss.Count == 0 && exceptionRhs == null && suchThat == null)) { // explicitly ghost or no init
         foreach (var lhs in lhss) {
           lhsExprs.Add(new IdentifierExpr(lhs.Tok, lhs.Name) { Origin = new SourceOrigin(lhs.StartToken, lhs.EndToken) } );
         }
       } else { // not explicitly ghost, but with init - so auto-ghost
         foreach (var lhs in lhss) {
           lhsExprs.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name) { Origin = new SourceOrigin(lhs.StartToken, lhs.EndToken) } );
         }
       }
       var rangeToken = new SourceOrigin(startToken, t);
       var updateOrigin = new SourceOrigin(assignTok, t);
       if (suchThat != null) {
         assignment = new AssignSuchThatStmt(updateOrigin, lhsExprs, suchThat, suchThatAssume, attrs);
       } else if (exceptionRhs != null) {
         assignment = new AssignOrReturnStmt(updateOrigin, lhsExprs, exceptionRhs, keywordToken, rhss);
       } else if (rhss.Count == 0) {
         assignment = null;
       } else {
         assignment = new AssignStatement(updateOrigin, lhsExprs, rhss);
       }
       s = new VarDeclStmt(rangeToken, lhss, assignment);
       if (proof != null) {
         s = new BlockByProofStmt(s.Origin, proof, s);
       }
    .)
  | (. CasePattern<LocalVariable> pat;
       Expression e = dummyExpr;
       Token id = t;
    .)
    CasePatternLocal<out pat, isGhost>
    ( ":="
    | { Attribute<ref attrs> }
      ":|"                          (.  SemErr(ErrorId.p_no_patterns_and_such_that, pat.Tok, "LHS of assign-such-that expression must be variables, not general patterns"); .)
    )
    Expression<out e, false, true>

    ";"
    (. s = new VarDeclPattern(e.Origin, pat, e, isGhost);
       s.Origin = new SourceOrigin(pat.StartToken, t); .)
  )
  .

/*------------------------------------------------------------------------*/
IfStmt<out Statement/*!*/ ifStmt>
= (. Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); Token/*!*/ x;
     Expression guard = null;  Token guardEllipsis = null;  bool isBindingGuard = false;
     BlockStmt/*!*/ thn;
     BlockStmt/*!*/ bs;
     Statement/*!*/ s;
     Statement els = null;
     Attributes attrs = null;
     Attributes elsattrs = null;
     Token bodyStart, bodyEnd;
     Token endTok;
     Token startToken;
     List<GuardedAlternative> alternatives;
     ifStmt = dummyIfStmt;  // to please the compiler
     bool usesOptionalBraces;
  .)
  "if"                       (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  (
    IF(IsAlternative())
    AlternativeBlock<true, out alternatives, out usesOptionalBraces, out endTok>
    (. ifStmt = new AlternativeStmt(new SourceOrigin(startToken, t), alternatives, usesOptionalBraces, attrs);
    .)
  |
    ( IF(IsBindingGuard())
      BindingGuard<out guard, true>      (. isBindingGuard = true; .)
    | Guard<out guard>
    | ellipsis                           (. guardEllipsis = t;
                                            errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                         .)
    )
    BlockStmt<out thn, out bodyStart, out bodyEnd> 
    [ "else"
      ( IfStmt<out s>                                 (. els = s; .)
      | { Attribute<ref elsattrs> } BlockStmt<out bs, out bodyStart, out bodyEnd> (. els = bs; .)
      )
    ]
    (.
       if (els != null && !(els is IfStmt)) {
        els.Attributes = elsattrs;
       }
       var rangeToken = new SourceOrigin(startToken, t);
       if (guardEllipsis != null) {
         ifStmt = new IfStmt(rangeToken, isBindingGuard, guard, thn, els);
         ifStmt = new SkeletonStatement(ifStmt, guardEllipsis, null);
       } else {
         ifStmt = new IfStmt(rangeToken, isBindingGuard, guard, thn, els, attrs);
       }
    .)
  )
  .

/*------------------------------------------------------------------------*/
AlternativeBlock<.bool allowBindingGuards, out List<GuardedAlternative> alternatives, out bool usesOptionalBraces, out Token endTok.>
= (. alternatives = new List<GuardedAlternative>();
     endTok = null;
     usesOptionalBraces = false;
     GuardedAlternative alt;
  .)
  ( "{"    (. usesOptionalBraces = true; .)
    {
      AlternativeBlockCase<allowBindingGuards, out alt>  (. alternatives.Add(alt); .)
    }
    "}"
  | /* Note, an alternative-less while is not parsed here; it is a body-less while (not an alternative while).
       Also, an alternative-less if is not allowed.
       These decisions save a Coco warning.
    */
    AlternativeBlockCase<allowBindingGuards, out alt>  (. alternatives.Add(alt); .)
    { IF(la.kind == _case)
      AlternativeBlockCase<allowBindingGuards, out alt>  (. alternatives.Add(alt); .)
    }
  )
  (. endTok = t; .)
  .

/*------------------------------------------------------------------------*/
AlternativeBlockCase<.bool allowBindingGuards, out GuardedAlternative alt.>
= (. Token x;
     Expression e; bool isBindingGuard;
     List<Statement> body;
     Attributes attrs = null;
  .)
  "case"                             (. x = t; isBindingGuard = false; e = dummyExpr; .)
  { Attribute<ref attrs> }
  ( IF(allowBindingGuards && IsBindingGuard())
    BindingGuard<out e, false >  (. isBindingGuard = true; .)  // NB: don't allow lambda here
  | Expression<out e, true, false> // NB: don't allow lambda here
  )
  "=>"
  (. body = new List<Statement>(); .)
  SYNC  /* this SYNC and the one inside the loop below are used to avoid problems with the IsNotEndOfCase test. The SYNC will
          * skip until the next symbol that can legally occur here, which is either the beginning of a Stmt or whatever is allowed
          * to follow the CaseStatement.
          */
  { IF(IsNotEndOfCase()) /* This is a little sketchy. It would be nicer to be able to write IF(la is start-symbol of Stmt), but Coco doesn't allow that */
    Stmt<body>
    SYNC  /* see comment about SYNC above */
  }
  (. alt = new GuardedAlternative(x, isBindingGuard, e, body, attrs); .)
  .

/*------------------------------------------------------------------------*/
WhileStmt<out Statement stmt>
= (. Contract.Ensures(Contract.ValueAtReturn(out stmt) != null); Token x;
     Expression guard = null;  Token guardEllipsis = null;
     Attributes attrs = null;
     List<AttributedExpression> invariants = new List<AttributedExpression>();
     List<Expression> decreases = new List<Expression>();
     Token startToken = null;
     Attributes decAttrs = null;
     Attributes modAttrs = null;
     List<FrameExpression> mod = null;
     BlockStmt body = null;  Token bodyEllipsis = null;
     Token bodyStart = null, bodyEnd = null;
     Token endTok;
     List<GuardedAlternative> alternatives;
     stmt = dummyStmt;  // to please the compiler
     bool isDirtyLoop = true;
     bool usesOptionalBraces;
  .)
  "while"                    (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  (
    IF(IsLoopSpec() || IsAlternative())
    LoopSpec<invariants, decreases, ref mod, ref decAttrs, ref modAttrs>
    AlternativeBlock<false, out alternatives, out usesOptionalBraces, out endTok>
    (. stmt = new AlternativeLoopStmt(new SourceOrigin(startToken, t), invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), alternatives, usesOptionalBraces, attrs);
       .)
  |
    ( Guard<out guard>           (. Contract.Assume(guard == null || cce.Owner.None(guard)); .)
    | ellipsis                   (. guardEllipsis = t; 
                                    errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                 .)
    )
    LoopSpec<invariants, decreases, ref mod, ref decAttrs, ref modAttrs>
    ( IF(la.kind == _lbrace)      /* if there's an open brace, claim it as the beginning of the loop body (as opposed to a BlockStmt following the loop) */
      BlockStmt<out body, out bodyStart, out bodyEnd>  (. isDirtyLoop = false; .)
    | IF(la.kind == _ellipsis)    /* if there's an ellipsis, claim it as standing for the loop body (as opposed to a "...;" statement following the loop) */
      ellipsis                   (. bodyEllipsis = t; isDirtyLoop = false;
                                    errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                  .)
    | /* go body-less */
    )
    (.
      var rangeToken = new SourceOrigin(startToken, t);
      if (guardEllipsis != null || bodyEllipsis != null) {
        if (mod != null) {
          SemErr(ErrorId.p_no_modifies_on_refining_loops, mod[0].E.Tok, "'modifies' clauses are not allowed on refining loops");
        }
        if (body == null && !isDirtyLoop) {
          body = new BlockStmt(rangeToken, new List<Statement>());
        }
        stmt = new WhileStmt(rangeToken, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(null, null), body, attrs);
        stmt = new SkeletonStatement(stmt, guardEllipsis, bodyEllipsis);
      } else {
        // The following statement protects against crashes in case of parsing errors
        if (body == null && !isDirtyLoop) {
          body = new BlockStmt(rangeToken, new List<Statement>());
        }
        stmt = new WhileStmt(rangeToken, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), body, attrs);
      }
    .)
  )
  .

ForLoopStmt<out Statement stmt>
= (. Contract.Ensures(Contract.ValueAtReturn(out stmt) != null);
     Token x;
     BoundVar loopIndex;
     Expression start;
     Expression end = null;
     bool goingUp = true;

     Attributes attrs = null;

     List<AttributedExpression> invariants = new List<AttributedExpression>();
     List<Expression> decreases = new List<Expression>();
     Token startToken = null;
     Attributes decAttrs = null;
     Attributes modAttrs = null;
     List<FrameExpression> mod = null;

     BlockStmt body = null;
     Token bodyStart = null, bodyEnd = null;
     stmt = dummyStmt;  // to please the compiler
     bool isDirtyLoop = true;
  .)
  "for"                    (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  IdentTypeOptional<out loopIndex>
  ":="
  Expression<out start, false, false>
  ForLoopDirection<out goingUp>
  ( Expression<out end, false, false>
  | "*"
  )
  LoopSpec<invariants, decreases, ref mod, ref decAttrs, ref modAttrs>
  ( IF(la.kind == _lbrace)      /* if there's an open brace, claim it as the beginning of the loop body (as opposed to a BlockStmt following the loop) */
    BlockStmt<out body, out bodyStart, out bodyEnd>  (. isDirtyLoop = false; .)
  | /* go body-less */
  )
  (.
    var rangeToken = new SourceOrigin(startToken, t);
    // The following statement protects against crashes in case of parsing errors
    if (body == null && !isDirtyLoop) {
      body = new BlockStmt(rangeToken, new List<Statement>());
    }
    stmt = new ForLoopStmt(rangeToken, loopIndex, start, end, goingUp,
      invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), body, attrs);
  .)
  .

ForLoopDirection<out bool goingUp>
= (. goingUp = true; .)
  ident
  (. if (t.val == "downto") {
       goingUp = false;
     } else if (t.val != "to") {
       SemErr(ErrorId.p_to_or_downto, t, "Expected 'to' or 'downto'");
     }
  .)
  .

/*------------------------------------------------------------------------*/
LoopSpec<.List<AttributedExpression> invariants, List<Expression> decreases, ref List<FrameExpression> mod, ref Attributes decAttrs, ref Attributes modAttrs.>
= { SYNC
    InvariantClause<invariants>
  | SYNC
    DecreasesClause<decreases, ref decAttrs, true, true>
  | SYNC
    ModifiesClause<ref mod, ref modAttrs, true, false>
  }
  .

/*------------------------------------------------------------------------*/
DecreasesList<.List<Expression> decreases, bool allowWildcard, bool allowLambda.>
= (. Expression e; .)
  PossiblyWildExpression<out e, allowLambda, allowWildcard>       (. decreases.Add(e); .)
  { "," PossiblyWildExpression<out e, allowLambda, allowWildcard> (. decreases.Add(e); .)
  }
  (. if (allowWildcard && decreases.Count > 1 && decreases.Exists(e => e is WildcardExpr)) {
       SemErr(ErrorId.p_no_decreases_expressions_with_star, e.Tok, "A 'decreases' clause that contains '*' is not allowed to contain any other expressions");
     }
  .)
  .

/*------------------------------------------------------------------------*/
Guard<out Expression e>   /* null represents demonic-choice */
= (. Expression/*!*/ ee;  e = null; .)
  ( "*"                                   (. e = null; .)
  | IF(IsParenStar())  "(" "*" ")"        (. e = null; .)
  | Expression<out ee, true, true>        (. e = ee; .)
  )
  .

/*------------------------------------------------------------------------*/
BindingGuard<out Expression e, bool allowLambda>
= (. var bvars = new List<BoundVar>();
     BoundVar bv;  
     Token x;
     Attributes attrs = null;
     Expression body;
  .)
  IdentTypeOptional<out bv>                    (. bvars.Add(bv); x = bv.StartToken; .)
  { ","
    IdentTypeOptional<out bv>                  (. bvars.Add(bv); .)
  }
  { Attribute<ref attrs> }
  ":|"
  Expression<out body, true, allowLambda>
  (. e = new ExistsExpr(x, new SourceOrigin(x, t), bvars, null, body, attrs); .)
  .


/*------------------------------------------------------------------------*/
SingleExtendedPattern<.out ExtendedPattern pat.>
= (. Token id; List<ExtendedPattern> arguments;
    Expression lit; BoundVar bv;
    pat = null;
  .)
  ( "("                                    (. id = t;
                                              arguments = new List<ExtendedPattern>(); .)
      [ ExtendedPattern<out pat>           (. arguments.Add(pat); .)
      { "," ExtendedPattern<out pat>       (. arguments.Add(pat); .)
      }]
    ")"                                    (. // make sure the tuple type exists
                                              SystemModuleModifiers.Add(b => b.TupleType(id, arguments.Count, true));
                                              //use the TupleTypeCtors
                                              string ctor = SystemModuleManager.TupleTypeCtorName(arguments.Count);
                                              pat = new IdPattern(id, ctor, arguments);
                                           .)
  | IF(IsIdentParen())
    Ident<out id>                          (. arguments = new List<ExtendedPattern>(); .)
    "("
      [ ExtendedPattern<out pat>           (. arguments.Add(pat); .)
        { "," ExtendedPattern<out pat>     (. arguments.Add(pat); .)
        }
      ]
     ")"
                                           (. pat = new IdPattern(id, id.val, arguments, false, true); .)
  | PossiblyNegatedLiteralExpr<out lit>    (. pat = new LitPattern(lit.Tok, lit); .)
  | IdentTypeOptional<out bv>              (. pat = new IdPattern(bv.Tok, bv.Name, bv.SyntacticType, null); .)
  )
  (. // In case of parsing errors, make sure 'pat' still returns as non-null
    if (pat == null) {
      pat = new IdPattern(t, "_ParseError", null);
    }
  .)
  .

/*------------------------------------------------------------------------*/
ExtendedPattern<.out ExtendedPattern pat.>
= (. List<ExtendedPattern> branches = null;
     ExtendedPattern branch = null; .)
  [ "|" ] SingleExtendedPattern<out branch>
  { "|"                                    (. branches ??= new() { branch }; .)
    SingleExtendedPattern<out branch>      (. branches.Add(branch); .)
  }
  (. pat = branches == null ? branch : new DisjunctivePattern(branches[0].Tok, branches); .)
  .

/*------------------------------------------------------------------------*/
MatchStmt<out Statement/*!*/ s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
     Token x; Expression/*!*/ e; NestedMatchCaseStmt/*!*/ c;
     List<NestedMatchCaseStmt/*!*/> cases = new List<NestedMatchCaseStmt/*!*/>();
     bool usesOptionalBraces = false;
     Token startToken = null;
     Attributes attrs = null;
  .)
  "match"                     (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  Expression<out e, true, true>
  ( IF(la.kind == _lbrace)  /* always favor brace-enclosed match body to a case-less match */
    "{" (. usesOptionalBraces = true; .)
        { CaseStmt<out c> (. cases.Add(c); .) }
    "}"
  |     { IF(la.kind == _case)  /* let each "case" bind to the closest preceding "match" */
          CaseStmt<out c> (. cases.Add(c); .)
        }
  )
  (. s = new NestedMatchStmt(new SourceOrigin(startToken, t), e, cases, usesOptionalBraces, attrs);
  .)
  .

/*------------------------------------------------------------------------*/
CaseStmt<out NestedMatchCaseStmt/*!*/ c>
= (. Contract.Ensures(Contract.ValueAtReturn(out c) != null);
     Token/*!*/ x;
     ExtendedPattern/*!*/ pat = null;
     var body = new List<Statement/*!*/>();
     Attributes attrs = null;
  .)
  "case"                      (. x = t; .)
  { Attribute<ref attrs> }
  ExtendedPattern<out pat>    (. .)
  "=>"
    SYNC  /* this SYNC and the one inside the loop below are used to avoid problems with the IsNotEndOfCase test. The SYNC will
           * skip until the next symbol that can legally occur here, which is either the beginning of a Stmt or whatever is allowed
           * to follow the CaseStatement.
           */
    { IF(IsNotEndOfCase()) /* This is a little sketchy. It would be nicer to be able to write IF(la is start-symbol of Stmt), but Coco doesn't allow that */
      Stmt<body>
      SYNC  /* see comment about SYNC above */
    }
  (. c = new NestedMatchCaseStmt(x, pat, body, attrs); .)
  .

/*------------------------------------------------------------------------*/
AssertStmt<out Statement/*!*/ s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); Token/*!*/ x;
     Expression e = dummyExpr; Attributes attrs = null;
     Token dotdotdot = null;
     BlockStmt proof = null;
     Token startToken = null;
     Token proofStart, proofEnd;
     Token lbl = null;
  .)
  "assert"                                     (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  ( [ IF(IsLabel(true))
      LabelName<out lbl> ":"
    ]
    Expression<out e, false, true>
    ( "by"
      BlockStmt<out proof, out proofStart, out proofEnd>
    | ";"
    | (.
        SemErr(ErrorId.p_assert_needs_by_or_semicolon, t, "expected either 'by' or a semicolon following the assert expression");
      .)
    )
  | ellipsis                                   (. dotdotdot = t; 
                                                  errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                                .)
    ";"
  )
  (. if (dotdotdot != null) {
       s = new AssertStmt(new SourceOrigin(startToken, t), new LiteralExpr(x, true), null, attrs);
       s = new SkeletonStatement(s, dotdotdot, null);
     } else {
       s = new AssertStmt(new SourceOrigin(startToken, t), e, lbl == null ? null : new AssertLabel(lbl, lbl.val), attrs);
       if (proof != null) {
         s = new BlockByProofStmt(s.Origin, proof, s);
       }
     }
  .)
  .

/*------------------------------------------------------------------------*/
ExpectStmt<out Statement/*!*/ s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); Token/*!*/ x;
     Expression e = dummyExpr; Expression m = null; Attributes attrs = null;
     Token dotdotdot = null;
     Token startToken = null;
  .)
  "expect"                                     (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  ( Expression<out e, false, true>
  | ellipsis                                   (. dotdotdot = t; 
                                                  errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                                .)
  )
  [ "," Expression<out m, false, true> ]
  ";"
  (. if (dotdotdot != null) {
       s = new ExpectStmt(new SourceOrigin(startToken, t), new LiteralExpr(x, true), m, attrs);
       s = new SkeletonStatement(s, dotdotdot, null);
     } else {
       s = new ExpectStmt(new SourceOrigin(startToken, t), e, m, attrs);
     }
  .)
  .

/*------------------------------------------------------------------------*/
AssumeStmt<out Statement/*!*/ s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); Token/*!*/ x;
     Expression e = dummyExpr; Attributes attrs = null;
     Token dotdotdot = null;
     Token startToken = null;
  .)
  "assume"                                     (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  ( Expression<out e, false, false>
  | ellipsis                                   (. dotdotdot = t; 
                                                  errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                                .)
  )
  ";"
  (. if (dotdotdot != null) {
       s = new AssumeStmt(new SourceOrigin(startToken, t), new LiteralExpr(x, true), attrs);
       s = new SkeletonStatement(s, dotdotdot, null);
     } else {
       s = new AssumeStmt(new SourceOrigin(startToken, t), e, attrs);
     }
  .)
  .

/*------------------------------------------------------------------------*/
PrintStmt<out Statement s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
     Token x;  Expression e;
     var args = new List<Expression>();
     Token startToken = null;
  .)
  "print"                                      (. x = t; startToken = t; .)
  Expression<out e, false, true>               (. args.Add(e); .)
  { "," Expression<out e, false, true>         (. args.Add(e); .)
  }
  ";"                                          (.
    s = new PrintStmt(new SourceOrigin(startToken, t), args);
    .)
  .

/*------------------------------------------------------------------------*/
RevealStmt<out Statement s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
     Token x; Expression e; var es = new List<Expression>();
     Token startToken = null;
     HideRevealCmd.Modes mode = HideRevealCmd.Modes.Reveal;
     s = null;
  .)
  ( "reveal"                                    (. x = t; startToken = t; mode = HideRevealCmd.Modes.Reveal; .)
  | "hide"                                      (. x = t; startToken = t; mode = HideRevealCmd.Modes.Hide; .)
  )
  ( Expression<out e, false, true>                (. es.Add(e); .)
    { "," Expression<out e, false, true>          (. es.Add(e); .)
    }                                           (. s = new HideRevealStmt(new SourceOrigin(startToken, t), es, mode); .)
  | "*"                                         (. s = new HideRevealStmt(new SourceOrigin(startToken, t), mode); .)
  )
  ";"                                           
  .

/*------------------------------------------------------------------------*/
ForallStmt<out Statement s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
     List<BoundVar> bvars = null;
     Attributes qattrs = null;
     Expression range = null;
     Token startToken = null;
  .)
  "forall"                                  (. startToken = t; .)

  ( IF(la.kind == _openparen)  /* disambiguation needed, because of the possibility of a body-less forall statement */
    "("   (. errors.Deprecated(ErrorId.p_deprecated_forall_statement_with_parentheses_around_bound_variables, t,
               "parentheses around the forall-statement bound variables are deprecated and not needed");
          .)
    [ QuantifierDomain<out bvars, out qattrs, out range, true, true, true> ]
    ")"
  | [ IF(IsIdentifier(la.kind))  /* disambiguation needed, because of the possibility of a body-less forall statement */
      QuantifierDomain<out bvars, out qattrs, out range, true, true, true>
    ]
  )
  (. if (bvars == null) {
       errors.Deprecated(ErrorId.p_deprecated_forall_with_no_bound_variables, startToken,
       "a forall statement with no bound variables is deprecated; use an 'assert by' statement instead");
     }
     bvars ??= new List<BoundVar>();
  .)

  ForallStatementEnsuresAndBody<out s, startToken, bvars, qattrs, range>
  .

ForallStatementEnsuresAndBody<.out Statement s, Token startToken, List<BoundVar> bvars, Attributes qattrs, Expression/*?*/ range.>
= (. var ens = new List<AttributedExpression/*!*/>();
     BlockStmt block = null;
     range ??= new LiteralExpr(startToken, true);
  .)
  {
    EnsuresClause<ens, true>
  }
  [ IF(la.kind == _lbrace)  /* if the input continues like a block statement, take it to be the body of the forall statement; a body-less forall statement must continue in some other way */
    BlockStmt<out block, out _, out _>
  ]
  (. if (theOptions.DisallowSoundnessCheating && block == null && 0 < ens.Count) {
        SemErr(ErrorId.p_forall_with_ensures_must_have_body, t, "a forall statement with an ensures clause must have a body");
     }

     s = new ForallStmt(new SourceOrigin(startToken, t), bvars, qattrs, range, ens, block);
  .)
  .

/*------------------------------------------------------------------------*/
ModifyStmt<out Statement s>
= (. Token tok;  Token endTok = Token.NoToken;
     Attributes attrs = null;
     FrameExpression fe;  var mod = new List<FrameExpression>();
     BlockStmt body = null;  Token bodyStart;
     Token ellipsisToken = null;
     Token startToken = null;
  .)
  "modify"           (. tok = t; startToken = t; .)
  { Attribute<ref attrs> }
  /* Note, there is an ambiguity here, because a curly brace may look like a FrameExpression and
   * may also look like a BlockStmt.  We're happy to parse the former, because if the user intended
   * the latter, then an explicit FrameExpression of {} could be given.
   */
  ( FrameExpression<out fe, false, true>       (. mod.Add(fe); .)
    { "," FrameExpression<out fe, false, true> (. mod.Add(fe); .)
    }
  | ellipsis                            (. ellipsisToken = t; 
                                           errors.Deprecated(ErrorId.p_deprecated_statement_refinement, t, "the ... refinement feature in statements is deprecated");
                                         .)
  )
  ( BlockStmt<out body, out bodyStart, out endTok>
                                        (. errors.Deprecated(ErrorId.p_deprecated_modify_statement_with_block, bodyStart, "the modify statement with a block statement is deprecated");
                                         .)
  | SYNC ";"         (. endTok = t; .)
  )
  (. s = new ModifyStmt(new SourceOrigin(startToken, t), mod, attrs, body);
     if (ellipsisToken != null) {
       s = new SkeletonStatement(s, ellipsisToken, null);
     }
  .)
  .

/*------------------------------------------------------------------------*/
CalcStmt<out Statement s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
     Token x;
     Attributes attrs = null;
     CalcStmt.CalcOp op, userSuppliedOp = null, resOp = Microsoft.Dafny.CalcStmt.DefaultOp;
     var lines = new List<Expression>();
     var hints = new List<BlockStmt>();
     CalcStmt.CalcOp stepOp;
     var stepOps = new List<CalcStmt.CalcOp>();
     Token startToken = null;
     Expression e;
     Token opTok;
     Token danglingOperator = null;
  .)
  "calc"                                                  (. x = t; startToken = t; .)
  { Attribute<ref attrs> }
  [ CalcOp<out opTok, out userSuppliedOp>                 (. if (userSuppliedOp.ResultOp(userSuppliedOp) == null) { // guard against non-transitive calcOp (like !=)
                                                               SemErr(ErrorId.p_calc_operator_must_be_transitive, opTok, "the main operator of a calculation must be transitive");
                                                             } else {
                                                               resOp = userSuppliedOp;
                                                             }
                                                          .)
  ]
  "{"
  { Expression<out e, false, true>                        (. lines.Add(e); stepOp = null; danglingOperator = null; .)
    ";"
    [ CalcOp<out opTok, out op>                           (. var maybeOp = resOp.ResultOp(op);
                                                             if (maybeOp == null) {
                                                               SemErr(ErrorId.p_invalid_calc_op_combination, opTok, "this operator cannot continue this calculation");
                                                             } else {
                                                               stepOp = op;
                                                               resOp = maybeOp;
                                                               danglingOperator = opTok;
                                                             }
                                                          .)
    ]                                                     (. stepOps.Add(stepOp); .)

    /* now for the hint, which we build up as a possibly empty sequence of statements placed into one BlockStmt */
    (. var subhints = new List<Statement>();
       Token hintStart = la;  
       Token hintEnd = hintStart.Prev;
       Token t0, t1;
       BlockStmt subBlock; Statement subCalc;
    .)
    { IF(la.kind == _lbrace || la.kind == _calc)  /* Grab as a hint if possible, not a next line in the calculation whose expression begins with an open brace
                                                   * or StmtExpr containing a calc.  A user has to rewrite such a line to be enclosed in parentheses.
                                                   */
      ( BlockStmt<out subBlock, out t0, out t1>   (. hintEnd = subBlock.EndToken; subhints.Add(subBlock); .)
      | CalcStmt<out subCalc>                     (. hintEnd = subCalc.EndToken; subhints.Add(subCalc); .)
      )
    }
    (. var h = new BlockStmt(new SourceOrigin(hintStart, hintEnd), subhints); // if the hint is empty, hintStart is the first token of the next line, but it doesn't matter because the block statement is just used as a container
       hints.Add(h);
       if (h.Body.Count != 0) { danglingOperator = null; }
    .)
  }
  "}"
  (.
    if (danglingOperator != null) {
      SemErr(ErrorId.p_calc_dangling_operator, danglingOperator, "a calculation cannot end with an operator");
    }
    if (lines.Count > 0) {
      // Repeat the last line to create a dummy line for the dangling hint
      lines.Add(lines[lines.Count - 1]);
    }
    s = new CalcStmt(new SourceOrigin(startToken, t), userSuppliedOp, lines, hints, stepOps, attrs);
  .)
  .

/*------------------------------------------------------------------------*/
CalcOp<out Token x, out CalcStmt.CalcOp/*!*/ op>
= (. var binOp = BinaryExpr.Opcode.Eq; // Returns Eq if parsing fails because it is compatible with any other operator
     Expression k = null;
     x = null;
  .)
  ( "=="           (. x = t;  binOp = BinaryExpr.Opcode.Eq; .)
    [ "#" "[" Expression<out k, true, true> "]" ]
  | "<"            (. x = t;  binOp = BinaryExpr.Opcode.Lt; .)
  | ">"            (. x = t;  binOp = BinaryExpr.Opcode.Gt; .)
  | "<="           (. x = t;  binOp = BinaryExpr.Opcode.Le; .)
  | ">="           (. x = t;  binOp = BinaryExpr.Opcode.Ge; .)
  | "!="           (. x = t;  binOp = BinaryExpr.Opcode.Neq; .)
  | EquivOp        (. x = t;  binOp = BinaryExpr.Opcode.Iff; .)
  | ImpliesOp      (. x = t;  binOp = BinaryExpr.Opcode.Imp; .)
  | ExpliesOp      (. x = t;  binOp = BinaryExpr.Opcode.Exp; .)
  )
  (.
    if (k == null) {
      op = new Microsoft.Dafny.CalcStmt.BinaryCalcOp(binOp);
    } else {
      op = new Microsoft.Dafny.CalcStmt.TernaryCalcOp(k);
    }
  .)
  .

/*------------------------------------------------------------------------*/
/* Note. In order to avoid LL(1) warnings for expressions that "parse as far as possible", it is
 * necessary to use Coco/R's IF construct.  That means there are two ways to check for some of
 * these operators, both in Is...() methods (defined above) and as grammar non-terminals (defined
 * here).  These pairs of definitions must be changed together.
 */
EquivOp = "<==>".
ImpliesOp = "==>".
ExpliesOp = "<==".
AndOp = "&&".
OrOp = "||".

NegOp = "!".
Forall = "forall".
Exists = "exists".
QSep = "::".

/* The "allowLemma" argument says whether or not the expression
 * to be parsed is allowed to have the form S;E where S is a call to a lemma.
 * "allowLemma" should be passed in as "false" whenever the expression to
 * be parsed sits in a context that itself is terminated by a semi-colon.
 *
 * The "allowLambda" says whether or not the expression to be parsed is
 * allowed to be a lambda expression.  More precisely, an identifier or
 * parenthesized-enclosed comma-delimited list of identifiers is allowed to
 * continue as a lambda expression (that is, continue with a "reads", "requires",
 * or "=>") only if "allowLambda" is true.  This affects function/method/iterator
 * specifications, if/while statements with guarded alternatives, and expressions
 * in the specification of a lambda expression itself.
 *
 * The "allowBitwiseOps" says whether or not to include or bypass bitwise operators
 * at the top level of this expression. It is passed in as "false" only inside
 * cardinality brackets, that is, "|expr|".
 */
Expression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps = true, bool allowDecreasesTo = true>
= (. Expression e0; Token semiToken; .)
  [ "new" (. SemErr(ErrorId.p_no_side_effects_in_expressions, t, "Calls with side-effects such as constructors are not allowed in expressions."); .) ]
  EquivExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  [ IF(allowDecreasesTo && IsDecreasesTo())
    DecreasesTo<out var decreasesToToken, out var allowNoChange>
    EquivExpression<out e0, allowLemma, allowLambda, allowBitwiseOps>
    (. e = new DecreasesToExpr(decreasesToToken, new List<Expression>() { e }, new List<Expression>() { e0 }, allowNoChange); .)
  ]
  [ IF(SemiFollowsCall(allowLemma, e))
    /* here we parse the ";E" that is part of a "LemmaCall;E" expression (other "S;E" expressions are parsed elsewhere) */
    ";"                       (. semiToken = t; .)
    Expression<out e0, allowLemma, allowLambda>
    (. var startToken = e.StartToken;
       e = new StmtExpr(e0.Tok,
             new AssignStatement(e.Origin, new List<Expression>(), new List<AssignmentRhs>() { new ExprRhs(e) }),
             e0);
       e.Origin = new SourceOrigin(startToken, e0.EndToken);
    .)
  ]
  .

/*------------------------------------------------------------------------*/
EquivExpression<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); 
     Token/*!*/ x;  
     Expression/*!*/ e1; 
  .)
  ImpliesExpliesExpression<out e0, allowLemma, allowLambda, allowBitwiseOps>
  { IF(IsEquivOp())  /* read an EquivExpression as far as possible */
    EquivOp                                                   (. x = t; .)
    ImpliesExpliesExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>  (.
      var startToken = e0.StartToken;
      e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1);
      e0.Origin = new SourceOrigin(startToken, t); .)
  }
  .

/*------------------------------------------------------------------------*/
ImpliesExpliesExpression<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null);
     Token/*!*/ x;
     Expression/*!*/ e1; .)
  LogicalExpression<out e0, allowLemma, allowLambda, allowBitwiseOps>
  [ IF(IsImpliesOp() || IsExpliesOp())  /* read an ImpliesExpliesExpression as far as possible */
    /* Note, the asymmetry in the parsing of implies and explies expressions stems from the fact that
     * implies is right associative whereas reverse implication is left associative
     */
    ( ImpliesOp                                               (. x = t; .)
      ImpliesExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>       (. var startToken = e0.StartToken;
                                                                                   e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1);
                                                                                   e0.Origin = new SourceOrigin(startToken, t); .)
    | ExpliesOp                                               (. x = t; .)
      LogicalExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>       (. // The order of operands is reversed so that it can be turned into implication during resolution
                                                                 var startToken = e0.StartToken;
                                                                 e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0);
                                                                 e0.Origin = new SourceOrigin(startToken, t); .)
      { IF(IsExpliesOp())  /* read a reverse implication as far as possible */
        ExpliesOp                                             (. x = t; .)
        LogicalExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>     (. //The order of operands is reversed so that it can be turned into implication during resolution
                                                                 startToken = e0.StartToken;
                                                                 e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0);
                                                                 e0.Origin = new SourceOrigin(startToken, t);
                                                              .)
      }
      [ IF(IsImpliesOp()) ImpliesOp (. SemErr(ErrorId.p_ambiguous_implies, t, "Ambiguous use of ==> and <==. Use parentheses to disambiguate."); .) 
        LogicalExpression<out e1, allowLemma, allowLambda, allowBitwiseOps> { IF(IsImpliesOp() || IsExpliesOp()) ((ImpliesOp | ExpliesOp) LogicalExpression<out e1, allowLemma, allowLambda, allowBitwiseOps> ) } // resynch after error
      ]
    )
  ]
  .

/*------------------------------------------------------------------------*/
ImpliesExpression<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); Token/*!*/ x;  Expression/*!*/ e1; .)
  LogicalExpression<out e0, allowLemma, allowLambda, allowBitwiseOps>
  [ IF(IsImpliesOp() || IsExpliesOp())
    ( ImpliesOp | ExpliesOp (. SemErr(ErrorId.p_ambiguous_implies_2, t, "Ambiguous use of ==> and <==. Use parentheses to disambiguate."); .) )
    (. x = t; .)
    ImpliesExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>       (. var startToken = e0.StartToken;
                                                                                 e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1); 
                                                                                 e0.Origin = new SourceOrigin(startToken, t);.)
  ]
  .

/*------------------------------------------------------------------------*/
LogicalExpression<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); Token/*!*/ x = null;  Expression/*!*/ e1;
     Expression first;
     Token startToken = null;
     Token/*!*/ firstOp = null;
     Token/*!*/ prefixOp = null;
     e0 = dummyExpr; /* mute the warning */
  .)
    // This extra AndOp will belong to the first binary expression
  [ (AndOp | OrOp) (. firstOp = t; prefixOp = t; x = t; .) ]
  RelationalExpression<out e0, allowLemma, allowLambda, allowBitwiseOps> (. first = e0; .)

  { IF(IsAndOp() || IsOrOp())  /* read a LogicalExpression as far as possible */
    ( AndOp | OrOp ) (. x = t; 
                        if (firstOp == null) firstOp = t; 
                        if (firstOp.kind != t.kind) SemErr(ErrorId.p_ambiguous_and_or, t, "Ambiguous use of && and ||. Use parentheses to disambiguate.");
                     .)
    RelationalExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>
         (. startToken = e0.StartToken;
            e0 = new BinaryExpr(x, firstOp.val == "&&" ? BinaryExpr.Opcode.And : BinaryExpr.Opcode.Or, e0, e1);
            e0.Origin = new SourceOrigin(startToken, t);
         .)
  }
  (.
       if (e0 == first && prefixOp != null) {
         // There was only one conjunct. To make sure that the type checker still checks it to
         // be a boolean, we conjoin "true" of "false" to its left.
         if (firstOp.val == "&&") e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, new LiteralExpr(new AutoGeneratedOrigin(x), true), e0);
         else e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, new LiteralExpr(new AutoGeneratedOrigin(x), false), e0);
         e0.Origin = new SourceOrigin(firstOp, t);
         e0.FormatTokens = new[] { firstOp };
       } else if(prefixOp != null) {
         e0.Origin = new SourceOrigin(prefixOp, e0.EndToken);
       }
  .)
  .

/*------------------------------------------------------------------------*/
RelationalExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
     Token x = null;  Expression e0, e1 = null;  BinaryExpr.Opcode op;
     List<Expression> chain = null;
     List<BinaryExpr.Opcode> ops = null;
     List<IOrigin> opLocs = null;
     List<Expression/*?*/> prefixLimits = null;
     Token startToken = null;
     Expression k;
     int kind = 0;  // 0 ("uncommitted") indicates chain of ==, possibly with one !=
                    // 1 ("ascending")   indicates chain of ==, <, <=, possibly with one !=
                    // 2 ("descending")  indicates chain of ==, >, >=, possibly with one !=
                    // 3 ("illegal")     indicates illegal chain
                    // 4 ("disjoint")    indicates chain of disjoint set operators
     bool hasSeenNeq = false;
  .)
  ShiftTerm<out e0, allowLemma, allowLambda, allowBitwiseOps>
                                   (. e = e0; startToken = e0.StartToken; .)
  [ IF(IsRelOp())  /* read a RelationalExpression as far as possible */
    RelOp<out x, out op, out k>
    ShiftTerm<out e1, allowLemma, allowLambda, allowBitwiseOps>
                                   (. startToken = e0.StartToken;
                                      if (k == null) {
                                        e = new BinaryExpr(x, op, e0, e1);
                                      } else {
                                        Contract.Assert(op == BinaryExpr.Opcode.Eq || op == BinaryExpr.Opcode.Neq);
                                        e = new TernaryExpr(x, op == BinaryExpr.Opcode.Eq ? TernaryExpr.Opcode.PrefixEqOp : TernaryExpr.Opcode.PrefixNeqOp, k, e0, e1);
                                      }
                                      e.Origin = new SourceOrigin(startToken, t);
                                   .)
    { IF(IsRelOp())  /* read a RelationalExpression as far as possible */
                                   (. if (chain == null) {
                                        chain = new List<Expression>();
                                        ops = new List<BinaryExpr.Opcode>();
                                        opLocs = new List<IOrigin>();
                                        prefixLimits = new List<Expression>();
                                        chain.Add(e0); ops.Add(op); opLocs.Add(x); prefixLimits.Add(k); chain.Add(e1);
                                        switch (op) {
                                          case BinaryExpr.Opcode.Eq:
                                            kind = 0;  break;
                                          case BinaryExpr.Opcode.Neq:
                                            kind = 0;  hasSeenNeq = true;  break;
                                          case BinaryExpr.Opcode.Lt:
                                          case BinaryExpr.Opcode.Le:
                                            kind = 1;  break;
                                          case BinaryExpr.Opcode.Gt:
                                          case BinaryExpr.Opcode.Ge:
                                            kind = 2;  break;
                                          case BinaryExpr.Opcode.Disjoint:
                                            kind = 4;  break;
                                          default:
                                            kind = 3;  break;
                                        }
                                      }
                                   .)
      RelOp<out x, out op, out k>  (. switch (op) {
                                        case BinaryExpr.Opcode.Eq:
                                          if (kind != 0 && kind != 1 && kind != 2) { SemErr(ErrorId.p_invalid_equal_chaining, x, "chaining not allowed from the previous operator"); kind = 3; }
                                          break;
                                        case BinaryExpr.Opcode.Neq:
                                          if (hasSeenNeq) { SemErr(ErrorId.p_invalid_notequal_chaining, x, "a chain cannot have more than one != operator"); kind = 3; }
                                          else if (kind != 0 && kind != 1 && kind != 2) { SemErr(ErrorId.p_invalid_operator_in_chain, x, "this operator cannot continue this chain"); kind = 3; }
                                          hasSeenNeq = true;  break;
                                        case BinaryExpr.Opcode.Lt:
                                        case BinaryExpr.Opcode.Le:
                                          if (kind == 0) { kind = 1; }
                                          else if (kind != 1) { SemErr(ErrorId.p_invalid_descending_chaining, x, "this operator chain cannot continue with an ascending operator"); kind = 3; }
                                          break;
                                        case BinaryExpr.Opcode.Gt:
                                        case BinaryExpr.Opcode.Ge:
                                          if (kind == 0) { kind = 2; }
                                          else if (kind != 2) { SemErr(ErrorId.p_invalid_ascending_chaining, x, "this operator chain cannot continue with a descending operator"); kind = 3; }
                                          break;
                                        case BinaryExpr.Opcode.Disjoint:
                                          if (kind != 4) { SemErr(ErrorId.p_invalid_disjoint_chaining, x, "can only chain disjoint (!!) with itself"); kind = 3; }
                                          break;
                                        default:
                                          SemErr(ErrorId.p_operator_does_not_chain, x, "this operator cannot be part of a chain");
                                          kind = 3;  break;
                                      }
                                   .)
      ShiftTerm<out e1, allowLemma, allowLambda, allowBitwiseOps>
                                   (. ops.Add(op); opLocs.Add(x); prefixLimits.Add(k); chain.Add(e1);
                                   .)
    }
  ]
  (. if (chain != null && kind != 3) {
       e = new ChainingExpression(opLocs[0], chain, ops, opLocs, prefixLimits);
     }
     e.Origin = new SourceOrigin(startToken, t);
  .)
  .

/*------------------------------------------------------------------------*/
RelOp<out Token/*!*/ x, out BinaryExpr.Opcode op, out Expression k>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null);
     x = Token.NoToken;  op = BinaryExpr.Opcode.Add/*(dummy)*/;
     Token y;
     k = null;
  .)
  ( "=="           (. x = t;  op = BinaryExpr.Opcode.Eq; .)
    [ "#" "[" Expression<out k, true, true> "]" ]
  | "<"            (. x = t;  op = BinaryExpr.Opcode.Lt;  .)
  | ">"            (. x = t;  op = BinaryExpr.Opcode.Gt;  .)
  | "<="           (. x = t;  op = BinaryExpr.Opcode.Le;  .)
  | ">="           (. x = t;  op = BinaryExpr.Opcode.Ge;  .)
  | "!="           (. x = t;  op = BinaryExpr.Opcode.Neq;  .)
    [ "#" "[" Expression<out k, true, true> "]" ]
  | "in"           (. x = t;  op = BinaryExpr.Opcode.In; .)
  | notIn          (. x = t;  op = BinaryExpr.Opcode.NotIn; .)
  | /* The next operator is "!!", but we have to scan it as two "!", since the scanner is greedy
       so if "!!" is a valid token, we won't be able to scan it as two "!" when needed: */
    "!"            (. x = t;  y = Token.NoToken; .)
    [ IF(la.val == "!")
      "!"          (. y = t; .)
    ]              (. if (y == Token.NoToken) {
                        SemErr(ErrorId.p_bang_not_a_relational_op, x, "invalid relational operator");
                      } else if (y.pos != x.pos + 1) {
                        SemErr(ErrorId.p_invalid_relational_op, new SourceOrigin(x,y), "invalid relational operator (perhaps you intended \"!!\" with no intervening whitespace?)");
                      } else {
                        x.val = "!!";
                        y.val = "";
                        op = BinaryExpr.Opcode.Disjoint;
                      }
                   .)
   )
  .

/*------------------------------------------------------------------------*/
ShiftTerm<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null);
     Token x = Token.NoToken;  Expression e1;  BinaryExpr.Opcode op = BinaryExpr.Opcode.LeftShift/*(dummy)*/;
  .)
  Term<out e0, allowLemma, allowLambda, allowBitwiseOps>
  { IF(IsShiftOp())  /* read a Term as far as possible */
    ( "<"            (. x = t;  op = BinaryExpr.Opcode.LeftShift; .)
      "<"            (. t.val = "<<";
                        t.pos = x.pos;
                        t.col = x.col;
                        t.Prev = x.Prev;
                        x.Prev.Next = t;
                        x = t; .)
    | ">"            (. x = t;  op = BinaryExpr.Opcode.RightShift; .)
      ">"            (. t.val = ">>";
                        t.pos = x.pos;
                        t.col = x.col;
                        t.Prev = x.Prev;
                        x.Prev.Next = t;
                        x = t; .)
    )
    Term<out e1, allowLemma, allowLambda, allowBitwiseOps> (.
      var startToken = e0.StartToken;
      e0 = new BinaryExpr(x, op, e0, e1);
      e0.Origin = new SourceOrigin(startToken, t);
     .)
  }
  .

/*------------------------------------------------------------------------*/
Term<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); Token/*!*/ x;  Expression/*!*/ e1;  BinaryExpr.Opcode op; .)
  Factor<out e0, allowLemma, allowLambda, allowBitwiseOps>
  { IF(IsAddOp())  /* read a Term as far as possible */
    AddOp<out x, out op>
    Factor<out e1, allowLemma, allowLambda, allowBitwiseOps>
    (. var startToken = e0.StartToken;
       e0 = new BinaryExpr(x, op, e0, e1);
       e0.Origin = new SourceOrigin(startToken, t); .)
  }
  .

/*------------------------------------------------------------------------*/
AddOp<out Token x, out BinaryExpr.Opcode op>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); x = Token.NoToken;  op=BinaryExpr.Opcode.Add/*(dummy)*/; .)
  ( "+"            (. x = t;  op = BinaryExpr.Opcode.Add; .)
  | "-"            (. x = t;  op = BinaryExpr.Opcode.Sub; .)
  )
  .

/*------------------------------------------------------------------------*/
Factor<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); Token/*!*/ x;  Expression/*!*/ e1;  BinaryExpr.Opcode op; .)
  BitvectorFactor<out e0, allowLemma, allowLambda, allowBitwiseOps>
  { IF(IsMulOp())  /* read a Factor as far as possible */
    MulOp<out x, out op>
    BitvectorFactor<out e1, allowLemma, allowLambda, allowBitwiseOps>
    (. 
      var startToken = e0.StartToken;
      e0 = new BinaryExpr(x, op, e0, e1);
      e0.Origin = new SourceOrigin(startToken, t);
    .)
  }
  .

/*------------------------------------------------------------------------*/
MulOp<out Token x, out BinaryExpr.Opcode op>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); x = Token.NoToken;  op = BinaryExpr.Opcode.Add/*(dummy)*/; .)
  ( "*"            (. x = t;  op = BinaryExpr.Opcode.Mul; .)
  | "/"            (. x = t;  op = BinaryExpr.Opcode.Div; .)
  | "%"            (. x = t;  op = BinaryExpr.Opcode.Mod; .)
  )
  .

/*------------------------------------------------------------------------*/
BitvectorFactor<out Expression e0, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); Token/*!*/ x = null;  Expression/*!*/ e1;  BinaryExpr.Opcode op; bool ambig = false; op = BinaryExpr.Opcode.BitwiseAnd; .)
  AsExpression<out e0, allowLemma, allowLambda, allowBitwiseOps>
  [ IF(allowBitwiseOps && IsBitwiseOp())  /* read a BitvectorFactor as far as possible, but not in the context inside a |.| size expression */
    ( "&"                                                (. op = BinaryExpr.Opcode.BitwiseAnd; x = t; .)
    | "|"                                                (. op = BinaryExpr.Opcode.BitwiseOr; x = t; .)
    | "^"                                                (. op = BinaryExpr.Opcode.BitwiseXor; x = t; .)
    )
    AsExpression<out e1, allowLemma, allowLambda, allowBitwiseOps>   (. var startToken = e0.StartToken; e0 = new BinaryExpr(x, op, e0, e1); e0.Origin = new SourceOrigin(startToken, t); .)
    { IF(IsBitwiseOp())
      ( "&" | "|" | "^" )                                (. if (x.kind != t.kind && !ambig) { ambig = true; SemErr(ErrorId.p_ambiguous_bitop, t, "Ambiguous use of &, |, ^. Use parentheses to disambiguate."); }
                                                            x = t; .)
      AsExpression<out e1, allowLemma, allowLambda, allowBitwiseOps> (. startToken = e0.StartToken; e0 = new BinaryExpr(x, op, e0, e1); e0.Origin = new SourceOrigin(startToken, t); .)
    }
  ]
  .

/*------------------------------------------------------------------------*/
AsExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Token tok; Token x; Type toType; .)
  UnaryExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  { IF(IsAsOrIs())
    ( "as"                                  (. tok = t; .)
      TypeAndToken<out x, out toType, true> (. e = new ConversionExpr(tok, e, toType) { Origin = new SourceOrigin(e.StartToken, t)}; .)
    | "is"                                  (. tok = t; .)
      TypeAndToken<out x, out toType, true> (. e = new TypeTestExpr(tok, e, toType) { Origin = new SourceOrigin(e.StartToken, t)}; .)
    )
  }
  .

/*------------------------------------------------------------------------*/
UnaryExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); Token/*!*/ x;  e = dummyExpr; .)
  ( "-"                                             (. x = t; .)
    UnaryExpression<out e, allowLemma, allowLambda, allowBitwiseOps>  (. e = new NegationExpression(x, e) { Origin = new SourceOrigin(x, t) }; .)
  | NegOp                                           (. x = t; .)
    UnaryExpression<out e, allowLemma, allowLambda, allowBitwiseOps>  (. e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Not, e) { Origin = new SourceOrigin(x, t) }; .)
  | PrimaryExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  )
  .

/*------------------------------------------------------------------------*/
PrimaryExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr; .)
  ( IF(ExprIsMapDisplay())  /* this alternative must be checked before going into EndlessExpression, where there is another "map" */
    MapDisplayExpr<out e>
    { IF(IsSuffix()) Suffix<ref e> }
  | IF(ExprIsSetDisplay())  /* this alternative must be checked before going into EndlessExpression, where there is another "iset" */
    SetDisplayExpr<out e>
    { IF(IsSuffix()) Suffix<ref e> }
  | IF(IsLambda(allowLambda))
    LambdaExpression<out e, allowLemma, allowBitwiseOps>  /* this is an endless expression */
  | EndlessExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  | NameSegment<out e>
    { IF(IsSuffix()) Suffix<ref e> }
  | SeqDisplayExpr<out e>
    { IF(IsSuffix()) Suffix<ref e> }
  | ConstAtomExpression<out e>
    { IF(IsSuffix()) Suffix<ref e> }
  )
  .

/*------------------------------------------------------------------------*/
Lhs<out Expression e>
= (. e = dummyExpr;  // the assignment is to please the compiler, the dummy value to satisfy contracts in the event of a parse error
  .)
  ( NameSegment<out e>
    { Suffix<ref e> }
  | ConstAtomExpression<out e>
    Suffix<ref e>
    { Suffix<ref e> }
  )
  .

/*------------------------------------------------------------------------*/
/* A ConstAtomExpression is never an l-value, and does not start with an identifier. */
ConstAtomExpression<out Expression e>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
     Token/*!*/ x = null;
     e = dummyExpr;
  .)
  ( LiteralExpression<out e>
  | "this"                                     (. x = t; e = new ThisExpr(t); .)
  | "allocated"                                (. x = t; .)
    "(" Expression<out e, true, true> ")"      (. e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Allocated, e); .)
  | "fresh"                                    (. x = t; Token atLabel = null; .)
    [ "@" LabelName<out atLabel> ]
    "(" Expression<out e, true, true> ")"      (. e = new FreshExpr(x, e, atLabel?.val); .)
  | "unchanged"                                (. x = t; FrameExpression fe; var mod = new List<FrameExpression>(); Token atLabel = null; .)
    [ "@" LabelName<out atLabel> ]
    "("
      FrameExpression<out fe, false, false>         (. mod.Add(fe); .)
      { "," FrameExpression<out fe, false, false>   (. mod.Add(fe); .)
      }
    ")"                                        (. e = new UnchangedExpr(x, mod, atLabel?.val); .)
  | "old"                                      (. x = t; Token atLabel = null; .)
    [ "@" LabelName<out atLabel> ]
    "(" Expression<out e, true, true> ")"      (. e = new OldExpr(x, e, atLabel?.val); .)
  | "|"                                        (. x = t; .)
      Expression<out e, true, true, false>     (. e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Cardinality, e); .)
    "|"
  | "assigned"                                 (. x = t; .)
    "(" NameSegment<out e> ")"                 (. e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Assigned, e); .)
  | ParensExpression<out e>
  ) (. if(x!= null) { e.Origin = new SourceOrigin(x, t); } .)
  .

/*------------------------------------------------------------------------*/
LiteralExpression<out Expression e>
= (. BigInteger n; BaseTypes.BigDec d;
     e = dummyExpr;
  .)
  ( "false"                                    (. e = new LiteralExpr(t, false); .)
  | "true"                                     (. e = new LiteralExpr(t, true); .)
  | "null"                                     (. e = new LiteralExpr(t); .)
  | Nat<out n>                                 (. e = new LiteralExpr(t, n); .)
  | Dec<out d>                                 (. e = new LiteralExpr(t, d); .)
  | charToken                                  (. string s = t.val.Substring(1, t.val.Length - 2);
                                                  Util.ValidateEscaping(theOptions, t, s, false, errors);
                                                  if (Util.UnescapedCharacters(theOptions, s, false).Count() > 1) {
                                                    errors.SemErr(ErrorId.p_invalid_char_literal, t, "too many characters in character literal");
                                                  }
                                                  e = new CharLiteralExpr(t, s); .)
  | stringToken                                (. bool isVerbatimString;
                                                  string s = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
                                                  Util.ValidateEscaping(theOptions, t, s, isVerbatimString, errors);
                                                  e = new StringLiteralExpr(t, s, isVerbatimString);
                                               .)
  ) (. e.Origin = new SourceOrigin(t, t); .)
  .

/*------------------------------------------------------------------------*/
PossiblyNegatedLiteralExpr<out Expression e>
= (. BigInteger n; BaseTypes.BigDec d;
     e = dummyExpr;
  .)
  ( "-"                                        (. var x = t; .)
    ( Nat<out n>                               (. e = new NegationExpression(x, new LiteralExpr(t, n)); .)
    | Dec<out d>                               (. e = new NegationExpression(x, new LiteralExpr(t, d)); .)
    )
  | LiteralExpression<out e>
  )
  .

/*------------------------------------------------------------------------*/
LambdaExpression<out Expression e, bool allowLemma, bool allowBitwiseOps>
= (. Token x = Token.NoToken;
     Token id;  BoundVar bv;
     var bvs = new List<BoundVar>();
     var reads = new List<FrameExpression>();
     Attributes readsAttrs = null;
     Expression req = null;
     Expression body = null;
  .)
  ( WildIdent<out id, true>                  (. x = t; bvs.Add(new BoundVar(id, id.val, new InferredTypeProxy()) {Origin = new SourceOrigin(x, x)}); .)
  | "("                                      (. x = t; .)
      [
        IdentTypeOptional<out bv>            (. bvs.Add(bv); .)
        { "," IdentTypeOptional<out bv>      (. bvs.Add(bv); .)
        }
      ]
    ")"
  )
  LambdaSpec<ref reads, ref readsAttrs, ref req>
  "=>"
  Expression<out body, allowLemma, true, allowBitwiseOps>
  (. e = new LambdaExpr(x, new SourceOrigin(x, t), bvs, req, new Specification<FrameExpression>(reads, readsAttrs), body);
     SystemModuleModifiers.Add(b => b.CreateArrowTypeDecl(bvs.Count));
  .)
  .

// Coco says LambdaSpec is deletable. This is OK (it might be empty).
LambdaSpec<.ref List<FrameExpression> reads, ref Attributes readsAttrs, ref Expression req.>
= { ReadsClause<reads, ref readsAttrs, true, false, true>
  | "requires"                             (. Expression ee; .)
    Expression<out ee, true, false>        (. req = req == null ? ee : new BinaryExpr(req.Tok, BinaryExpr.Opcode.And, req, ee) { Origin = new SourceOrigin(req.StartToken, ee.EndToken) } ; .)
  }
  .

/*------------------------------------------------------------------------*/
ParensExpression<out Expression e>
= (. Token lp = null; Token rp = null;
     var args = new List<ActualBinding>();
     e = dummyExpr;
     var isDecreasesToExpr = false;
     Token decreasesToToken = null;
     var allowNoChange = false;
     List<Expression> oldExprs = null;
     List<Expression> newExprs = null;
  .)
  "("                                       (. lp = t; .)
  ( ")"                                     (. rp = t; .)

  | (. isDecreasesToExpr = true;
       oldExprs = new();
    .)
    DecreasesTo<out decreasesToToken, out allowNoChange>
    DecreasesToExpressionList<out newExprs>
    ")"                                     (. rp = t; .)

  | TupleArgs<args>
    [ (. isDecreasesToExpr = true; .)
      DecreasesTo<out decreasesToToken, out allowNoChange>
      (. foreach (var binding in args) {
           if (binding.FormalParameterName != null) {
             SemErr(ErrorId.p_binding_in_decreases_to, binding.FormalParameterName,
               $"bindings are not allowed in `{decreasesToToken.val} to` expressions.");
           } else if (binding.IsGhost) {
             SemErr(ErrorId.p_ghost_in_decreases_to, binding.Actual.Tok, // we don't have the token of "ghost" :(
               $"'ghost' components are not allowed in `{decreasesToToken.val} to` expressions.");
           }
         }
         oldExprs = args.Select(arg => arg.Actual).ToList();
      .)
      DecreasesToExpressionList<out newExprs>
    ]
    ")"                                     (. rp = t; .)
  )

  (. if (isDecreasesToExpr) {
       e = new DecreasesToExpr(decreasesToToken, oldExprs, newExprs, allowNoChange);
     } else {
       e = ProcessTupleArgs(args, lp);
       if (e is not DatatypeValue) { // here, a DatatypeValue is a tuple expression
         e = new ParensExpression(lp, e);
       }
     }
     e.Origin = new SourceOrigin(lp, rp);
  .)
  .

TupleArgs<.List<ActualBinding> args.>
= (. ActualBinding binding; bool isGhost = false; .)
  // The IF is to distinguish between `(ghost var x := 5; x + x)` and (ghost x), both of which begin with `( ghost`.
  [ IF(la.kind == _ghost && !IsPeekVar())
    "ghost"                                     (. isGhost = true; .)
  ]
  ActualBinding<out binding, isGhost, false>    (. args.Add(binding); .)
  { ","                                         (. isGhost = false; .)
    [ IF(la.kind == _ghost && !IsPeekVar())
      "ghost"                                   (. isGhost = true; .)
    ]
    ActualBinding<out binding, isGhost, false>  (. args.Add(binding); .)
  }
  .

// the DecreasesTo production can be anticipated by IsDecreasesTo()
DecreasesTo<out Token x, out bool allowNoChange>
= (. allowNoChange = false; .)
  ( "decreases"
  | "nonincreases"                (. allowNoChange = true; .)
  )                               (. x = t; .)
  ident
  (. if (t.val != "to") {
       SemErr(ErrorId.p_decreases_without_to, t, "expected 'to'");
     }
  .)
  .

DecreasesToExpressionList<.out List<Expression> newExprs.>
= (. newExprs = new();
     Expression expr;
  .)
  [ Expression<out expr, false, false>                   (. newExprs.Add(expr); .)
    { ","
      Expression<out expr, false, false>                 (. newExprs.Add(expr); .)
    }
  ]
  .

/*------------------------------------------------------------------------*/
SetDisplayExpr<out Expression e>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
     Token token = null;
     Token x = null, startToken = null;
     e = dummyExpr;
  .)
  [ ( "iset" | "multiset" )                  (. token = t; startToken = t; x = t; .)
  ]
  ( "{"                                      (. token = token ?? t;
                                                startToken = startToken ?? t;
                                                x = token; 
                                                List<Expression> elements = new List<Expression/*!*/>();
                                             .)
    [ Expressions<elements> ]                (. if (token.kind == _iset) {
                                                  e = new SetDisplayExpr(x, false, elements);
                                                } else if (token.kind == _multiset) {
                                                  e = new MultiSetDisplayExpr(x, elements);
                                                } else {
                                                  e = new SetDisplayExpr(x, true, elements);
                                                }
                                             .)
    "}"
  |
    "("                                       (. x = t; startToken = startToken ?? t; .)
     Expression<out e, true, true>            (.
                                                  if (token == null || token.kind != _multiset) {
                                                    SemErr(ErrorId.p_must_be_multiset, x, "A forming expression must be a multiset");
                                                  }
                                                  e = new MultiSetFormingExpr(x, e);
                                               .)
    ")"
  )
  (. e.Origin = new SourceOrigin(startToken, t); .)
  .

/*------------------------------------------------------------------------*/
SeqDisplayExpr<out Expression e>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
     Token x = null;
     Token startInstantiation = null;
     Type explicitTypeArg = null;
     Expression n, f;
     e = dummyExpr;
  .)
  (
    "seq"                          (. x = t; .)
    [ (. var gt = new List<Type>(); startInstantiation = la; .)
    GenericInstantiation<gt>     (. if (gt.Count > 1) {
                                      SemErr(ErrorId.p_seq_display_has_one_type_argument, new SourceOrigin(startInstantiation,t), "seq type expects only one type argument");
                                    } else {
                                      explicitTypeArg = gt[0];
                                    }
                                 .)
    ]
    "("
    Expression<out n, true, true>
    ","
    Expression<out f, true, true>
    ")"                            (. e = new SeqConstructionExpr(x, explicitTypeArg, n, f); .)
  |
    "["                                      (. List<Expression> elements = new List<Expression/*!*/>();
                                                x = t;

                                             .)
    [ Expressions<elements> ]                (. e = new SeqDisplayExpr(x, elements);
                                             .)
    "]"
  )
  (.  e.Origin = new SourceOrigin(x, t); .)
  .

/*------------------------------------------------------------------------*/
MapDisplayExpr<out Expression e>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
     List<ExpressionPair/*!*/>/*!*/ elements= new List<ExpressionPair/*!*/>() ;
     e = dummyExpr;
  .)
  ( "map" | "imap" ) (. Token mapToken = t; .)
  "["
    [ MapLiteralExpressions<out elements> ] 
                     (. e = new MapDisplayExpr(mapToken, mapToken.kind == _map, elements);.)
  "]"                (. e.Origin = new SourceOrigin(mapToken, t); .)
  .

/*------------------------------------------------------------------------*/
MapLiteralExpressions<.out List<ExpressionPair> elements.>
= (. Expression/*!*/ d, r;
     elements = new List<ExpressionPair/*!*/>();
  .)
  Expression<out d, true, true> ":=" Expression<out r, true, true>       (. elements.Add(new ExpressionPair(d,r)); .)
  { "," Expression<out d, true, true> ":=" Expression<out r, true, true> (. elements.Add(new ExpressionPair(d,r)); .)
  }
  .

/*------------------------------------------------------------------------*/
MapComprehensionExpr<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
     List<BoundVar> bvars = new List<BoundVar>();
     Expression range = null;
     Expression bodyLeft = null;
     Expression bodyRight;
     Attributes attrs = null;
     bool finite = true;
  .)
  ( "map" | "imap" (. finite = false; .) )     (. Token mapToken = t; .)
  QuantifierDomain<out bvars, out attrs, out range, true, true, true>
  QSep
  Expression<out bodyRight, allowLemma, allowLambda, allowBitwiseOps>
  [ IF(IsGets())  /* greedily parse ":=" */    (. bodyLeft = bodyRight; .)
    ":=" Expression<out bodyRight, allowLemma, allowLambda, allowBitwiseOps || !finite>
  ]
  (. if (bodyLeft == null && bvars.Count != 1) {
       SemErr(ErrorId.p_map_comprehension_must_have_term_expression, t, "a map comprehension with more than one bound variable must have a term expression of the form 'Expr := Expr'");
       e = dummyExpr;
     } else {
       e = new MapComprehension(mapToken, new SourceOrigin(mapToken, t), finite, bvars, range ?? new LiteralExpr(Token.NoToken, true), bodyLeft, bodyRight, attrs);
     }
  .)
  .

/*------------------------------------------------------------------------*/
EndlessExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Statement s;
     e = dummyExpr;
  .)
  ( IfExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  | MatchExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  | QuantifierExpression<out e, allowLemma, allowLambda>  /* types are such that we can allow bitwise operations in the quantifier body */
  | SetComprehensionExpr<out e, allowLemma, allowLambda, allowBitwiseOps>
  | StmtInExpr<out s>
    Expression<out e, allowLemma, allowLambda, allowBitwiseOps>
    (. e = new StmtExpr(s.Tok, s, e) { Origin = new SourceOrigin(s.StartToken, e.EndToken) }; .)
  | LetExpression<out e, allowLemma, allowLambda, allowBitwiseOps>
  | MapComprehensionExpr<out e, allowLemma, allowLambda, allowBitwiseOps>
  )
  .

/*------------------------------------------------------------------------*/
IfExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= "if"   (. Token x = t; Expression e0; Expression e1;
            bool isBindingGuard = false;
            e = dummyExpr;
         .)
      ( IF(IsBindingGuard())
        BindingGuard<out e, true>  (. isBindingGuard = true; .)
      | Expression<out e, true, true>
      )
      "then" Expression<out e0, true, true, true>
      "else" Expression<out e1, allowLemma, allowLambda, allowBitwiseOps>
                (. if (isBindingGuard) {
                     var exists = (ExistsExpr) e;
                     var LHSs = new List<CasePattern<BoundVar>>();
                     foreach (var v in exists.BoundVars) {
                       LHSs.Add(new CasePattern<BoundVar>(e.Tok, v));
                     }
                     e0 = new LetExpr(e.Tok, LHSs, new List<Expression>() {
                       exists.Term }, e0, false) { Origin = exists.Origin};
                 }
                 e = new ITEExpr(x, isBindingGuard, e, e0, e1);
                 e.Origin = new SourceOrigin(x, t);
                .)
  .
/*------------------------------------------------------------------------*/
StmtInExpr<out Statement s>
= (. s = dummyStmt; .)
  ( AssertStmt<out s>
  | ExpectStmt<out s>
  | AssumeStmt<out s>
  | RevealStmt<out s>
  | CalcStmt<out s>
  )
  .

/*------------------------------------------------------------------------*/
LetExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. e = dummyExpr; .)
  ( LetExprWithLHS<out e, allowLemma, allowLambda, allowBitwiseOps>
  | LetExprWithoutLHS<out e, allowLemma, allowLambda, allowBitwiseOps>
  ).

/*------------------------------------------------------------------------*/
LetExprWithLHS<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Token x = null;
     bool isGhost = false;
     var letLHSs = new List<CasePattern<BoundVar>>();
     var letRHSs = new List<Expression>();
     Token lastLHS = null;
     Token lastRHS = null;
     CasePattern<BoundVar> pat;
     bool exact = true;
     bool isLetOrFail = false;
     Attributes attrs = null;
     e = dummyExpr;
  .)
    [ "ghost"                       (. isGhost = true;  x = t; .)
    ]
    "var"                           (. if (!isGhost) { x = t; } .)
    CasePattern<out pat>            (. if (isGhost) { pat.Vars.ForEach(bv => bv.IsGhost = true); }
                                       letLHSs.Add(pat);
                                       lastLHS = la;
                                    .)
    { "," CasePattern<out pat>      (. if (isGhost) { pat.Vars.ForEach(bv => bv.IsGhost = true); }
                                       letLHSs.Add(pat);
                                    .)
    }
    ( ":=" 
    | { Attribute<ref attrs> }
      ":|"                          (. exact = false;
                                       foreach (var lhs in letLHSs) {
                                         if (lhs.Arguments != null) {
                                           SemErr(ErrorId.p_no_patterns_in_let_such_that, lhs.Tok, "LHS of let-such-that expression must be variables, not general patterns");
                                         }
                                       }
                                    .)
    | ":-"                          (. isLetOrFail = true; .)
    | "=" (. SemErr(ErrorId.p_no_equal_in_let_initialization, t, "a variable in a let expression should be initialized using ':=', ':-', or ':|', not '='"); .)
    )
    Expression<out e, false, true>        (. letRHSs.Add(e); lastRHS = la; .)
    { "," Expression<out e, false, true>  (. letRHSs.Add(e); .)
    }
    ";"
    Expression<out e, allowLemma, allowLambda, allowBitwiseOps>
  (.
    if (isLetOrFail) {
      CasePattern<BoundVar> lhs = null;
      Contract.Assert(letLHSs.Count > 0);
      if (letLHSs.Count == 1) {
        lhs = letLHSs[0];
      } else {
        var erange = new SourceOrigin(letLHSs[1].StartToken.Prev, letLHSs[^1].EndToken);
        SemErr(ErrorId.p_elephant_has_one_lhs, erange, "':-' can have at most one left-hand side");
      }
      Expression rhs = null;
      Contract.Assert(letRHSs.Count > 0);
      if (letRHSs.Count == 1) {
        rhs = letRHSs[0];
      } else {
        var erange = new SourceOrigin(letRHSs[0].EndToken.Next, letRHSs[^1].EndToken);
        SemErr(ErrorId.p_elephant_has_one_rhs, erange, "':-' must have exactly one right-hand side");
      }
      e = new LetOrFailExpr(x, lhs, rhs, e);
    } else {
      e = new LetExpr(x, letLHSs, letRHSs, e, exact, attrs);
    }
    e.Origin = new SourceOrigin(x, t);
  .)
  .

/*------------------------------------------------------------------------*/
LetExprWithoutLHS<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Token x;
     Expression rhs;
     Expression body;
  .)
  ":-"                                    (. x = t; .)
  Expression<out rhs, false, true>
  ";"
  Expression<out body, allowLemma, allowLambda, allowBitwiseOps>
  (. e = new LetOrFailExpr(x, null, rhs, body);
     e.Origin = new SourceOrigin(x, t);
   .)
  .

/*------------------------------------------------------------------------*/
MatchExpression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); Token/*!*/ x;  NestedMatchCaseExpr/*!*/ c;
     List<NestedMatchCaseExpr/*!*/> cases = new List<NestedMatchCaseExpr/*!*/>();
     bool usesOptionalBraces = false;
  .)
  "match"                     (. x = t; .)
  Expression<out e, allowLemma, allowLambda, allowBitwiseOps>
  ( IF(la.kind == _lbrace)  /* always favor brace-enclosed match body to a case-less match */
    "{" (. usesOptionalBraces = true; .)
        { CaseExpression<out c, true, true, allowBitwiseOps> (. cases.Add(c); .) }
    "}"
  |     { IF(la.kind == _case)  /* let each "case" bind to the closest preceding "match" */
          CaseExpression<out c, allowLemma, allowLambda, allowBitwiseOps> (. cases.Add(c); .)
        }
  )
  (. e = new NestedMatchExpr(x, e, cases, usesOptionalBraces);
     e.Origin = new SourceOrigin(x, t); .)
.

/*------------------------------------------------------------------------*/
CaseExpression<out NestedMatchCaseExpr c, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out c) != null); Token/*!*/ x;
     ExtendedPattern/*!*/ pat = null;
     Expression/*!*/ body;
     Attributes attrs = null;
  .)
  "case"                      (. x = t; .)
  { Attribute<ref attrs> }
  ExtendedPattern<out pat>             (. .)
  "=>"
  Expression<out body, allowLemma, allowLambda, allowBitwiseOps>    (. c = new NestedMatchCaseExpr(x, pat, body, attrs); .)
.

/*------------------------------------------------------------------------*/
CasePattern<.out CasePattern<BoundVar> pat.>
= (. Token id;  List<CasePattern<BoundVar>> arguments;
     BoundVar bv;
     pat = null;
  .)
  ( IF(IsIdentParen())
    Ident<out id>
    "("                                (. arguments = new List<CasePattern<BoundVar>>(); .)
      [ CasePattern<out pat>           (. arguments.Add(pat); .)
        { "," CasePattern<out pat>     (. arguments.Add(pat); .)
        }
      ]
    ")"                                (. pat = new CasePattern<BoundVar>(id, id.val, arguments); .)
  | "("                                (. id = t;
                                          arguments = new List<CasePattern<BoundVar>>();
                                       .)
      [ CasePattern<out pat>           (. arguments.Add(pat); .)
        { "," CasePattern<out pat>     (. arguments.Add(pat); .)
        }
      ]
    ")"                                (. // Parse parenthesis without an identifier as a built in tuple type.
                                          string ctor = SystemModuleManager.TupleTypeCtorName(arguments.Count);  //use the TupleTypeCtors
                                          pat = new CasePattern<BoundVar>(id, ctor, arguments);
                                       .)
  | IdentTypeOptional<out bv>          (. // This could be a BoundVar of a parameter-less constructor and we may not know until resolution.
                                          // Nevertheless, we do put the "bv" into the CasePattern here (even though it will get thrown out
                                          // later if resolution finds the CasePattern to denote a parameter-less constructor), because this
                                          // (in particular, bv.IsGhost) is the place where a LetExpr records whether or not the "ghost"
                                          // keyword was used in the declaration.
                                          pat = new CasePattern<BoundVar>(bv.Tok, bv);
                                       .)
  )
  (. // In case of parsing errors, make sure 'pat' still returns as non-null
     if (pat == null) {
       pat = new CasePattern<BoundVar>(t, "_ParseError", null);
     }
  .)
  .

/*------------------------------------------------------------------------*/
/* CasePatternLocal is identical to CasePattern, except that it uses LocalVariable instead of BoundVar. Coco does
 * not have a way to make the patterns take a bounded type parameter.
 */
CasePatternLocal<.out CasePattern<LocalVariable> pat, bool isGhost.>
= (. Token id; List<CasePattern<LocalVariable>> arguments;
     LocalVariable local;
     pat = null;
  .)
  ( IF(IsIdentParen())
    Ident<out id>
    "("                                          (. arguments = new List<CasePattern<LocalVariable>>(); .)
      [ CasePatternLocal<out pat, isGhost>       (. arguments.Add(pat); .)
        { "," CasePatternLocal<out pat, isGhost> (. arguments.Add(pat); .)
        }
      ]
    ")"                                          (. pat = new CasePattern<LocalVariable>(id, id.val, arguments); .)
  | "("                                          (. id = t;
                                                    arguments = new List<CasePattern<LocalVariable>>();
                                                 .)
      [ CasePatternLocal<out pat, isGhost>       (. arguments.Add(pat); .)
        { "," CasePatternLocal<out pat, isGhost> (. arguments.Add(pat); .)
        }
      ]
    ")"                                (. // Parse parenthesis without an identifier as a built in tuple type.
                                          string ctor = SystemModuleManager.TupleTypeCtorName(arguments.Count);  //use the TupleTypeCtors
                                          pat = new CasePattern<LocalVariable>(id, ctor, arguments);
                                       .)
  | LocalIdentTypeOptional<out local, isGhost>
                                       (. // This could be a LocalVariable of a parameter-less constructor and we may not know until resolution.
                                          // Nevertheless, we do put the local" into the CasePattern here (even though it will get thrown out
                                          // later if resolution finds the CasePattern to denote a parameter-less constructor), because this
                                          // (in particular, local.IsGhost) is the place where a LetExpr records whether or not the "ghost"
                                          // keyword was used in the declaration.
                                          pat = new CasePattern<LocalVariable>(local.Tok, local);
                                       .)
  )
  (. // In case of parsing errors, make sure 'pat' still returns as non-null
     if (pat == null) {
       pat = new CasePattern<LocalVariable>(t, "_ParseError", null);
     }
  .)
  .

/*------------------------------------------------------------------------*/
NameSegment<out Expression e>
= (. Token id;
     Token openParen = null; Token atLabel = null;
     List<Type> typeArgs = null; List<ActualBinding> args = null;
  .)
  Ident<out id>
  ( IF(IsGenericInstantiation(true))
    (. typeArgs = new List<Type>(); .)
    GenericInstantiation<typeArgs>
    [ IF(IsAtCall())
      AtCall<out atLabel, out openParen, out args> ]
  | HashCall<id, out openParen, out typeArgs, out args>
  | [ IF(IsAtCall())
      AtCall<out atLabel, out openParen, out args> ]
  )
  /* Note, since HashCall updates id.val, we make sure not to use id.val until after the possibility of calling HashCall. */
  (. e = new NameSegment(id, id.val, typeArgs);
     if (openParen != null) {
       e = new ApplySuffix(openParen, atLabel, e, args, t);
     } else {
       Contract.Assert(atLabel == null);
     }
     e.Origin = new SourceOrigin(id, t);
  .)
  .

/*------------------------------------------------------------------------*/
/* NameSegmentForTypeName is like the production NameSegment, except that it does not allow HashCall */
NameSegmentForTypeName<out Expression e, bool inExpressionContext>
= (. Token id;  List<Type> typeArgs; .)
  Ident<out id>
  OptGenericInstantiation<out typeArgs, inExpressionContext>
  (. e = new NameSegment(id, id.val, typeArgs);
     e.Origin = new SourceOrigin(id, t);
  .)
  .

/*------------------------------------------------------------------------*/
/* The HashCall production extends a given identifier with a hash sign followed by
 * a list of argument expressions.  That is, if what was just parsed was an identifier id,
 * then the HashCall production will continue parsing into id#[arg](args).
 * One could imagine parsing just the id# as an expression, but Dafny doesn't do that
 * since the first argument to a prefix predicate/method is textually set apart; instead
 * if a programmer wants to curry the arguments, one has to resort to using a lambda
 * expression, just like for other function applications.
 * Note: This grammar production mutates the id.val field to append the hash sign.
 */
HashCall<.Token id, out Token openParen, out List<Type> typeArgs, out List<ActualBinding> args.>
= (. Expression k; args = new List<ActualBinding>(); typeArgs = null; .)
  "#"                                      (. id.val = id.val + "#"; t.val = ""; t.pos = t.pos+1; t.col = t.col+1; .)
  [                                        (. typeArgs = new List<Type>(); .)
    GenericInstantiation<typeArgs>
  ]
  "[" Expression<out k, true, true> "]"    (. args.Add(new ActualBinding(null, k)); .)
  "("                                      (. openParen = t; .)
    [ ActualBindings<args> ]
  ")"
  .

/*------------------------------------------------------------------------*/
AtCall<.out Token atLabel, out Token openParen, out List<ActualBinding> args.>
= (. atLabel = null;
     openParen = null;
     args = new List<ActualBinding>();
  .)
  "@" LabelName<out atLabel>
  "("                                      (. openParen = t; .)
    [ ActualBindings<args> ]
  ")"
  .

/*------------------------------------------------------------------------*/
Suffix<ref Expression e>
= (. Contract.Requires(e != null); Contract.Ensures(e!=null);
     Token id, x;
     Expression e0 = null;  Expression e1 = null;  Expression ee;  bool anyDots = false;
     List<Expression> multipleLengths = null; bool takeRest = false; // takeRest is relevant only if multipleLengths is non-null
     List<Expression> multipleIndices = null;
     List<Tuple<IOrigin, string, Expression>> updates;
     var startToken = e.StartToken;
     Expression v;
  .)
  ( "."
    ( "("                                             (. x = t; updates = new List<Tuple<IOrigin, string, Expression>>(); .)
        MemberBindingUpdate<out id, out v>            (. updates.Add(Tuple.Create((IOrigin)id, id.val, v)); .)
        { "," MemberBindingUpdate<out id, out v>      (. updates.Add(Tuple.Create((IOrigin)id, id.val, v)); .)
        }
      ")"
      (. e = new DatatypeUpdateExpr(x, e, updates) { Origin = new SourceOrigin(startToken, t)}; .)
    | DotSuffix<out id, out x>                 (. if (x != null) {
                                                    // process id as a Suffix in its own right
                                                    e = new ExprDotName(id, e, new Name(id), null) { Origin = new SourceOrigin(startToken, t)};
                                                    id = x;  // move to the next Suffix
                                                  }
                                                  Token openParen = null;  List<Type> typeArgs = null;
                                                  List<ActualBinding> args = null;
                                                  Token atLabel = null;
                                               .)

      ( IF(IsGenericInstantiation(true))
        (. typeArgs = new List<Type>(); .)
        GenericInstantiation<typeArgs>
        [ IF(IsAtCall())
          AtCall<out atLabel, out openParen, out args> ]
      | HashCall<id, out openParen, out typeArgs, out args>
      | [ IF(IsAtCall())
          AtCall<out atLabel, out openParen, out args> ]
      )
      (. e = new ExprDotName(id, e, new Name(id), typeArgs) {
           Origin = new SourceOrigin(startToken, id)
         };
         if (openParen != null) {
           e = new ApplySuffix(openParen, atLabel, e, args, t) {
             Origin = new SourceOrigin(startToken, t)
           };
         } else {
           Contract.Assert(atLabel == null);
         }
      .)
    )
  | "["                                        (. x = t; .)
      ( Expression<out ee, true, true>         (. e0 = ee; .)
        ( ".."                                 (. anyDots = true; .)
          [ Expression<out ee, true, true>     (. e1 = ee; .)
          ]
        | ":="
          Expression<out ee, true, true>       (. e1 = ee; .)
        | ":"                                  (. multipleLengths = new List<Expression>();
                                                  multipleLengths.Add(e0);  // account for the Expression read before the colon
                                                  takeRest = true;
                                               .)
          [ Expression<out ee, true, true>     (. multipleLengths.Add(ee); takeRest = false; .)
            { IF(IsNonFinalColon())
              ":"
              Expression<out ee, true, true>   (. multipleLengths.Add(ee); .)
            }
            [ ":"                              (. takeRest = true; .)
            ]
          ]
        | { "," Expression<out ee, true, true> (. if (multipleIndices == null) {
                                                    multipleIndices = new List<Expression>();
                                                    multipleIndices.Add(e0);
                                                  }
                                                  multipleIndices.Add(ee);
                                               .)
          }
        )
      | ".."                                   (. anyDots = true; .)
        [ Expression<out ee, true, true>       (. e1 = ee; .)
        ]
      )
    "]"
      (. if (multipleIndices != null) {
           e = new MultiSelectExpr(x, e, multipleIndices);
           // make sure an array class with this dimensionality exists
           SystemModuleModifiers.Add(b => b.ArrayType(multipleIndices.Count, new IntType(), true));
         } else {
           if (!anyDots && e0 == null) {
             /* a parsing error occurred */
             e0 = dummyExpr;
           }
           Contract.Assert(anyDots || e0 != null);
           if (anyDots) {
             //Contract.Assert(e0 != null || e1 != null);
             e = new SeqSelectExpr(x, false, e, e0, e1, t);
           } else if (multipleLengths != null) {
             Expression prev = null;
             List<Expression> seqs = new List<Expression>();
              foreach (var len in multipleLengths) {
                var end = prev == null ? len : new BinaryExpr(x, BinaryExpr.Opcode.Add, prev, len) {
                  Origin = new SourceOrigin(prev.StartToken, len.EndToken)
                };
                seqs.Add(new SeqSelectExpr(x, false, e, prev, end, t) {
                  Origin = new SourceOrigin(e.StartToken, t)
                });
                prev = end;
              }
             if (takeRest) {
               seqs.Add(new SeqSelectExpr(x, false, e, prev, null, t) {
                 Origin = new SourceOrigin(e.StartToken, t)
               });
             }
             e = new SeqDisplayExpr(x, seqs);
           } else if (e1 == null) {
             Contract.Assert(e0 != null);
             e = new SeqSelectExpr(x, true, e, e0, null, t);
           } else {
             Contract.Assert(e0 != null);
             e = new SeqUpdateExpr(x, e, e0, e1);
           }
         }
         e.Origin = new SourceOrigin(startToken, t);
      .)
  | "("                                    (. Token openParen = t; var args = new List<ActualBinding>(); .)
    [ ActualBindings<args> ]
    ")"                                    (. e = new ApplySuffix(openParen, null, e, args, t);
                                              e.Origin = new SourceOrigin(startToken, t);
                                           .)
  )
  .

/*------------------------------------------------------------------------*/
ActualBindings<.List<ActualBinding> bindings.>
= (. ActualBinding binding; .)
  ActualBinding<out binding, false, true>            (. bindings.Add(binding); .)
  { "," ActualBinding<out binding, false, true>      (. bindings.Add(binding); .)
  }
  .

ActualBinding<out ActualBinding binding, bool isGhost, bool allowDecreasesTo>
= (. Token id = null; Expression e; .)
  [ IF(IsBinding())
    NoUSIdentOrDigits<out id>
    ":="
  ]
  Expression<out e, true, true, allowDecreasesTo: allowDecreasesTo>
  (. binding = new ActualBinding(id, e, isGhost); .)
  .

/*------------------------------------------------------------------------*/
QuantifierExpression<out Expression q, bool allowLemma, bool allowLambda>
= (. Contract.Ensures(Contract.ValueAtReturn(out q) != null);
     Token/*!*/ x = Token.NoToken;
     bool univ = false;
     List<BoundVar/*!*/> bvars;
     Attributes attrs;
     Expression range;
     q = dummyExpr;
  .)
  ( Forall                                     (. x = t;  univ = true; .)
  | Exists                                     (. x = t; .)
  )
  QuantifierDomain<out bvars, out attrs, out range, true, true, true>
  ( IF(univ && (la.kind == _ensures || la.kind == _lbrace))
    // It's a forall statement. Parse it as part of a StmtExpr.
    ForallStatementEnsuresAndBody<out var forallStatement, x, bvars, attrs, range>
    Expression<out var expr, allowLemma, allowLambda>
    (. q = new StmtExpr(forallStatement.Tok, forallStatement, expr) {
         Origin = new SourceOrigin(forallStatement.StartToken, forallStatement.EndToken)
       };
    .)

  | QSep
    Expression<out var body, allowLemma, allowLambda>
    (. if (univ) {
         q = new ForallExpr(x, new SourceOrigin(x, t), bvars, range, body, attrs);
       } else {
         q = new ExistsExpr(x, new SourceOrigin(x, t), bvars, range, body, attrs);
       }
    .)
  )
  .

/*------------------------------------------------------------------------*/
QuantifierDomain<.out List<BoundVar> bvars, out Attributes attrs, out Expression range, bool allowLemma, bool allowLambda, bool allowBitwiseOps.>
= (.
     List<QuantifiedVar> qvars = new List<QuantifiedVar>();
     QuantifiedVar/*!*/ qv;
     attrs = null;
     range = null;
  .)
  QuantifierVariableDecl<out qv, ref attrs, allowLemma, allowLambda, allowBitwiseOps>         (. qvars.Add(qv); .)
  { IF(IsQuantifierVariableDecl(qv))
    ","
    QuantifierVariableDecl<out qv, ref attrs, allowLemma, allowLambda, allowBitwiseOps>       (. qvars.Add(qv); .)
  }
  (. QuantifiedVar.ExtractSingleRange(qvars, out bvars, out range); .)
  .
  
/*------------------------------------------------------------------------*/
QuantifierVariableDecl<.out QuantifiedVar qvar, ref Attributes attrs, bool allowLemma, bool allowLambda, bool allowBitwiseOps.>
= (.
     BoundVar bv;
     Expression domain = null;
     Expression range = null;
  .)
  IdentTypeOptional<out bv>
  [ // "<-" can also appear in GenericParameters in something like "<-T>",
    // so we parse it as two separate tokens, but use lookahead to ensure
    // we don't allow whitespace between them.
    IF(IsFromArrow())
    "<" "-"
    // We can't allow bitwise ops here since otherwise we'll swallow up any
    // optional "| <Range>" suffix 
    Expression<out domain, allowLemma, allowLambda, false>
  ]
  { IF( la.kind == _lbracecolon) Attribute<ref attrs> } // Ambiguity -- if the next optional block is not present, 
                                                        // this {Attribute} ends a QuantifierVariableDecl so it can end a SetComprehensionExpr
                                                        // so it can end an Expression
                                                        // But an Expression can be followed by {Attribute} as in VarDeclStatement, Rhs, AssignStatement
  [ // Coco complains about this ambiguity, thinking that a "|" can follow a body-less forall statement. 
    // That can happen because the grammar allows a QuantifierDecl to end a QuantifierDomain, which can end
    // a SetComprehensionExpr, and a Expression can be followed by a bitvector '|'. 
    // The semantic predicate here resolves the ambiguity in favor of a such-that in this QuantifierVariableDecl.
    // The other parse can be obtained by using appropriate parentheses.
    IF(la.kind == _verticalbar) 
    "|"
    Expression<out range, allowLemma, allowLambda, allowBitwiseOps>
  ]
  (. qvar = new QuantifiedVar(bv.Tok, bv.Name, bv.SyntacticType, domain, range); .)
  .

/*------------------------------------------------------------------------*/
SetComprehensionExpr<out Expression q, bool allowLemma, bool allowLambda, bool allowBitwiseOps>
= (. Contract.Ensures(Contract.ValueAtReturn(out q) != null);
     List<BoundVar/*!*/> bvars = new List<BoundVar>();
     Expression range;
     Expression body = null;
     Attributes attrs = null;
     bool finite = true;
  .)
  ( "set" | "iset" (. finite = false; .) )     (. Token setToken = t; .)
  QuantifierDomain<out bvars, out attrs, out range, allowLemma, allowLambda, allowBitwiseOps>
  [ IF(IsQSep())  /* let any given body bind to the closest enclosing set comprehension */
    QSep
    Expression<out body, allowLemma, allowLambda, allowBitwiseOps || !finite>
  ]
  (. if (body == null && bvars.Count != 1) {
       SemErr(ErrorId.p_set_comprehension_needs_term_expression, t, "a set comprehension with more than one bound variable must have a term expression");
       q = dummyExpr;
     } else {
       // This production used to have its own version of QuantifierDomain in which the
       // range was not optional, so we map null to "true" here so that the rest of the
       // logic doesn't hit exceptions.
       range ??= LiteralExpr.CreateBoolLiteral(new AutoGeneratedOrigin(t), true);
       q = new SetComprehension(setToken, new SourceOrigin(setToken, t), finite, bvars, range, body, attrs);
     }
  .)
  .

/*------------------------------------------------------------------------*/
Expressions<.List<Expression> args.>
= (. Expression e; .)
  Expression<out e, true, true>                      (. args.Add(e); .)
  { "," Expression<out e, true, true>                (. args.Add(e); .)
  }
  .

/*------------------------------------------------------------------------*/
AttributeName<out Token id> = NoUSIdent<out id>.

Attribute<ref Attributes attrs>
= (. Token openBrace, closeBrace;
     Token x = null;
     var args = new List<Expression>();
  .)
  "{:"                         (. openBrace = t; .)
  (. ConvertKeywordTokenToIdent(); .)
  AttributeName<out x>
  [ Expressions<args> ]
  "}"                         (. closeBrace = t; .)
  (. 
     var rtok = new SourceOrigin(openBrace, t);
     if (!CheckAttribute(errors, x, rtok)) return;
     attrs = new UserSuppliedAttributes(x, openBrace, closeBrace, args, attrs);
     attrs.Origin = rtok;
  .)
  .

AtAttributes<ref Attributes attrs>
= { AtAttribute<ref attrs> }
  .

/* After literals that start a block, we usually add this
   Note that it will parse all at-attributes at once since expression parsing
   return attributes parsed after them. No need to wrap in a repeat statement
*/
AtAttribute<ref Attributes attrs>
= (. Token atToken = null;
     Expression arg;
  .)
  "@"
  (. atToken = t; .)
  Expression<out arg, false, false, false>
  (. 
     var rtok = new SourceOrigin(atToken, t);
     attrs = new UserSuppliedAtAttribute(t, arg, attrs);
     attrs.Origin = rtok;
  .)
  .

/*------------------------------------------------------------------------*/
Ident<out Token/*!*/ x>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
  ( ident
  | "least"      (. t.kind = _ident; .) // convert it to an ident
  | "greatest"   (. t.kind = _ident; .) // convert it to an ident
  | "older"      (. t.kind = _ident; .) // convert it to an ident
  | "opaque"     (. t.kind = _ident; .) // convert it to an ident
                 (. errors.Deprecated(ErrorId.p_deprecated_opaque_as_identifier, t, "opaque is deprecated as an identifier. It will soon become a reserved word. Use a different name."); .)
  | "hide"       (. t.kind = _ident; .) // convert it to an ident
  | "reveal"     (. t.kind = _ident; .) // convert it to an ident
                 
  )
  (. x = t; .)
  .

/*------------------------------------------------------------------------*/
// Identifier or sequence of digits
// Parse one of the following, which are supposed to follow a ".":
//        ident
//        digits
//        digits . digits
// In the first two cases, x returns as the token for the ident/digits and y returns as null.
// In the third case, x and y return as the tokens for the first and second digits.
// This parser production solves a problem where the scanner might parse a real number instead
// of stopping at the decimal point.
DotSuffix<out Token x, out Token y>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null);
     x = Token.NoToken;
     y = null;
  .)
  ( Ident<out x>
  | digits         (. x = t; .)
  | decimaldigits  (. x = t;
                      int exponent = x.val.IndexOf('e');
                      if (0 <= exponent) {
                        // this is not a legal field/destructor name
                        // Also it is not currently reachable, because decimaldigits does not yet support exponents
                        SemErr(ErrorId.p_invalid_name_after_dot, x, "invalid name after a '.'");
                      } else {
                        int dot = x.val.IndexOf('.');
                        if (0 <= dot) {
                          // change token
                          // [prev][    x    ]
                          // to
                          // [prev][y][dot][x]
                          y = new Token();
                          y.pos = x.pos;
                          y.val = x.val.Substring(0, dot);
                          y.col = x.col;
                          y.line = x.line;
                          y.Uri = x.Uri;
                          y.kind = x.kind;
                          
                          var dotTok = new Token();
                          dotTok.pos = x.pos + dot;
                          dotTok.val = ".";
                          dotTok.Uri = x.Uri;
                          dotTok.line = x.line;
                          dotTok.kind = x.kind;
                          dotTok.col = x.col + dot;
                          
                          x.pos = x.pos + dot + 1;
                          x.val = x.val.Substring(dot + 1);
                          x.col = x.col + dot + 1;
                          
                          var prev = x.Prev;
                          
                          prev.Next = y;
                          y.Prev = prev;
                          
                          y.Next = dotTok;
                          dotTok.Prev = y;
                          
                          dotTok.Next = x;
                          x.Prev = dotTok;
                          
                          // Ok, now swaps x and y when returning
                          var tmp = x;
                          x = y;
                          y = tmp;
                        }
                      }
                   .)
  | "requires"     (. x = t; .)
  | "reads"        (. x = t; .)
  )
  .

/*------------------------------------------------------------------------*/
Name<out Name name>
= NoUSIdent<out var t> (. name = new Name(t); .)
  .
  
// Identifier, disallowing leading underscores
NoUSIdent<out Token/*!*/ x>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
  Ident<out x>     (. if (x.val.StartsWith("_")) {
                        SemErr(ErrorId.p_no_leading_underscore_2, x, 
                          "cannot declare identifier beginning with underscore");
                      }
                   .)
  .

/*------------------------------------------------------------------------*/
IdentOrDigits<out Token id>
= (. id = Token.NoToken; .)
  ( Ident<out id>
  | digits         (. id = t; .)
  )
  .

/*------------------------------------------------------------------------*/
NoDigitName<out Name x>
       = (. Token t; .)
         NoUSIdentOrDigits<out t> (. x = new Name(t); .)
         .
         
NoUSIdentOrDigits<out Token id>
= (. id = Token.NoToken; .)
  ( NoUSIdent<out id>
  | digits         (. id = t; .)
  )
  .

/*------------------------------------------------------------------------*/
MemberBindingUpdate<out Token id, out Expression e>
= (. id = Token.NoToken; e = dummyExpr; .)
  NoUSIdentOrDigits<out id>
  ":="
  Expression<out e, true, true>
  .

/*------------------------------------------------------------------------*/
LabelName<out Token id>
= NoUSIdentOrDigits<out id>
  .

/*------------------------------------------------------------------------*/
MethodFunctionName<out Name name>
= NoDigitName<out name>
  .

/*------------------------------------------------------------------------*/
TypeNameOrCtorSuffix<out Token id>
= (. id = Token.NoToken; .)
  IdentOrDigits<out id>
  .

/*------------------------------------------------------------------------*/
// Identifier, disallowing leading underscores, except possibly the "wildcard" identifier "_"
WildIdentN<out Name name, bool allowWildcardId>
= WildIdent<out var x, allowWildcardId>     (. name = new Name(x); .) .
  
// Identifier, disallowing leading underscores, except possibly the "wildcard" identifier "_"
WildIdent<out Token x, bool allowWildcardId>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
  Ident<out x>     (. x = (Token)t.WithVal(UnwildIdent(t, allowWildcardId)); .)
  .

/*------------------------------------------------------------------------*/
OldSemi  /* NOTE: Coco complains about "OldSemi deletable". That's okay. */
= /* In the future, it may be that semi-colons will be neither needed nor allowed in certain places where,
   * in the past, they were required.  As a period of transition between the two, such semi-colons are optional.
   */
  [ SYNC ";"    (. errors.Deprecated(ErrorId.p_deprecated_semicolon, t, "deprecated style: a semi-colon is not needed here"); .)
  ].

/*------------------------------------------------------------------------*/
Nat<out BigInteger n>
= (. n = BigInteger.Zero;
     string S;
  .)
  ( digits
    (. S = Util.RemoveUnderscores(t.val);
       try {
         n = BigIntegerParser.Parse(S);
       } catch (System.FormatException) {
         SemErr(ErrorId.p_bad_number_format, t, "incorrectly formatted number");
         n = BigInteger.Zero;
       }
    .)
  | hexdigits
    (. S = Util.RemoveUnderscores(t.val.Substring(2));
       try {
         // note: leading 0 required when parsing positive hex numbers
         n = BigIntegerParser.Parse("0" + S, System.Globalization.NumberStyles.HexNumber);
       } catch (System.FormatException) {
         SemErr(ErrorId.p_bad_hex_number_format, t, "incorrectly formatted number");
         n = BigInteger.Zero;
       }
    .)
  )
  .

/*------------------------------------------------------------------------*/
Dec<out BaseTypes.BigDec d>
= (. d = BaseTypes.BigDec.ZERO; .)
  (decimaldigits
    (. var S = Util.RemoveUnderscores(t.val);
       try {
         d = BaseTypes.BigDec.FromString(S);
       } catch (System.FormatException) {
         SemErr(ErrorId.p_bad_decimal_number_format, t, "incorrectly formatted number");
         d = BaseTypes.BigDec.ZERO;
       }
    .)
  )
  .

/*------------------------------------------------------------------------*/
END Dafny.