/**************************************************************************/
/*                                                                        */
/*  This file is part of Frama-Clang                                      */
/*                                                                        */
/*  Copyright (C) 2012-2022                                               */
/*    CEA (Commissariat à l'énergie atomique et aux énergies              */
/*         alternatives)                                                  */
/*                                                                        */
/*  you can redistribute it and/or modify it under the terms of the GNU   */
/*  Lesser General Public License as published by the Free Software       */
/*  Foundation, version 2.1.                                              */
/*                                                                        */
/*  It is distributed in the hope that it will be useful,                 */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
/*  GNU Lesser General Public License for more details.                   */
/*                                                                        */
/*  See the GNU Lesser General Public License version 2.1                 */
/*  for more details (enclosed in the file LICENSE).                      */
/*                                                                        */
/**************************************************************************/

//
// Description:
//   Implementation of the ACSL global annotations.
//

#include "ACSLGlobalAnnotation.h"
#include "clang/AST/DeclTemplate.h"

namespace Acsl {

using namespace DLexer;

#if defined(__clang__) || defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-label"
#endif

Parser::ReadResult
GlobalAnnotation::Parameters::readToken(
  Parser::State& state, Parser::Arguments& arguments) {
  enum delimiters { DBegin, DParam, DEndParam };
  ReadResult result = RRNeedChars;
  switch (state.point()) {
    DefineParseCase(Begin)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator
             && ((const OperatorPunctuatorToken&)token).getType() ==
                 OperatorPunctuatorToken::TCloseParen) {
          // DefineReduce will clear the next token, that we in fact need
          // in the rest of the rule. 
          DefineReduceAndParse
        }
        LogicType* type = new LogicType;
        state.absorbRuleResult(type);
        DefineShiftAndParse(Param,*type,&LogicType::readToken)
      }
   DefineParseCase(Param)
     { logic_type typ;
       { Parser::State::RuleAccess::TCastFromRule<LogicType>
           node(state.getRuleResult());
         typ = node->extractType();
         if (!typ) {
           DefineAddError("Expecting a type in parameter declaration")
           DefineReduceAndParse
         }
       };
       state.freeRuleResult();
       AbstractToken token = arguments.queryToken();
       if (token.getType() == AbstractToken::TIdentifier) {
         std::string name =
           ((const IdentifierToken&)arguments.getContentToken()).content();
         logic_arg_decl arg = logic_arg_decl_cons(typ,copy_string(name));
         if (!_params)
           _params = _endParams = cons_container(arg,NULL);
         else {
           _endParams->next = cons_container(arg,NULL);
           _endParams = _endParams->next;
         }
         DefineGotoCase(EndParam)
       };
       DefineAddError(
         std::string("Unexpected '")
         .append(token.str())
         .append("'. Expected identifier after type in parameter declaration"))
       DefineReduceAndParse
     }
   DefineParseCase(EndParam)
    { AbstractToken token = arguments.queryToken();
      if (token.getType () == AbstractToken::TOperatorPunctuator) {
       switch (((const OperatorPunctuatorToken&)token).getType()) {
       case OperatorPunctuatorToken::TCloseParen: DefineReduceAndParse
       case OperatorPunctuatorToken::TComma: DefineGotoCase(Begin)
       default:
        DefineAddError(
          std::string("Unexpected '")
          .append(token.str())
          .append("'. Expected ')' or ',' after parameter declaration"))
        DefineReduceAndParse
       }
      } else {
       DefineAddError(
        std::string("Unexpected '")
        .append(token.str())
        .append("'. Expected ')' or ',' after parameter declaration"))
       DefineReduceAndParse
      }
    }
  }
  return result;
}

Parser::ReadResult GlobalAnnotation::LabelList::readToken(
 Parser::State& state, Parser::Arguments& arguments) {
  enum Delimiters { DBegin, DAfterId };
  ReadResult result = RRNeedChars;
  switch (state.point()) {
    DefineParseCase(Begin) {
      if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
        std::string label = ((const IdentifierToken&)
            arguments.getContentToken()).content();
        const clang::NamedDecl* cidentifier = arguments.isCodeName(label);
        logic_label logicLabel = NULL;
        arguments.addLogicLabel(label);
        if (cidentifier && cidentifier->getKind() == clang::Decl::Label)
          logicLabel = logic_label_StmtLabel(strdup(label.c_str()));
        else
          logicLabel = logic_label_LogicLabel(strdup(label.c_str()));
        list tail = cons_container(logicLabel, NULL);
        if(_endLabels) {
          _endLabels->next = tail;
        } else { // first element
            _labels = _endLabels = tail;
        }
        DefineGotoCase(AfterId)
      };
      CleanLogicLabels(arguments);
      DefineAddError("expecting identifier after logic label construction");
      DefineReduce;
    }
    DefineParseCase(AfterId) {
      AbstractToken token = arguments.queryToken();
      if (token.getType() == AbstractToken::TOperatorPunctuator) {
        OperatorPunctuatorToken::Type
          type = ((const OperatorPunctuatorToken&) token).getType();
        if (type == OperatorPunctuatorToken::TComma)
          DefineGotoCase(Begin)
         if (type == OperatorPunctuatorToken::TCloseBrace)
           DefineReduce
        };
        CleanLogicLabels(arguments);
        DefineAddError("expecting ',' or '}' in a logic label construction")
        DefineReduce
     }
    default:
      DefineAddError("unexpected parser state in LabelList")
      DefineReduce
  }
}

