%{
  /********************************************************************
   * AUTHORS: Vijay Ganesh, Trevor Hansen
   *
   * BEGIN DATE: July, 2006
   *
   * This file is modified version of the CVCL's smtlib.y file. Please
   * see CVCL license below
  ********************************************************************/
  
  /********************************************************************
   *
   * \file smtlib.y
   * 
   * Author: Sergey Berezin, Clark Barrett
   * 
   * Created: Apr 30 2005
   *
   * <hr>
   * Copyright (C) 2004 by the Board of Trustees of Leland Stanford
   * Junior University and by New York University. 
   *
   * License to use, copy, modify, sell and/or distribute this software
   * and its documentation for any purpose is hereby granted without
   * royalty, subject to the terms and conditions defined in the \ref
   * LICENSE file provided with this distribution.  In particular:
   *
   * - The above copyright notice and this permission notice must appear
   * in all copies of the software and related documentation.
   *
   * - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES,
   * EXPRESSED OR IMPLIED.  USE IT AT YOUR OWN RISK.
   * 
   * <hr>
  ********************************************************************/

#include "stp/cpp_interface.h"
#include "stp/Parser/LetMgr.h"
#include "stp/Parser/parser.h"

  using namespace stp;
  using std::cout;
  using std::cerr;
  using std::endl;

  // Suppress the bogus warning suppression in bison (it generates
  // compile error)
#undef __GNUC_MINOR__
  
  extern char* smttext;
  extern int smtlineno;
  extern int smtlex(void);

  int yyerror(const char *s) {
    cout << "syntax error: line " << smtlineno << "\n" << s << endl;
    cout << "  token: " << smttext << endl;
    FatalError("");
    return 1;
  }
  int yyerror(void* /*AssertsQuery*/, const char* s) { return yyerror(s); }

  ASTNode query;
#define YYLTYPE_IS_TRIVIAL 1
#define YYMAXDEPTH 104857600
#define YYERROR_VERBOSE 1
#define YY_EXIT_FAILURE -1
  %}

%parse-param {void* AssertsQuery}

%union {  
  // FIXME: Why is this not an UNSIGNED int?
  int uintval;                  /* for numerals in types. */

  // for BV32 BVCONST 
  unsigned long long ullval;

  struct {
    //stores the indexwidth and valuewidth
    //indexwidth is 0 iff type is bitvector. positive iff type is
    //array, and stores the width of the indexing bitvector
    unsigned int indexwidth;
    //width of the bitvector type
    unsigned int valuewidth;
  } indexvaluewidth;

  //ASTNode,ASTVec
  stp::ASTNode *node;
  stp::ASTVec *vec;
  std::string *str;
};

%start cmd

%type <indexvaluewidth> sort_symb sort_symbs
%type <node> status
%type <vec> bench_attributes an_formulas an_terms

%type <node> benchmark bench_attribute
%type <node> an_term an_nonbvconst_term an_formula 

%type <node> var fvar 
%type <str> user_value logic_name bench_name

%token <uintval> NUMERAL_TOK
%token <str> BVCONST_TOK
%token <node> BITCONST_TOK
%token <node> FORMID_TOK TERMID_TOK 
%token <str> STRING_TOK
%token <str> USER_VAL_TOK
%token SOURCE_TOK
%token CATEGORY_TOK
%token DIFFICULTY_TOK
%token BITVEC_TOK
%token ARRAY_TOK
%token SELECT_TOK
%token STORE_TOK
%token TRUE_TOK
%token FALSE_TOK
%token NOT_TOK
%token IMPLIES_TOK
%token ITE_TOK
%token AND_TOK
%token OR_TOK
%token XOR_TOK
%token IFF_TOK
%token EXISTS_TOK
%token FORALL_TOK
%token LET_TOK
%token FLET_TOK
%token NOTES_TOK
%token CVC_COMMAND_TOK
%token SORTS_TOK
%token FUNS_TOK
%token PREDS_TOK
%token EXTENSIONS_TOK
%token DEFINITION_TOK
%token AXIOMS_TOK
%token LOGIC_TOK
%token COLON_TOK
%token LBRACKET_TOK
%token RBRACKET_TOK
%token LPAREN_TOK
%token RPAREN_TOK
%token SAT_TOK
%token UNSAT_TOK
%token UNKNOWN_TOK
%token ASSUMPTION_TOK
%token FORMULA_TOK
%token STATUS_TOK
%token BENCHMARK_TOK
%token EXTRASORTS_TOK
%token EXTRAFUNS_TOK
%token EXTRAPREDS_TOK
%token LANGUAGE_TOK
%token DOLLAR_TOK
%token QUESTION_TOK
%token DISTINCT_TOK
%token SEMICOLON_TOK
%token EOF_TOK
%token EQ_TOK
 /*BV SPECIFIC TOKENS*/