void
GlobalAnnotation::BuildLogicFunction(Parser::Arguments& context)
{ list /* params */ tparams = NULL;
  if (!_typeVarBinders.empty()) {
   std::list<std::string>::const_reverse_iterator iterEnd =
    _typeVarBinders.rend();
   for (std::list<std::string>::const_reverse_iterator iter = 
        _typeVarBinders.rbegin();
      iter != iterEnd;
      ++iter)
    tparams = cons_container(strdup(iter->c_str()), tparams);
   _typeVarBinders.clear();
  };
  logic_type vartype = _typeVar;
  _typeVar = NULL;

  /* logic_arg_decl */ list params = _params;
  _params = NULL;
  logic_type thisType = context.queryThisType();
  if (thisType) {
    logic_arg_decl prm = logic_arg_decl_cons(thisType,strdup("\\this"));
    params = cons_container(prm,params);
    _isMethod = true;
  }
  option ret_type = vartype?opt_some_container(vartype):opt_none();
  qualified_name name;
  if (!_qualificationId)
    name = context.makeLogicQualifiedName(_polyId);
  else
    name = qualified_name_cons(_qualificationId->makeQualificationList(),
        strdup(_polyId.c_str()));
  logic_body body = NULL;
  if (_term) body = logic_body_LBterm(_term);
  if (_pred) body = logic_body_LBpred(_pred);
  if (_hasReads) body = logic_body_LBreads(_reads.getFront());
  if (!body) body = logic_body_LBnone();
  _term=NULL; _pred = NULL;
  _hasReads = false;
  _reads = ForwardList();
  logic_info info =
   logic_info_cons(
    name,
    context.isExternCContext(),
    _polyIdLabels,
    tparams,
    ret_type,
    params, 
    body);
  _polyIdLabels = NULL;
  bool isMethod = _isMethod;
  _isMethod = false;
  if (!(_codeOperator
        ? context.addOverloadedLogicOperators(_polyId, _codeOperator,
              logic_info_dup(info), isMethod)
        : context.addOverloadedLogicFunctions(_polyId, logic_info_dup(info),
          isMethod))) {
    context.addErrorMessage(std::string("impossible to add logic function ")
        .append(_polyId));
    return;
  };
  _polyId = "";
  _globals.insertContainer(
   global_annotation_Dfun_or_pred(copy_loc(_loc), info));
  // if (body->tag_logic_body != LBNONE)
  //   _globals.insertContainer(
  //     global_annotation_Dfun_or_pred(copy_loc(_loc), info));
  // else
  //   free_logic_info(info);
}

void GlobalAnnotation::CleanLogicLabels(Parser::Arguments& context) {
  list l = _polyIdLabels;
  while(l) {
    logic_label lab = (logic_label)l->element.container;
    if (lab->tag_logic_label == LOGICLABEL) {
      context.removeLogicLabel(lab->cons_logic_label.LogicLabel.label);
    }
    if (lab->tag_logic_label == STMTLABEL) {
      context.removeLogicLabel(lab->cons_logic_label.StmtLabel.code_label);
    }
    l = l->next;
  }
}

void GlobalAnnotation::CleanFormals(Parser::Arguments& context) {
  list p = _params;
  while(p) {
    logic_arg_decl arg = (logic_arg_decl)p->element.container;
    context.removeLogicFormal(arg->la_name);
    p = p->next;
  }
}

void
GlobalAnnotation::clear() {
  if (_typeVar) {
   free_logic_type(_typeVar);
   _typeVar = NULL;
  };
  if (_term) {
   free_term(_term);
   _term = NULL;
  };
  if (_pred) {
   free_predicate_named(_pred);
   _pred = NULL;
  };
  list reads = _reads.getFront();
  while(reads) {
    free_term((term) reads->element.container);
    reads = reads->next;
  };
  _hasReads = false;
  _isMethod = false;
  if (!_typeVarBinders.empty())
   _typeVarBinders.clear();
  _polyId = "";
  while (_polyIdLabels) {
    free_logic_label((logic_label) _polyIdLabels->element.container);
    /* logic_label */ list temp = _polyIdLabels;
    _polyIdLabels = _polyIdLabels->next;
    free(temp);
  };
  _qualificationId = NULL;
  /* logic_arg_decl */ list params = _params;
  while (params) {
   free_logic_arg_decl((logic_arg_decl) params->element.container);
   /* logic_arg_decl */ list cell = params;
   params = params->next;
   free(cell);
  };
  _params = NULL;
  if (_typeName) {
    free_qualified_name(_typeName);
    _typeName = NULL;
  };
  _codeOperator = DLexer::OperatorPunctuatorToken::TUndefined;
}

Parser::ReadResult
GlobalAnnotation::readToken(Parser::State& state, Parser::Arguments& arguments)
{ enum Delimiters
  { DBegin, DLogic, DLogicOperator, DLogicEndOperator, DLogicQualification,
    DAfterLogicQualification, DOptPolyIdLabel, DFunctionArgs, DPolyIdLabel,
    DTypeVarBindersForPolyId, DEndTypeVarBindersForPolyId,
    DParam, DLogicDef, DLogicReads, DLogicReadsTerm, DPredicate,
    DPredicateQualification, DAfterPredicateQualification, DFunctionBody,
    DAxiomatic, DBeforeAxiomaticBody, DAfterAxiomaticBody, DLemma, DAxiom,
    DLemmaId, DAxiomId, DLabelLemma, DLabelAxiom, DEndLemma, DEndAxiom,
    DType, DClass, DAfterType, DTypeDef, DTypeInvariant, DClassInvariant,
    DTypeInvariantParameterList, DTypeInvariantParameter, 
    DTypeInvariantDefinition, DEndTypeInvariant, DInductive, DGlobal,
    DGlobalInvariant, DGlobalInvariantBody, DEndGlobalInvariant,
    DBeforeSemiColon
  };

  ReadResult result = RRNeedChars;
  switch (state.point()) {
    DefineParseCase(Begin)
      assert (_typeVarBinders.empty());
      arguments.setGlobalAnnotContext();
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TKeyword) {
          KeywordToken::Type type = ((const KeywordToken&) token).getType();
          if (type == KeywordToken::TLogic) {
            LogicType* rule = new LogicType;
            state.absorbRuleResult(rule);
            // rule->setStopOnSuffix();
            DefineShift(Logic, *rule, &LogicType::readToken);
          }
          if (type == KeywordToken::TPredicate)
            DefineGotoCase(Predicate)
          if (type == KeywordToken::TLemma)
            DefineGotoCase(Lemma)
          if (type == KeywordToken::TAxiomatic)
            DefineGotoCase(Axiomatic)
          if (type == KeywordToken::TAxiom)
            DefineGotoCase(Axiom)
          if (type == KeywordToken::TType)
            DefineGotoCase(Type)
          if (type == KeywordToken::TClass)
            DefineGotoCase(Class)
          if (type == KeywordToken::TInductive)
            DefineGotoCase(Inductive)
          if (type == KeywordToken::TGlobal)
            DefineGotoCase(Global)
        };
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TCloseBrace) {
            arguments.resetAnnotContext();
            DefineReduceAndParse
          };
        }
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting 'logic', 'predicate' or 'lemma' keyword")
      DefineGotoCase(Begin)
    DefineParseCase(Logic)
      { Parser::State::RuleAccess::TCastFromRule<LogicType>
          rule(state.getRuleResult());
        _typeVar = rule->extractType();
        state.freeRuleResult();
      };
      { AbstractToken token = arguments.queryToken();
        AbstractToken::Type type = token.getType();
        if (type == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
             == OperatorPunctuatorToken::TLess) {
            assert(false); // handled by the LogicType rule = [TODO]
          }
        }
        if (type == AbstractToken::TKeyword) {
          if (((const KeywordToken&) token).getType()
              == KeywordToken::TOperator) {
            logic_type thisRecordType = arguments.queryThisType();
            if (thisRecordType) {
              _isMethod = true;
              arguments.addLogicFormal("\\this", thisRecordType);
            }
            DefineGotoCase(LogicOperator)
          };
        }
      };
      if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
        _polyId = ((const IdentifierToken&)
            arguments.getContentToken()).content();
        absorbLoc(arguments.newTokenLocation());
        DefineGotoCase(LogicQualification)
      };
      DefineAddError("expecting identifier after 'logic type-var'");
      DefineGotoCase(BeforeSemiColon);
    DefineParseCase(LogicOperator)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).isOverloadable()) {
            OperatorPunctuatorToken concreteToken(
                (const OperatorPunctuatorToken&) token);
            std::ostringstream name;
            name << "operator";
            concreteToken.writeSign(name);
            OperatorPunctuatorToken::Type operatorType
              = ((const OperatorPunctuatorToken&) token).getType();
            if (operatorType == OperatorPunctuatorToken::TOpenBracket)
              name << ']';
            else if (operatorType == OperatorPunctuatorToken::TOpenParen)
              name << ')';
            _polyId = name.str();
            if (!_qualificationId) absorbLoc(arguments.newTokenLocation());
            _codeOperator = operatorType;
            if (operatorType == OperatorPunctuatorToken::TOpenBracket
                || operatorType == OperatorPunctuatorToken::TOpenParen)
              DefineGotoCase(LogicEndOperator);
            DefineGotoCase(OptPolyIdLabel)
          };
        };
      };
      CleanFormals(arguments);
      arguments.resetAnnotContext();
      DefineAddError("expecting an overloadable operator"
          " after 'logic type-var operator'");
      DefineGotoCase(BeforeSemiColon);
    DefineParseCase(LogicEndOperator)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type operatorType
            = ((const OperatorPunctuatorToken&) token).getType();
          bool endParse = false;
          if (operatorType == OperatorPunctuatorToken::TCloseBracket
              && _codeOperator == OperatorPunctuatorToken::TOpenBracket)
            endParse = true;
          if (operatorType == OperatorPunctuatorToken::TCloseParen
              && _codeOperator == OperatorPunctuatorToken::TOpenParen)
            endParse = true;
          if (endParse)
            DefineGotoCase(OptPolyIdLabel)
        };
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting an overloadable operator"
          " after 'logic type-var operator'");
      DefineGotoCase(BeforeSemiColon);
    DefineParseCase(LogicQualification)
    DefineParseCase(PredicateQualification)