%token NAND_TOK
%token NOR_TOK
%token NEQ_TOK
%token ASSIGN_TOK
%token BV_TOK
%token BOOLEAN_TOK
%token BVLEFTSHIFT_1_TOK
%token BVRIGHTSHIFT_1_TOK
%token BVARITHRIGHTSHIFT_TOK
%token BVPLUS_TOK
%token BVSUB_TOK
%token BVNOT_TOK //bvneg in CVCL
%token BVMULT_TOK
%token BVDIV_TOK
%token SBVDIV_TOK
%token BVMOD_TOK
%token SBVREM_TOK
%token SBVMOD_TOK
%token BVNEG_TOK //bvuminus in CVCL
%token BVAND_TOK
%token BVOR_TOK
%token BVXOR_TOK
%token BVNAND_TOK
%token BVNOR_TOK
%token BVXNOR_TOK
%token BVCONCAT_TOK
%token BVLT_TOK
%token BVGT_TOK
%token BVLE_TOK
%token BVGE_TOK
%token BVSLT_TOK
%token BVSGT_TOK
%token BVSLE_TOK
%token BVSGE_TOK
%token BVSX_TOK 
%token BVZX_TOK
%token BVROTATE_RIGHT_TOK
%token BVROTATE_LEFT_TOK
%token BVREPEAT_TOK 
%token BVCOMP_TOK

%token BOOLEXTRACT_TOK
%token BOOL_TO_BV_TOK
%token BVEXTRACT_TOK

%left LBRACKET_TOK RBRACKET_TOK

%%

cmd:
benchmark
{
  ASTNode assumptions;
  if($1 == NULL) 
    {
      assumptions = GlobalParserInterface->CreateNode(TRUE);
    } 
  else 
    {
      assumptions = *$1;
    }
      
  if(query.IsNull()) 
    {
      query = GlobalParserInterface->CreateNode(FALSE);
    }

  ((ASTVec*)AssertsQuery)->push_back(assumptions);
  ((ASTVec*)AssertsQuery)->push_back(query);
  delete $1;
  query = ASTNode();
  YYACCEPT;
}
;

benchmark:
LPAREN_TOK BENCHMARK_TOK bench_name bench_attributes RPAREN_TOK
{
  if($4 != NULL){
    if($4->size() > 1) 
      $$ = new ASTNode(GlobalParserInterface->CreateNode(AND,*$4));
    else if($4->size() ==1)
      $$ = new ASTNode((*$4)[0]);
     else
      $$ = new ASTNode(GlobalParserInterface->CreateNode(TRUE));     
    delete $4;
  }
  else {
    $$ = NULL;
  }
  delete $3; //discard the benchmarkname.
}
/*   | EOF_TOK */
/*     { */
/*     } */
;

bench_name:
STRING_TOK
{
}
;

bench_attributes:
bench_attribute
{
  $$ = new ASTVec;
  if ($1 != NULL) {
    $$->push_back(*$1);
    GlobalParserInterface->AddAssert(*$1);
    delete $1;
  }
}
| bench_attributes bench_attribute
{
  if ($1 != NULL && $2 != NULL) {
    $1->push_back(*$2);
    GlobalParserInterface->AddAssert(*$2);
    $$ = $1;
    delete $2;
  }
}
;

bench_attribute:
COLON_TOK ASSUMPTION_TOK an_formula
{
  //assumptions are like asserts
  $$ = $3;
}
| COLON_TOK FORMULA_TOK an_formula
{
  // Previously this would call AddQuery() on the negation.
  // But if multiple formula were (eroneously) present
  // it discarded all but the last formula. Allowing multiple 
  // formula and taking the conjunction of them along with all
  // the assumptions is what the other solvers do.  

  //assumptions are like asserts
  $$ = $3;
}
| COLON_TOK STATUS_TOK status
{
  $$ = NULL;
}
| COLON_TOK LOGIC_TOK logic_name
{
  if (!(0 == strcmp($3->c_str(),"QF_UFBV")  ||
        0 == strcmp($3->c_str(),"QF_BV") ||
        //0 == strcmp($3->c_str(),"QF_UF") ||
        0 == strcmp($3->c_str(),"QF_AUFBV"))) {
    yyerror("Wrong input logic:");
  }
  delete $3;
  $$ = NULL;
}
| COLON_TOK EXTRAFUNS_TOK LPAREN_TOK var_decls RPAREN_TOK
{
  $$ = NULL;
}
| COLON_TOK EXTRAPREDS_TOK LPAREN_TOK var_decls RPAREN_TOK
{
  $$ = NULL;
}
| annotation 
{
  $$ = NULL;
}
;

logic_name:
STRING_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK
{
  $$ = $1;
}
| STRING_TOK
{
  $$ = $1;
}
;

status:
SAT_TOK { 
  input_status = TO_BE_SATISFIABLE; 
  $$ = NULL; 
}
| UNSAT_TOK { 
  input_status = TO_BE_UNSATISFIABLE; 
  $$ = NULL; 
  }
| UNKNOWN_TOK 
{ 
  input_status = TO_BE_UNKNOWN; 
  $$ = NULL; 
}
;


/* annotations: */
/*     annotation */
/*     { */
/*     } */
/*   | annotations annotation */
/*     { */
/*     } */
/*   ; */
  
annotation:
attribute 
{
}
| attribute user_value 
{
}
;

user_value:
USER_VAL_TOK
{
  //cerr << "Printing user_value: " << *$1 << endl;
  delete $1;
}
;

attribute:
COLON_TOK SOURCE_TOK
{
}
| COLON_TOK CATEGORY_TOK
{
}
| COLON_TOK DIFFICULTY_TOK 
;

sort_symbs:
sort_symb 
{
  //a single sort symbol here means either a BitVec or a Boolean
  $$.indexwidth = $1.indexwidth;
  $$.valuewidth = $1.valuewidth;
}
| sort_symb sort_symb
{
  //two sort symbols mean f: type --> type
  $$.indexwidth = $1.valuewidth;
  $$.valuewidth = $2.valuewidth;
}
;

// There are some gulwani benchmarks that create multiple variables in the same header.
// Maybe you shouldn'.t..
var_decls:
var_decl
{}
|
var_decls var_decl
{}
;



var_decl:
LPAREN_TOK STRING_TOK sort_symbs RPAREN_TOK
{
  ASTNode s = stp::GlobalParserInterface->LookupOrCreateSymbol($2->c_str());
  //Sort_symbs has the indexwidth/valuewidth. Set those fields in
  //var
  s.SetIndexWidth($3.indexwidth);
  s.SetValueWidth($3.valuewidth);
  GlobalParserInterface->addSymbol(s);
  delete $2;
}
| LPAREN_TOK STRING_TOK RPAREN_TOK
{
  ASTNode s = stp::GlobalParserInterface->LookupOrCreateSymbol($2->c_str());
  s.SetIndexWidth(0);
  s.SetValueWidth(0);
  GlobalParserInterface->addSymbol(s);
  //Sort_symbs has the indexwidth/valuewidth. Set those fields in
  //var
  delete $2;
}
;

an_formulas:
an_formula
{
  $$ = new ASTVec;
  if ($1 != NULL) {
    $$->push_back(*$1);
    delete $1;
  }
}
|
an_formulas an_formula
{
  if ($1 != NULL && $2 != NULL) {
    $1->push_back(*$2);
    $$ = $1;
    delete $2;
  }
}
;