LQualification:
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type type
            = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TColonColon) {
            GlobalContext::NestedContext* found = NULL;
            if (!_qualificationId)
              found = arguments.findLogicName(_polyId);
            else
              found = arguments.get_clang_utils()->globalAcslContext()
                .find(_polyId, _qualificationId);
            if (!found || !found->isQualification()) {
              CleanFormals(arguments);
              DefineAddError("unbound qualification for 'logic qual::id ...'")
            }
            else
              _qualificationId = (GlobalContext::Qualification*) found;
            _polyId = "";
            DefineGotoCaseWithIncrement(DAfterLogicQualification
                - DLogicQualification, LAfterQualification)
          };
          if (type == OperatorPunctuatorToken::TOpenBrace) {
            DefineGotoCaseAndParse(OptPolyIdLabel)
          }
        };
        logic_type thisRecordType = NULL;
        if (!_qualificationId)
          thisRecordType = arguments.queryThisType();
        else if (_qualificationId->hasRecordType()) {
          // we are giving an out-of-line definition for a predicate or
          // logic function declared inside a class.
          qualified_name name = _qualificationId->makeRecordName();
          thisRecordType = arguments.makeLogicType(name);
          free_qualified_name(name);
        };
        if (thisRecordType) {
          _isMethod = true;
          arguments.addLogicFormal("\\this", thisRecordType);
        }
        DefineGotoCaseAndParse(FunctionArgs)
      }
    DefineParseCase(AfterLogicQualification)
    DefineParseCase(AfterPredicateQualification)