an_formula:   
TRUE_TOK
{
  $$ = new ASTNode(GlobalParserInterface->CreateNode(TRUE)); 
  assert(0 == $$->GetIndexWidth()); 
  assert(0 == $$->GetValueWidth());
}
| FALSE_TOK
{
  $$ = new ASTNode(GlobalParserInterface->CreateNode(FALSE)); 
  assert(0 == $$->GetIndexWidth()); 
  assert(0 == $$->GetValueWidth());
}
| fvar
{
  $$ = $1;
}
| LPAREN_TOK EQ_TOK an_term an_term RPAREN_TOK
{
  ASTNode * n = new ASTNode(GlobalParserInterface->CreateNode(EQ,*$3, *$4));
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK DISTINCT_TOK an_terms RPAREN_TOK
{
  using namespace stp;

  ASTVec terms = *$3;
  ASTVec forms;

  for(ASTVec::const_iterator it=terms.begin(),itend=terms.end();
      it!=itend; it++) {
    for(ASTVec::const_iterator it2=it+1; it2!=itend; it2++) {
      ASTNode n = (GlobalParserInterface->nf->CreateNode(NOT, GlobalParserInterface->CreateNode(EQ, *it, *it2)));

          
      forms.push_back(n); 
    }
  }

  if(forms.size() == 0) 
    FatalError("empty distinct");
 
  $$ = (forms.size() == 1) ?
    new ASTNode(forms[0]) :
    new ASTNode(GlobalParserInterface->CreateNode(AND, forms));

  delete $3;
}

| LPAREN_TOK BVSLT_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVSLT_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVSLT, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVSLE_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVSLE_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVSLE, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVSGT_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVSGT_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVSGT, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVSGE_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVSGE_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVSGE, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVLT_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVLT_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVLT, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVLE_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVLE_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVLE, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVGT_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVGT_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVGT, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK BVGE_TOK an_term an_term RPAREN_TOK
  //| LPAREN_TOK BVGE_TOK an_term an_term annotations RPAREN_TOK
{
  ASTNode * n = GlobalParserInterface->newNode(BVGE, *$3, *$4);
  $$ = n;
  delete $3;
  delete $4;      
}
| LPAREN_TOK an_formula RPAREN_TOK
{
  $$ = $2;
}
| LPAREN_TOK NOT_TOK an_formula RPAREN_TOK
{
  $$ = new ASTNode(GlobalParserInterface->nf->CreateNode(NOT, *$3));
  delete $3;
}
| LPAREN_TOK IMPLIES_TOK an_formula an_formula RPAREN_TOK
{
  $$ = GlobalParserInterface->newNode(IMPLIES, *$3, *$4);
  delete $3;
  delete $4;
}
| LPAREN_TOK ITE_TOK an_formula an_formula an_formula RPAREN_TOK
{
  $$ = new ASTNode(GlobalParserInterface->nf->CreateNode(ITE, *$3, *$4, *$5));
  delete $3;
  delete $4;
  delete $5;
}
| LPAREN_TOK AND_TOK an_formulas RPAREN_TOK
{
  $$ = new ASTNode(GlobalParserInterface->CreateNode(AND, *$3));
  delete $3;
}
| LPAREN_TOK OR_TOK an_formulas RPAREN_TOK
{
  $$ = new ASTNode(GlobalParserInterface->CreateNode(OR, *$3));
  delete $3;
}
| LPAREN_TOK XOR_TOK an_formula an_formula RPAREN_TOK
{
  $$ = GlobalParserInterface->newNode(XOR, *$3, *$4);
  delete $3;
  delete $4;
}
| LPAREN_TOK IFF_TOK an_formula an_formula RPAREN_TOK
{
  $$ = GlobalParserInterface->newNode(IFF, *$3, *$4);
  delete $3;
  delete $4;
}
| letexpr_mgmt an_formula RPAREN_TOK
  //| letexpr_mgmt an_formula annotations RPAREN_TOK
{
  $$ = $2;
  GlobalParserInterface->letMgr->CleanupLetIDMap();                      
}
;

letexpr_mgmt: 
LPAREN_TOK LET_TOK LPAREN_TOK QUESTION_TOK STRING_TOK an_term RPAREN_TOK
{
  //populate the hashtable from LET-var -->
  //LET-exprs and then process them:
  //
  //1. ensure that LET variables do not clash
  //1. with declared variables.
  //
  //2. Ensure that LET variables are not
  //2. defined more than once
  GlobalParserInterface->letMgr->LetExprMgr(*$5,*$6);
  
  delete $5;
  delete $6;      
}
| LPAREN_TOK FLET_TOK LPAREN_TOK DOLLAR_TOK STRING_TOK an_formula RPAREN_TOK 
{
  //Do LET-expr management
  GlobalParserInterface->letMgr->LetExprMgr(*$5,*$6);
  delete $5;
  delete $6;     
}

an_terms: 
an_term
{
  $$ = new ASTVec;
  if ($1 != NULL) {
    $$->push_back(*$1);
    delete $1;
  }
}
|
an_terms an_term
{
  if ($1 != NULL && $2 != NULL) {
    $1->push_back(*$2);
    $$ = $1;
    delete $2;
  }
}
;

an_term:
BVCONST_TOK
{
  $$ = new ASTNode(GlobalParserInterface->CreateBVConst(*$1, 10, 32));
  delete $1;
}
| BVCONST_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK
{
  $$ = new ASTNode(GlobalParserInterface->CreateBVConst(*$1,10,$3));
  delete $1;
}
| an_nonbvconst_term
{
$$ = $1;
}
;

an_nonbvconst_term: 
BITCONST_TOK { $$ = $1; }
| var
{
  $$ = new ASTNode((*$1));
  delete $1;
}
| LPAREN_TOK an_term RPAREN_TOK
{
  $$ = $2;
}
| SELECT_TOK an_term an_term
{
  //ARRAY READ
  // valuewidth is same as array, indexwidth is 0.
  ASTNode array = *$2;
  ASTNode index = *$3;
  unsigned int width = array.GetValueWidth();
  ASTNode * n = 
    new ASTNode(GlobalParserInterface->nf->CreateTerm(READ, width, array, index));
  $$ = n;
  delete $2;
  delete $3;
}
| STORE_TOK an_term an_term an_term
{
  //ARRAY WRITE
  unsigned int width = $4->GetValueWidth();
  ASTNode array = *$2;
  ASTNode index = *$3;
  ASTNode writeval = *$4;
  ASTNode write_term = GlobalParserInterface->nf->CreateArrayTerm(WRITE,$2->GetIndexWidth(),width,array,index,writeval);
  ASTNode * n = new ASTNode(write_term);
  $$ = n;
  delete $2;
  delete $3;
  delete $4;
}
| BVEXTRACT_TOK LBRACKET_TOK NUMERAL_TOK COLON_TOK NUMERAL_TOK RBRACKET_TOK an_term
{
  int width = $3 - $5 + 1;
  if (width < 0)
    yyerror("Negative width in extract");
      
  if((unsigned)$3 >= $7->GetValueWidth())
    yyerror("Parsing: Wrong width in BVEXTRACT\n");                      
      
  ASTNode hi  =  GlobalParserInterface->CreateBVConst(32, $3);
  ASTNode low =  GlobalParserInterface->CreateBVConst(32, $5);
  ASTNode output = GlobalParserInterface->nf->CreateTerm(BVEXTRACT, width, *$7,hi,low);
  ASTNode * n = new ASTNode(output);
  $$ = n;
  delete $7;
}
|  ITE_TOK an_formula an_term an_term 
{
  const unsigned int width = $3->GetValueWidth();
  $$ = new ASTNode(GlobalParserInterface->nf->CreateArrayTerm(ITE,$4->GetIndexWidth(), width,*$2, *$3, *$4));      
  delete $2;
  delete $3;
  delete $4;
}
|  BVCONCAT_TOK an_term an_term 
{
  const unsigned int width = $2->GetValueWidth() + $3->GetValueWidth();
  ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(BVCONCAT, width, *$2, *$3));
  $$ = n;
  delete $2;
  delete $3;
}
|  BVNOT_TOK an_term
{
  //this is the BVNOT (term) in the CVCL language
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(BVNOT, width, *$2));
  $$ = n;
  delete $2;
}
|  BVNEG_TOK an_term
{
  //this is the BVUMINUS term in CVCL langauge
  unsigned width = $2->GetValueWidth();
  ASTNode * n =  new ASTNode(GlobalParserInterface->nf->CreateTerm(BVUMINUS,width,*$2));
  $$ = n;
  delete $2;
}
|  BVAND_TOK an_term an_term 
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVAND, width, *$2, *$3);
  $$ = n;
  delete $2;
  delete $3;
}
|  BVOR_TOK an_term an_term 
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVOR, width, *$2, *$3); 
  $$ = n;
  delete $2;
  delete $3;
}
|  BVXOR_TOK an_term an_term 
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n =GlobalParserInterface->newNode(BVXOR, width, *$2, *$3);
  $$ = n;
  delete $2;
  delete $3;
}
  | BVXNOR_TOK an_term an_term
  {
//   (bvxnor s t) abbreviates (bvor (bvand s t) (bvand (bvnot s) (bvnot t)))

	
      unsigned int width = $2->GetValueWidth();
      ASTNode * n = new ASTNode(
      GlobalParserInterface->nf->CreateTerm( BVOR, width,
     GlobalParserInterface->nf->CreateTerm(BVAND, width, *$2, *$3),
     GlobalParserInterface->nf->CreateTerm(BVAND, width,
	     GlobalParserInterface->nf->CreateTerm(BVNOT, width, *$2),
     	 GlobalParserInterface->nf->CreateTerm(BVNOT, width, *$3)
     )));

      $$ = n;
      delete $2;
      delete $3;
  
  }
  |  BVCOMP_TOK an_term an_term
  {
	

  	ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(ITE, 1, 
  	GlobalParserInterface->nf->CreateNode(EQ, *$2, *$3),
  	GlobalParserInterface->CreateOneConst(1),
  	GlobalParserInterface->CreateZeroConst(1)));
  	
      $$ = n;
      delete $2;
      delete $3;
  }
  
    
|  BVSUB_TOK an_term an_term 
{
  const unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVSUB, width, *$2, *$3);
  $$ = n;
  delete $2;
  delete $3;
}
|  BVPLUS_TOK an_terms 
{
  const unsigned int width = (*$2)[0].GetValueWidth();
  ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(BVPLUS, width, *$2));
  $$ = n;
  delete $2;

}
|  BVMULT_TOK an_terms 
{
  const unsigned int width = (*$2)[0].GetValueWidth();
  ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(BVMULT, width, *$2));
  $$ = n;
  delete $2;
}

|      BVDIV_TOK an_term an_term  
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVDIV, width, *$2, *$3);
  $$ = n;

  delete $2;
  delete $3;
}
|      BVMOD_TOK an_term an_term
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVMOD, width, *$2, *$3);
  $$ = n;

  delete $2;
  delete $3;
}
|      SBVDIV_TOK an_term an_term
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(SBVDIV, width, *$2, *$3);
  $$ = n;

  delete $2;
  delete $3;
}
|      SBVREM_TOK an_term an_term
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(SBVREM, width, *$2, *$3);
  $$ = n;
  delete $2;
  delete $3;
}        
|      SBVMOD_TOK an_term an_term
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(SBVMOD, width, *$2, *$3);
  $$ = n;
  delete $2;
  delete $3;
}        
|  BVNAND_TOK an_term an_term 
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(BVNOT, width, GlobalParserInterface->nf->CreateTerm(BVAND, width, *$2, *$3)));
  $$ = n;
  delete $2;
  delete $3;
}
|  BVNOR_TOK an_term an_term 
{
  unsigned int width = $2->GetValueWidth();
  ASTNode * n = new ASTNode(GlobalParserInterface->nf->CreateTerm(BVNOT, width, GlobalParserInterface->nf->CreateTerm(BVOR, width, *$2, *$3))); 
  $$ = n;
  delete $2;
  delete $3;
}
|  BVLEFTSHIFT_1_TOK an_term an_term 
{
  // shifting left by who know how much?
  unsigned int w = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVLEFTSHIFT,w,*$2,*$3);
  $$ = n;
  delete $2;
  delete $3;
}
| BVRIGHTSHIFT_1_TOK an_term an_term 
{
  // shifting right by who know how much?
  unsigned int w = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVRIGHTSHIFT,w,*$2,*$3);
  $$ = n;
  delete $2;
  delete $3;
}
|  BVARITHRIGHTSHIFT_TOK an_term an_term
{
  // shifting arithmetic right by who know how much?
  unsigned int w = $2->GetValueWidth();
  ASTNode * n = GlobalParserInterface->newNode(BVSRSHIFT,w,*$2,*$3);
  $$ = n;
  delete $2;
  delete $3;
}
|  BVROTATE_LEFT_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK an_term 
{
      
  ASTNode *n;
  unsigned width = $5->GetValueWidth();
  unsigned rotate = $3;
  if (0 == rotate)
    {
      n = $5;
    }
  else if (rotate < width)
    {
      ASTNode high = GlobalParserInterface->CreateBVConst(32,width-1);
      ASTNode zero = GlobalParserInterface->CreateBVConst(32,0);
      ASTNode cut = GlobalParserInterface->CreateBVConst(32,width-rotate);
      ASTNode cutMinusOne = GlobalParserInterface->CreateBVConst(32,width-rotate-1);

      ASTNode top =  GlobalParserInterface->nf->CreateTerm(BVEXTRACT,rotate,*$5,high, cut);
      ASTNode bottom =  GlobalParserInterface->nf->CreateTerm(BVEXTRACT,width-rotate,*$5,cutMinusOne,zero);
      n =  new ASTNode(GlobalParserInterface->nf->CreateTerm(BVCONCAT,width,bottom,top));
      delete $5;
    }
  else
    {
      n = NULL; // remove gcc warning.
      yyerror("Rotate must be strictly less than the width.");
    }
      
  $$ = n;
      
}
|  BVROTATE_RIGHT_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK an_term 
{
      
  ASTNode *n;
  unsigned width = $5->GetValueWidth();
  unsigned rotate = $3;
  if (0 == rotate)
    {
      n = $5;
    }
  else if (rotate < width)
    {
      ASTNode high = GlobalParserInterface->CreateBVConst(32,width-1);
      ASTNode zero = GlobalParserInterface->CreateBVConst(32,0);
      ASTNode cut = GlobalParserInterface->CreateBVConst(32,rotate); 
      ASTNode cutMinusOne = GlobalParserInterface->CreateBVConst(32,rotate-1);

      ASTNode bottom =  GlobalParserInterface->nf->CreateTerm(BVEXTRACT,rotate,*$5,cutMinusOne, zero);
      ASTNode top =  GlobalParserInterface->nf->CreateTerm(BVEXTRACT,width-rotate,*$5,high,cut);
      n =  new ASTNode(GlobalParserInterface->nf->CreateTerm(BVCONCAT,width,bottom,top));
      delete $5;
    }
  else
    {
      n = NULL; // remove gcc warning.
      yyerror("Rotate must be strictly less than the width.");
    }
      
  $$ = n;
      
}
   |  BVREPEAT_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK an_term 
    {
	  unsigned count = $3;
	  if (count < 1)
	  	FatalError("One or more repeats please");

	  unsigned w = $5->GetValueWidth();  
      ASTNode n =  *$5;
      
      for (unsigned i =1; i < count; i++)
      {
      	  n = GlobalParserInterface->nf->CreateTerm(BVCONCAT,w*(i+1),n,*$5);
      }
       delete $5;
      $$ = new ASTNode(n);
    }