LAfterQualification:
      { AbstractToken token = arguments.queryToken();
        AbstractToken::Type type = token.getType();
        if (type == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
             == OperatorPunctuatorToken::TLess) {
            assert(false); // handled by the LogicType rule = [TODO]
          }
        }
        if (state.point() == DAfterLogicQualification
            && type == AbstractToken::TKeyword) {
          if (((const KeywordToken&) token).getType()
              == KeywordToken::TOperator) {
            assert(_qualificationId);
            logic_type thisRecordType = NULL;
            if (_qualificationId->hasRecordType()) {
              qualified_name name = _qualificationId->makeRecordName();
              thisRecordType = arguments.makeLogicType(name);
              free_qualified_name(name);
            };
            if (thisRecordType) {
              _isMethod = true;
              arguments.addLogicFormal("\\this", thisRecordType);
            }
            DefineGotoCase(LogicOperator)
          };
        }
      };
      if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
        _polyId = ((const IdentifierToken&)
            arguments.getContentToken()).content();
        DefineGotoCaseWithIncrement(
            DLogicQualification - DAfterLogicQualification, LQualification)
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting identifier after 'logic qual::...'");
      DefineGotoCase(BeforeSemiColon);
    DefineParseCase(OptPolyIdLabel)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TOpenBrace) {
            LabelList* labels = new LabelList;
            state.absorbRuleResult(labels);
            DefineShift(PolyIdLabel,*labels,&LabelList::readToken)
          }
        };
      };
      DefineGotoCaseAndParse(FunctionArgs)
    DefineParseCase(PolyIdLabel) {
        Parser::State::RuleAccess::TCastFromRule<LabelList>
          labels(state.getRuleResult());
        _polyIdLabels = labels->extractLabels();
        state.freeRuleResult();
        DefineGotoCaseAndParse(FunctionArgs)
    }
    DefineParseCase(FunctionArgs)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type type
            = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TLess) {
            if (!_typeVarBinders.empty()) {
              CleanLogicLabels(arguments);
              DefineAddError("multiple var bindings "
                  "in 'logic type-var<...><...>'")
                }
            DefineGotoCase(TypeVarBindersForPolyId)
          };
          if (type == OperatorPunctuatorToken::TOpenParen) {
            if (_params) {
              CleanLogicLabels(arguments);
              DefineAddError("multiple parameters "
                  "in 'logic type-var(...)(...)'")
            }
            Parameters* parameters = new Parameters(_loc);
            state.absorbRuleResult(parameters);
            DefineShift(Param, *parameters, &Parameters::readToken)
            // inlined version
            //  DefineGotoCase(Param)
          };
          DefineGotoCaseAndParse(FunctionBody);
        };
        arguments.lexer().assumeContentToken();
        CleanLogicLabels(arguments);
        CleanFormals(arguments);
        arguments.resetAnnotContext();
        DefineAddError(std::string("unexpected token '")
          .append(arguments.getContentToken().str())
          .append("' in logic function/predicate definition"));
        clear();
        DefineGotoCase(Begin)
      }
    DefineParseCase(FunctionBody)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&)token).getType();
          if (type == OperatorPunctuatorToken::TAssign) {
            TermOrPredicate* term = new TermOrPredicate;
            if (_typeVar)
              state.absorbRuleResult(&term->setTerm());
            else
              state.absorbRuleResult(&term->setPredicate());
            DefineShift(LogicDef, *term, &TermOrPredicate::readToken)
          }
          if (type == OperatorPunctuatorToken::TSemiColon) {
            CleanLogicLabels(arguments);
            CleanFormals(arguments);
            BuildLogicFunction(arguments);
            arguments.resetAnnotContext();
            DefineGotoCase(Begin);
          }
        }
        if (token.getType() == AbstractToken::TKeyword) {
          KeywordToken::Type type = ((const KeywordToken&) token).getType();
          if (type == KeywordToken::TReads)
            DefineGotoCase(LogicReads)
        };
        arguments.lexer().assumeContentToken();
        CleanLogicLabels(arguments);
        CleanFormals(arguments);
        arguments.resetAnnotContext();
        DefineAddError(std::string("unexpected token '")
         .append(arguments.getContentToken().str())
         .append("' in logic function/predicate definition"));
        clear();
        DefineGotoCase(Begin)
      };
    DefineParseCase(TypeVarBindersForPolyId)
      if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
        _typeVarBinders.push_back(((const IdentifierToken&)
            arguments.getContentToken()).content());
        DefineGotoCase(EndTypeVarBindersForPolyId)
      }
      CleanLogicLabels(arguments);
      DefineAddError("expecting identifier for xx in 'logic type-var<xx, ...>'")
      DefineGotoCase(EndTypeVarBindersForPolyId)
    DefineParseCase(EndTypeVarBindersForPolyId)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type type
            = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TComma)
            DefineGotoCase(TypeVarBindersForPolyId)
          if (type == OperatorPunctuatorToken::TGreater)
            DefineGotoCase(OptPolyIdLabel)
        };
        CleanLogicLabels(arguments);
        DefineAddError("expecting end of binding '>' in 'logic type-var<id >'")
        DefineGotoCase(EndTypeVarBindersForPolyId)
      };
    DefineParseCase(Param)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&) token).getType();
         if (type == OperatorPunctuatorToken::TCloseParen) {
            Parser::State::RuleAccess::TCastFromRule<Parameters>
              params(state.getRuleResult());
            _params = params->extractParams();
            state.freeRuleResult();
            list tmp_prms = _params;
            while (tmp_prms) {
              logic_arg_decl prm = (logic_arg_decl) tmp_prms->element.container;
              if (arguments.hasLogicFormal(prm->la_name)) {
                CleanLogicLabels(arguments);
                CleanFormals(arguments);
                DefineAddError(std::string("Variable ").append(prm->la_name)
                  .append(" is declared twice as formal of function ")
                  .append(_polyId).append("."));
              }
              else
                arguments.addLogicFormal(prm->la_name,prm->la_type);
              tmp_prms = tmp_prms->next;
            }
            DefineGotoCase(FunctionBody);
          };
        };
      };
      DefineAddError("expecting ')' at the end of a list of parameters");
      DefineGotoCase(Param)
    DefineParseCase(LogicDef)
      { { Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
            node(state.getRuleResult());
          if (_typeVar) {
            logic_type ltypeTerm = NULL;
            _term = node->extractTerm(arguments, ltypeTerm);
            if (_term) {
               if (_typeVar && !logic_type_equal(_typeVar, ltypeTerm))
                 _term = TermOrPredicate::implicitConversion(_typeVar, _term,
                     ltypeTerm, false, arguments);
               else
                 free_logic_type(ltypeTerm);
            };
            if (!_term) {
              CleanLogicLabels(arguments);
              CleanFormals(arguments);
              arguments.resetAnnotContext();
              DefineAddError("expecting a term as body of function");
              DefineReduceAndParse
            };
          }
          else {
            _pred = node->extractPredicate(arguments);
            if (!_pred) {
              arguments.resetAnnotContext();
              DefineAddError("expecting a predicate");
              DefineReduceAndParse
            };
          };
          extend_location_with(_loc, node->getLocation());
          state.freeRuleResult();
        };
        AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TSemiColon) {
            CleanLogicLabels(arguments);
            CleanFormals(arguments);
            BuildLogicFunction(arguments);
            arguments.resetAnnotContext();
            DefineGotoCase(Begin);
          }
        }
      }
      CleanLogicLabels(arguments);
      CleanFormals(arguments);
      DefineAddError("';' expected after logic function definition")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(LogicReads)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TKeyword) {
          if (((const KeywordToken&) token).getType() == KeywordToken::TNothing)
          {
            CleanLogicLabels(arguments);
            CleanFormals(arguments);
            arguments.extendLocationWithToken(_loc);
            BuildLogicFunction(arguments);
            DefineGotoCase(BeforeSemiColon);
          }
        }
        _hasReads = true;
        TermOrPredicate* term = new TermOrPredicate;
        state.absorbRuleResult(&term->setTerm());
        DefineShiftAndParse(LogicReadsTerm, *term, &TermOrPredicate::readToken)
      }
    DefineParseCase(LogicReadsTerm)
      { Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
            termReads(state.getRuleResult());
        logic_type ltype = NULL;
        term readTerm = termReads->extractTermOrSet(arguments,ltype);
        if (readTerm)
          _reads.insertContainer(readTerm);
        extend_location_with(_loc, termReads->getLocation());
        state.freeRuleResult();
      }
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&)
              token).getType();
          if (type == OperatorPunctuatorToken::TComma) { 
            TermOrPredicate* term = new TermOrPredicate;
            state.absorbRuleResult(&term->setTerm());
            DefineShift(LogicReadsTerm, *term, &TermOrPredicate::readToken)
          }
          if (type == OperatorPunctuatorToken::TSemiColon) {
            CleanLogicLabels(arguments);
            CleanFormals(arguments);
            BuildLogicFunction(arguments);
            arguments.resetAnnotContext();
            DefineGotoCase(Begin)
          }
        }
      }
      CleanLogicLabels(arguments);
      CleanFormals(arguments);
      arguments.resetAnnotContext();
      DefineAddError("expecting ',' or ';' after logic reads clause "
          "in function definition")
      DefineGotoCase(BeforeSemiColon);
    DefineParseCase(Predicate)
      if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
        _polyId = ((const IdentifierToken&) arguments.getContentToken())
          .content();
        absorbLoc(arguments.newTokenLocation());
        DefineGotoCase(PredicateQualification)
      };
      CleanLogicLabels(arguments);
      CleanFormals(arguments);
      arguments.resetAnnotContext();
      DefineAddError("expecting an identifier after 'predicate'");
      DefineGotoCase(BeforeSemiColon);
    DefineParseCase(Axiomatic)
      if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
        _polyId = ((const IdentifierToken&) arguments.getContentToken())
          .content();
        absorbLoc(arguments.newTokenLocation());
        DefineGotoCase(BeforeAxiomaticBody)
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting an identifier after 'axiomatic id...'")
      DefineReduceAndParse
    DefineParseCase(BeforeAxiomaticBody)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TOpenBrace) {
            GlobalAnnotation* rule = new GlobalAnnotation(_loc);
            state.absorbRuleResult(rule);
            rule->_polyAxiomId = _polyId;
            DefineShift(AfterAxiomaticBody, *rule, &GlobalAnnotation::readToken)
          };
        };
      };
      DefineAddError("expecting a '{' after 'axiomatic id '")
      DefineGotoCaseAndParse(BeforeSemiColon)
   DefineParseCase(AfterAxiomaticBody)
     { AbstractToken token = arguments.queryToken();
       if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TCloseBrace) {
            Parser::State::RuleAccess::TCastFromRule<GlobalAnnotation>
              rule(state.getRuleResult());
            extend_location_with(_loc, rule->_loc);
            _globals.insertContainer(global_annotation_Daxiomatic(
                copy_loc(_loc), strdup(_polyId.c_str()), rule->_annots));
            _polyId = "";
            rule->_annots = NULL;
            state.freeRuleResult();
            clear();
            arguments.resetAnnotContext();
            DefineGotoCase(Begin)
          };
        };
      };
      DefineAddError("expecting a '}' after 'axiomatic id { ... '")
      DefineGotoCaseAndParse(BeforeSemiColon)
    DefineParseCase(Lemma)
    DefineParseCase(Axiom)
      if (arguments.queryToken().getType()
          == AbstractToken::TIdentifier) {
        _polyId = ((const IdentifierToken&) arguments.getContentToken())
          .content();
        if (state.point() == DAxiom && _polyAxiomId == "") {
          DefineAddError(std::string("axiom ").append(_polyId)
            .append(" should be declared inside an axiomatic"));
          absorbLoc(arguments.newTokenLocation());
        }
        DefineGotoCaseWithIncrement(DAxiomId - DAxiom, LAxiomId)
      };
      DefineAddError("expecting an identifier after 'axiom id...'")
      DefineGotoCaseAndParse(BeforeSemiColon)
    DefineParseCase(LemmaId)
    DefineParseCase(AxiomId)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TColon) {
            TermOrPredicate* rule = new TermOrPredicate;
            state.absorbRuleResult(rule);
            DefineShiftWithIncrement(DEndAxiom - DAxiomId, LEndAxiom,
                *rule, &TermOrPredicate::readToken);
          } else if (type == OperatorPunctuatorToken::TOpenBrace) {
            if (_polyIdLabels) {
              DefineAddError(
                "multiple declaration of logic labels { ... } { ... }")
            }
            LabelList* rule = new LabelList;
            state.absorbRuleResult(rule);
            DefineShiftWithIncrement(
              DLabelAxiom - DAxiomId, LLabelAxiom,*rule,&LabelList::readToken)
          };
        };
      };
      DefineAddError("expecting ':' after \"axiom id...\"")
      { TermOrPredicate* rule = new TermOrPredicate;
        state.absorbRuleResult(rule);
        DefineShiftWithIncrement(DEndAxiom - DAxiomId, LEndAxiom,
            *rule, &TermOrPredicate::readToken);
      };
    DefineParseCase(LabelLemma)
    DefineParseCase(LabelAxiom)
      {
        Parser::State::RuleAccess::TCastFromRule<LabelList>
          labels(state.getRuleResult());
        _polyIdLabels = labels->extractLabels();
        state.freeRuleResult();
        DefineGotoCaseAndParseWithIncrement(DAxiomId-DLabelAxiom,LAxiomId)
      }
    DefineParseCase(EndLemma)
    DefineParseCase(EndAxiom)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type type
            = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TSemiColon) {
            Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
                axiom(state.getRuleResult());
            predicate_named pred = axiom->extractPredicate(arguments);
            if (pred) {
              _globals.insertContainer(global_annotation_Dlemma(
                copy_loc(_loc),
                strdup(_polyId.c_str()),
                state.point() == DEndAxiom,
                _polyIdLabels,
                NULL /* ids */,
                pred));
              CleanLogicLabels(arguments);
              _polyIdLabels = NULL;
            }
            state.freeRuleResult();
            _polyId = "";
            CleanFormals(arguments);
            arguments.resetAnnotContext();
            DefineGotoCase(Begin)
          };
        };
      };
      DefineGotoCase(EndAxiom)
    DefineParseCase(Type)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TIdentifier) {
          _polyId = ((const IdentifierToken&) arguments.getContentToken())
            .content();
          absorbLoc(arguments.newTokenLocation());
          DefineGotoCase(AfterType)
        }
        else if (token.getType() == AbstractToken::TKeyword) {
          if (((const KeywordToken&) token).getType()
              == KeywordToken::TInvariant)
          DefineGotoCase(TypeInvariant)
        };
      };
      DefineAddError("expecting identifier or 'invariant' after keyword 'type'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(Class)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TKeyword) {
          if (((const KeywordToken&) token).getType()
              == KeywordToken::TInvariant)
            DefineGotoCase(ClassInvariant)
        };
      };
      DefineAddError("expecting identifier or 'invariant' after keyword 'type'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(AfterType)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          OperatorPunctuatorToken::Type
            type = ((const OperatorPunctuatorToken&) token).getType();
          if (type == OperatorPunctuatorToken::TLess) {
            if (!_typeVarBinders.empty())
              DefineAddError("multiple var bindings "
                  "in 'logic type-var<...><...>'")
            DefineGotoCase(TypeVarBindersForPolyId)
          };
          if (type == OperatorPunctuatorToken::TAssign) {
            assert(!_typeName);
            _typeName = arguments.makeLogicQualifiedName(_polyId);
            LogicType* rule = new LogicType(_typeName);
            state.absorbRuleResult(rule);
            DefineShift(TypeDef, *rule, &LogicType::readToken)
          };
          if (type == OperatorPunctuatorToken::TSemiColon) {
            qualified_name typeName = arguments.makeLogicQualifiedName(_polyId);
            bool extern_c = arguments.isExternCContext();
            logic_type_info info =
              logic_type_info_cons(
                qualified_name_dup(typeName), extern_c, NULL, opt_none());
            arguments.addLogicType(_polyId, logic_type_info_dup(info));
            _globals.insertContainer(global_annotation_Dtype(copy_loc(_loc),
                  info));
            _polyId = "";
            arguments.resetAnnotContext();
            DefineGotoCase(Begin);
          }
        };
        DefineAddError("expecting assignment '=' after 'logic type-var'")
        DefineGotoCaseAndParse(BeforeSemiColon)
      };
    DefineParseCase(TypeDef)
      { Parser::State::RuleAccess::TCastFromRule<LogicType>
          definition(state.getRuleResult());
        /* string */ list params = NULL;
        if (!_typeVarBinders.empty()) {
          std::list<std::string>::const_iterator
            paramIterEnd = _typeVarBinders.end();
          std::list<std::string>::const_iterator
            paramIter = _typeVarBinders.begin();
          list endParams = params = cons_container(strdup(paramIter->c_str()),
              NULL);
          for (++paramIter; paramIter != paramIterEnd; ++paramIter) {
            endParams->next = cons_container(strdup(paramIter->c_str()), NULL);
            endParams = endParams->next;
          };
        };
        bool extern_c = arguments.isExternCContext();
        logic_type_info info =
          logic_type_info_cons(
            _typeName, extern_c, params,
            opt_some_container(definition->extractTypeDef()));
        arguments.addLogicType(_typeName->decl_name, logic_type_info_dup(info));
        _globals.insertContainer(global_annotation_Dtype(copy_loc(_loc), info));
        state.freeRuleResult();
        _typeVarBinders.clear();
        _typeName = NULL;
        _params = NULL;
        clear();
        arguments.resetAnnotContext();
        DefineGotoCase(Begin)
      }
    DefineParseCase(TypeInvariant)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TIdentifier) {
          _polyId = ((const IdentifierToken&) arguments.getContentToken())
            .content();
          absorbLoc(arguments.newTokenLocation());
          DefineGotoCase(TypeInvariantParameterList)
        }
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting an identifier "
          "after the construction 'type invariant'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(ClassInvariant)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TIdentifier) {
          _polyId = ((const IdentifierToken&) arguments.getContentToken())
            .content();
          absorbLoc(arguments.newTokenLocation());
          logic_type thisRecordType = arguments.queryThisType();
          if (thisRecordType) {
            _isMethod = true;
            arguments.addLogicFormal("\\this", thisRecordType);
            assert (!_params);
            _params =
              cons_container(
                logic_arg_decl_cons(logic_type_dup(thisRecordType),"\\this"),
                NULL);
          } else {
            DefineAddError("class invariant outside of a class");
          }
          DefineGotoCase(TypeInvariantDefinition)
        }
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting an identifier "
          "after the construction 'class invariant'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(TypeInvariantParameterList)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TOpenParen) {
            if (_params) {
              DefineAddError("multiple parameters "
                  "in 'type invariant type-var(...)(...)'")
                }
            Parameters* parameters = new Parameters(_loc);
            state.absorbRuleResult(parameters);
            DefineShift(TypeInvariantParameter, *parameters,
                &Parameters::readToken)
          };
        };
      }
      arguments.resetAnnotContext();
      DefineAddError("':' expected after the construction "
          "'global invariant id'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(TypeInvariantParameter)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TCloseParen) {
            Parser::State::RuleAccess::TCastFromRule<Parameters>
              params(state.getRuleResult());
            _params = params->extractParams();
            if (_params != NULL) {
              /* logic_arg_decl */ list iter = _params;
              while (iter) {
                logic_arg_decl prm = (logic_arg_decl) iter->element.container;
                if (arguments.hasLogicFormal(prm->la_name)) {
                  DefineAddError(std::string("Variable ").append(prm->la_name)
                    .append(" is declared twice as formal of type ")
                    .append(_polyId).append("."));
                }
                else
                  arguments.addLogicFormal(prm->la_name,prm->la_type);
                iter = iter->next;
              };
            };
            state.freeRuleResult();
            if (!_params || _params->next) {
              DefineAddError("only one parameter is allowed "
                  "in a type parameter definition");
            }
            DefineGotoCase(TypeInvariantDefinition) 
          };
        };
      };
      arguments.resetAnnotContext();
      DefineAddErrorAndParse("expecting a ')' at the end "
          "of a type parameter definition");
    DefineParseCase(TypeInvariantDefinition)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TAssign) {
            TermOrPredicate* def = new TermOrPredicate;
            state.absorbRuleResult(def);
            def->setPredicate();
            DefineShift(EndTypeInvariant, *def, &TermOrPredicate::readToken);
          };
        };
      };
      CleanFormals(arguments);
      arguments.resetAnnotContext();
      DefineAddError("'=' expected in a type or class invariant definition");
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(EndTypeInvariant)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TSemiColon) {
            Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
                def(state.getRuleResult());
            extend_location_with(_loc, def->getLocation());
            predicate_named subdef = def->extractPredicate(arguments);
            if (!subdef) {
              CleanFormals(arguments);
              arguments.resetAnnotContext();
              DefineAddError("expecting a predicate");
              DefineReduceAndParse
            };
            logic_info info =
              logic_info_cons(
                arguments.makeLogicQualifiedName(_polyId),
                arguments.isExternCContext(),
                NULL, NULL /* string list tparams */,
                opt_none() /* logic_type option returned_type */,
                _params /* logic_arg_decl list profile */,
                logic_body_LBpred(subdef));
            CleanFormals(arguments);
            _params = NULL;
            arguments.addOverloadedLogicFunctions(_polyId,
                logic_info_dup(info), _isMethod);
            _globals.insertContainer(
              global_annotation_Dtype_annot(copy_loc(_loc), info));
            state.freeRuleResult();
            _polyId = "";
            clear();
            arguments.resetAnnotContext();
            DefineGotoCase(Begin)
          };
        };
      };
      DefineAddError("';' expected after a type invariant definition")
      DefineGotoCase(EndTypeInvariant)
    DefineParseCase(Inductive)
      DefineAddError("unimplemented case")
      DefineGotoCaseAndParse(BeforeSemiColon)

    DefineParseCase(Global)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TKeyword) {
          if (((const KeywordToken&) token).getType()
              == KeywordToken::TInvariant)
          DefineGotoCase(GlobalInvariant)
        };
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting 'invariant' after keyword 'global'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(GlobalInvariant)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TIdentifier) {
          _polyId = ((const IdentifierToken&)
              arguments.getContentToken()).content();
          absorbLoc(arguments.newTokenLocation());
          DefineGotoCase(GlobalInvariantBody)
        }
      };
      arguments.resetAnnotContext();
      DefineAddError("expecting an identifier after the construction "
          "'global invariant'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(GlobalInvariantBody)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TColon) {
            TermOrPredicate* rule = new TermOrPredicate;
            state.absorbRuleResult(rule);
            rule->setPredicate();
            DefineShift(EndGlobalInvariant, *rule, &TermOrPredicate::readToken);
          };
        };
      }
      arguments.resetAnnotContext();
      DefineAddError("':' expected after the construction "
          "'global invariant id'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(EndGlobalInvariant)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TSemiColon) {
            Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
              definition(state.getRuleResult());
            extend_location_with(_loc, definition->getLocation());
            predicate_named subdef = definition->extractPredicate(arguments);
            if (!subdef) {
              arguments.resetAnnotContext();
              DefineAddError("expecting a predicate");
              DefineReduceAndParse
            };
            logic_info info = logic_info_cons(
                arguments.makeLogicQualifiedName(_polyId),
                arguments.isExternCContext(),
                cons_container(logic_label_LogicLabel(strdup("here")), NULL),
                NULL /* string list tparams */,
                opt_none() /* logic_type option returned_type */,
                NULL /* logic_arg_decl list profile */,
                logic_body_LBpred(subdef));
            arguments.addOverloadedLogicFunctions(_polyId,
                logic_info_dup(info), _isMethod);
            _globals.insertContainer(global_annotation_Dinvariant(
                copy_loc(_loc), info));
            state.freeRuleResult();
            clear();
            DefineGotoCase(Begin)
          };
        };
      }
      arguments.resetAnnotContext();
      DefineAddError("':' expected after the construction "
          "'global invariant id'")
      DefineGotoCase(BeforeSemiColon)
    DefineParseCase(BeforeSemiColon)
      { AbstractToken token = arguments.queryToken();
        if (token.getType() == AbstractToken::TOperatorPunctuator) {
          if (((const OperatorPunctuatorToken&) token).getType()
              == OperatorPunctuatorToken::TSemiColon) {
            clear();
            arguments.resetAnnotContext();
            DefineGotoCase(Begin);
          }
        };
      };
      DefineGotoCase(BeforeSemiColon)
  };
  return result;
}

#if defined(__clang__) || defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

} // end of namespace Acsl