|  BVSX_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK an_term 
{
  unsigned w = $5->GetValueWidth() + $3;
  ASTNode width = GlobalParserInterface->CreateBVConst(32,w);
  ASTNode *n =  new ASTNode(GlobalParserInterface->nf->CreateTerm(BVSX,w,*$5,width));
  $$ = n;
  delete $5;
}
|  BVZX_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK an_term
{
  unsigned w = $5->GetValueWidth() + $3;
  ASTNode width = GlobalParserInterface->CreateBVConst(32,w);
  ASTNode *n =  new ASTNode(GlobalParserInterface->nf->CreateTerm(BVZX,w,*$5,width));
  $$ = n;
  delete $5;
}
;
  
sort_symb:
BITVEC_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK
{
  // Just return BV width.  If sort is BOOL, width is 0.
  // Otherwise, BITVEC[w] returns w. 
  //
  //((indexwidth is 0) && (valuewidth>0)) iff type is BV
  $$.indexwidth = 0;
  unsigned int length = $3;
  if(length > 0) {
    $$.valuewidth = length;
  }
  else {
    FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n");
  }
}
| ARRAY_TOK LBRACKET_TOK NUMERAL_TOK COLON_TOK NUMERAL_TOK RBRACKET_TOK
{
  unsigned int index_len = $3;
  unsigned int value_len = $5;
  if(index_len > 0) {
    $$.indexwidth = $3;
  }
  else {
    FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n");
  }

  if(value_len > 0) {
    $$.valuewidth = $5;
  }
  else {
    FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n");
  }
}
;

var:
FORMID_TOK 
{
  $$ = new ASTNode((*$1)); 
  delete $1;      
}
| TERMID_TOK
{
  $$ = new ASTNode((*$1));
  delete $1;
}
| QUESTION_TOK TERMID_TOK
{
  $$ = $2;
}
;

fvar:
DOLLAR_TOK FORMID_TOK
{
  $$ = $2; 
}
| FORMID_TOK
{
  $$ = new ASTNode((*$1)); 
  delete $1;      
}   
;
%%

namespace stp {
  int SMTParse(void* AssertsQuery) {
    return smtparse(AssertsQuery);
  }
}