/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef __UNIFY_HH__
#define __UNIFY_HH__

#include "funcdata.hh"

namespace ghidra {

class UnifyCPrinter;

class UnifyDatatype {
public:
  enum {
    op_type, var_type, const_type, block_type
  };
private:
  uint4 type;
  union {
    PcodeOp *op;
    Varnode *vn;
    uintb *cn;
    BlockBasic *bl;
  } storespot;
public:
  UnifyDatatype(void) { type = op_type; }
  UnifyDatatype(uint4 tp);
  UnifyDatatype(const UnifyDatatype &op2);
  UnifyDatatype &operator=(const UnifyDatatype &op2);
  ~UnifyDatatype(void);
  uint4 getType(void) const { return type; }
  void setOp(PcodeOp *o) { storespot.op = o; }
  PcodeOp *getOp(void) const { return storespot.op; }
  void setVarnode(Varnode *v) { storespot.vn = v; }
  Varnode *getVarnode(void) const { return storespot.vn; }
  void setBlock(BlockBasic *b) { storespot.bl = b; }
  BlockBasic *getBlock(void) const { return storespot.bl; }
  void setConstant(uintb val);
  uintb getConstant(void) const { return *storespot.cn; }
  void printVarDecl(ostream &s,int4 id,const UnifyCPrinter &cprinter) const;
  string getBaseName(void) const;
};

class UnifyState;

class RHSConstant {		// A construction that results in a constant on the right-hand side of an expression
public:
  virtual ~RHSConstant(void) {}
  virtual RHSConstant *clone(void)=0;
  virtual uintb getConstant(UnifyState &state) const=0;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const=0;
};

class ConstantNamed : public RHSConstant {
  int4 constindex;
public:
  ConstantNamed(int4 id) { constindex = id; }
  int4 getId(void) const { return constindex; }
  virtual RHSConstant *clone(void) { return new ConstantNamed(constindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantAbsolute : public RHSConstant {
  uintb val;			// The absolute value
public:
  ConstantAbsolute(uintb v) { val = v; }
  uintb getVal(void) const { return val; }
  virtual RHSConstant *clone(void) { return new ConstantAbsolute(val); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantNZMask : public RHSConstant { // A varnode's non-zero mask
  int4 varindex;
public:
  ConstantNZMask(int4 ind) { varindex = ind; }
  virtual RHSConstant *clone(void) { return new ConstantNZMask(varindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantConsumed : public RHSConstant { // A varnode's consume mask
  int4 varindex;
public:
  ConstantConsumed(int4 ind) { varindex = ind; }
  virtual RHSConstant *clone(void) { return new ConstantConsumed(varindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantOffset : public RHSConstant { // A varnode's offset
  int4 varindex;
public:
  ConstantOffset(int4 ind) { varindex = ind; }
  virtual RHSConstant *clone(void) { return new ConstantOffset(varindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantIsConstant : public RHSConstant { // TRUE if the varnode is constant
  int4 varindex;
public:
  ConstantIsConstant(int4 ind) { varindex = ind; }
  virtual RHSConstant *clone(void) { return new ConstantIsConstant(varindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantHeritageKnown : public RHSConstant { // A varnode's consume mask
  int4 varindex;
public:
  ConstantHeritageKnown(int4 ind) { varindex = ind; }
  virtual RHSConstant *clone(void) { return new ConstantHeritageKnown(varindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantVarnodeSize : public RHSConstant { // A varnode's size as an actual constant
  int4 varindex;
public:
  ConstantVarnodeSize(int4 ind) { varindex = ind; }
  virtual RHSConstant *clone(void) { return new ConstantVarnodeSize(varindex); }
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstantExpression : public RHSConstant {
  RHSConstant *expr1,*expr2;
  OpCode opc;
public:
  ConstantExpression(RHSConstant *e1,RHSConstant *e2,OpCode oc) { expr1 = e1; expr2 = e2; opc = oc; }
  virtual ~ConstantExpression(void);
  virtual RHSConstant *clone(void);
  virtual uintb getConstant(UnifyState &state) const;
  virtual void writeExpression(ostream &s,UnifyCPrinter &printstate) const;
};

class TraverseConstraint {
protected:
  int4 uniqid;
public:
  TraverseConstraint(int4 i) { uniqid = i; }
  virtual ~TraverseConstraint(void) {}
  //  int4 getId(void) const { return uniqid; }
};

class TraverseDescendState : public TraverseConstraint {
  bool onestep;			// true if first step has occurred
  list<PcodeOp *>::const_iterator iter;	// Different forward branches we could traverse
  list<PcodeOp *>::const_iterator enditer;
public:
  TraverseDescendState(int4 i) : TraverseConstraint(i) {}
  PcodeOp *getCurrentOp(void) const { return *iter; }
  void initialize(Varnode *vn) { onestep = false; iter = vn->beginDescend(); enditer = vn->endDescend(); }
  bool step(void) {
    if (onestep)
      ++iter;
    else
      onestep = true;
    return (iter!=enditer); }
};

class TraverseCountState : public TraverseConstraint {
  int4 state;
  int4 endstate;
public:
  TraverseCountState(int4 i) : TraverseConstraint(i) {}
  int4 getState(void) const { return state; }
  void initialize(int4 end) { state = -1; endstate = end; }
  bool step(void) { ++state; return (state != endstate); }
};

class TraverseGroupState : public TraverseConstraint {
  vector<TraverseConstraint *> traverselist;
  int4 currentconstraint;
  int4 state;
public:
  TraverseGroupState(int4 i) : TraverseConstraint(i) {}
  void addTraverse(TraverseConstraint *tc) { traverselist.push_back(tc); }
  TraverseConstraint *getSubTraverse(int4 slot) const { return traverselist[slot]; }
  int4 getCurrentIndex(void) const { return currentconstraint; }
  void setCurrentIndex(int4 val) { currentconstraint = val; }
  int4 getState(void) const { return state; }
  void setState(int4 val) { state = val; }
};

class UnifyConstraint {
  friend class ConstraintGroup;
protected:
  int4 uniqid;			// Unique identifier for constraint for retrieving state
  int4 maxnum;
  UnifyConstraint *copyid(const UnifyConstraint *op) { uniqid = op->uniqid; maxnum = op->maxnum; return this; }
public:
  virtual ~UnifyConstraint(void) {}
  int4 getId(void) const { return uniqid; }
  int4 getMaxNum(void) { return maxnum; }
  virtual UnifyConstraint *clone(void) const=0;
  virtual void initialize(UnifyState &state);
  virtual bool step(UnifyState &state)=0;
  virtual void buildTraverseState(UnifyState &state);
  virtual void setId(int4 &id) { uniqid = id; id += 1; }
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const {}
  virtual int4 getBaseIndex(void) const { return -1; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const=0;
  virtual bool isDummy(void) const { return false; }
  virtual void removeDummy(void) {}
};

class DummyOpConstraint : public UnifyConstraint {
  int4 opindex;
public:
  DummyOpConstraint(int4 ind) { maxnum = opindex = ind; }
  virtual UnifyConstraint *clone(void) const { return (new DummyOpConstraint(opindex))->copyid(this); }
  virtual bool step(UnifyState &state) { return true; }
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const { typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type); }
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const {}
  virtual bool isDummy(void) const { return true; }
};

class DummyVarnodeConstraint : public UnifyConstraint {
  int4 varindex;
public:
  DummyVarnodeConstraint(int4 ind) { maxnum = varindex = ind; }
  virtual UnifyConstraint *clone(void) const { return (new DummyVarnodeConstraint(varindex))->copyid(this); }
  virtual bool step(UnifyState &state) { return true; }
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const { typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type); }
  virtual int4 getBaseIndex(void) const { return varindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const {}
  virtual bool isDummy(void) const { return true; }
};

class DummyConstConstraint : public UnifyConstraint {
  int4 constindex;
public:
  DummyConstConstraint(int4 ind) { maxnum = constindex = ind; }
  virtual UnifyConstraint *clone(void) const { return (new DummyConstConstraint(constindex))->copyid(this); }
  virtual bool step(UnifyState &state) { return true; }
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const { typelist[constindex] = UnifyDatatype(UnifyDatatype::const_type); }
  virtual int4 getBaseIndex(void) const { return constindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const {}
  virtual bool isDummy(void) const { return true; }
};

class ConstraintBoolean : public UnifyConstraint { // Constant expression must evaluate to true (or false)
  bool istrue;
  RHSConstant *expr;
public:
  ConstraintBoolean(bool ist,RHSConstant *ex) { istrue = ist; expr = ex; maxnum = -1; }
  virtual ~ConstraintBoolean(void) { delete expr; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintBoolean(istrue,expr->clone()))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintVarConst : public UnifyConstraint { // Create a new varnode constant
  int4 varindex;
  RHSConstant *expr;
  RHSConstant *exprsz;
public:
  ConstraintVarConst(int4 ind,RHSConstant *ex,RHSConstant *sz) { varindex = ind; maxnum = ind; expr = ex; exprsz = sz; }
  virtual ~ConstraintVarConst(void);
  virtual UnifyConstraint *clone(void) const;
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return varindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintNamedExpression : public UnifyConstraint {
  int4 constindex;
  RHSConstant *expr;
public:
  ConstraintNamedExpression(int4 ind,RHSConstant *ex) { constindex = ind, expr=ex; maxnum = constindex; }
  virtual ~ConstraintNamedExpression(void) { delete expr; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintNamedExpression(constindex,expr->clone()))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return constindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOpCopy : public UnifyConstraint {
  int4 oldopindex;
  int4 newopindex;
public:
  ConstraintOpCopy(int4 oldind,int4 newind) { oldopindex = oldind; newopindex = newind; maxnum = (oldopindex > newopindex) ? oldopindex : newopindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOpCopy(oldopindex,newopindex))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return oldopindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOpcode : public UnifyConstraint {
  int4 opindex;
  vector<OpCode> opcodes;	// Which opcodes match
public:
  ConstraintOpcode(int4 ind,const vector<OpCode> &o) { maxnum = opindex = ind; opcodes = o; }
  const vector<OpCode> &getOpCodes(void) const { return opcodes; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOpcode(opindex,opcodes))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOpCompare : public UnifyConstraint {
  int4 op1index;
  int4 op2index;
  bool istrue;
public:
  ConstraintOpCompare(int4 op1ind,int4 op2ind,bool val) { op1index = op1ind; op2index = op2ind; istrue = val; maxnum = (op1index > op2index) ? op1index : op2index; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOpCompare(op1index,op2index,istrue))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return op1index; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOpInput : public UnifyConstraint {	// Move from op to one of its input varnodes
  int4 opindex;			// Which op
  int4 varnodeindex;		// Which input varnode
  int4 slot;			// Which slot to take
public:
  ConstraintOpInput(int4 oind,int4 vind,int4 sl) { opindex = oind; varnodeindex = vind; slot = sl; maxnum = (opindex > varnodeindex) ? opindex : varnodeindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOpInput(opindex,varnodeindex,slot))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return varnodeindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOpInputAny : public UnifyConstraint { // Move from op to ANY of its input varnodes
  int4 opindex;			// Which op
  int4 varnodeindex;		// What to label input varnode
public:
  ConstraintOpInputAny(int4 oind,int4 vind) { opindex = oind; varnodeindex = vind;  maxnum = (opindex > varnodeindex) ? opindex : varnodeindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOpInputAny(opindex,varnodeindex))->copyid(this); }
  virtual void initialize(UnifyState &state);
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return varnodeindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOpOutput : public UnifyConstraint {	// Move from op to its output varnode
  int4 opindex;			// Which op
  int4 varnodeindex;		// Label of output varnode
public:
  ConstraintOpOutput(int4 oind,int4 vind) { opindex = oind; varnodeindex = vind; maxnum = (opindex > varnodeindex) ? opindex : varnodeindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOpOutput(opindex,varnodeindex))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return varnodeindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintParamConstVal : public UnifyConstraint {
  int4 opindex;			// Which opcode
  int4 slot;			// Which slot to examine for constant
  uintb val;			// What value parameter must match
public:
  ConstraintParamConstVal(int4 oind,int4 sl,uintb v) { maxnum = opindex = oind; slot=sl; val = v; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintParamConstVal(opindex,slot,val))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintParamConst : public UnifyConstraint {
  int4 opindex;			// Which opcode
  int4 slot;			// Which slot to examine for constant
  int4 constindex;		// Which varnode is the constant
public:
  ConstraintParamConst(int4 oind,int4 sl,int4 cind) { opindex = oind; slot=sl; constindex = cind; maxnum = (opindex > constindex) ? opindex : constindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintParamConst(opindex,slot,constindex))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return constindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintVarnodeCopy : public UnifyConstraint {
  int4 oldvarindex;
  int4 newvarindex;
public:
  ConstraintVarnodeCopy(int4 oldind,int4 newind) { oldvarindex = oldind; newvarindex = newind; maxnum = (oldvarindex > newvarindex) ? oldvarindex : newvarindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintVarnodeCopy(oldvarindex,newvarindex))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return oldvarindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintVarCompare : public UnifyConstraint {
  int4 var1index;
  int4 var2index;
  bool istrue;
public:
  ConstraintVarCompare(int4 var1ind,int4 var2ind,bool val) { var1index = var1ind; var2index = var2ind; istrue = val; maxnum = (var1index > var2index) ? var1index : var2index; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintVarCompare(var1index,var2index,istrue))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return var1index; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintDef : public UnifyConstraint {
  int4 opindex;			// Where to store defining op
  int4 varindex;		// Which varnode to examine for def
public:
  ConstraintDef(int4 oind,int4 vind) { opindex = oind; varindex = vind; maxnum = (opindex > varindex) ? opindex : varindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintDef(opindex,varindex))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintDescend : public UnifyConstraint {
  int4 opindex;
  int4 varindex;
public:
  ConstraintDescend(int4 oind,int4 vind) { opindex = oind; varindex = vind; maxnum = (opindex > varindex) ? opindex : varindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintDescend(opindex,varindex))->copyid(this); }
  virtual void buildTraverseState(UnifyState &state);
  virtual void initialize(UnifyState &state);
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintLoneDescend : public UnifyConstraint {
  int4 opindex;
  int4 varindex;
public:
  ConstraintLoneDescend(int4 oind,int4 vind) { opindex = oind; varindex = vind; maxnum = (opindex > varindex) ? opindex : varindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintLoneDescend(opindex,varindex))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintOtherInput : public UnifyConstraint {
  int4 opindex;			// For a particular binary op
  int4 varindex_in;		// Given one of its input varnodes
  int4 varindex_out;		// Label the other input to op
public:
  ConstraintOtherInput(int4 oind,int4 v_in,int4 v_out) { maxnum = opindex = oind; varindex_in = v_in; varindex_out = v_out; 
    if (varindex_in > maxnum) maxnum = varindex_in; if (varindex_out > maxnum) maxnum = varindex_out; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintOtherInput(opindex,varindex_in,varindex_out))->copyid(this); }
  virtual int4 getBaseIndex(void) const { return varindex_out; }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintConstCompare : public UnifyConstraint {
  int4 const1index;		// Compare two constants resulting in a boolean
  int4 const2index;
  OpCode opc;
public:
  ConstraintConstCompare(int4 c1ind,int4 c2ind,OpCode oc) { const1index = c1ind; const2index = c2ind; opc = oc;
    maxnum = (const1index > const2index) ? const1index : const2index; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintConstCompare(const1index,const2index,opc))->copyid(this); }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual int4 getBaseIndex(void) const { return const1index; }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

// For a ConstraintGroup, the list of subconstraints must all match for the whole constraint to match.
// Constraints are tested first to last, i.e. testing for constraint n can assume that 1 thru n-1 match.
class ConstraintGroup : public UnifyConstraint {
protected:
  vector<UnifyConstraint *> constraintlist;
public:
  ConstraintGroup(void);
  virtual ~ConstraintGroup(void);
  UnifyConstraint *getConstraint(int4 slot) const { return constraintlist[slot]; }
  void addConstraint(UnifyConstraint *a);
  int4 numConstraints(void) const { return constraintlist.size(); }
  void deleteConstraint(int4 slot);
  void mergeIn(ConstraintGroup *b);
  virtual UnifyConstraint *clone(void) const;
  virtual void initialize(UnifyState &state);
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void buildTraverseState(UnifyState &state);
  virtual void setId(int4 &id);
  virtual int4 getBaseIndex(void) const { return constraintlist.back()->getBaseIndex(); }
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
  virtual void removeDummy(void);
};

// For a ConstraintOr, exactly one subconstraint needs to be true, for the whole constraint to match
// The constraints are tested sequentially, but there can be no dependency between subconstraints
class ConstraintOr : public ConstraintGroup {
public:
  virtual UnifyConstraint *clone(void) const;
  virtual void initialize(UnifyState &state);
  virtual bool step(UnifyState &state);
  virtual void buildTraverseState(UnifyState &state);
  virtual int4 getBaseIndex(void) const { return -1; } // Does not have a base
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

// Action constraints, these must always step exactly once (returning true), and do their action

class ConstraintNewOp : public UnifyConstraint {
  int4 newopindex;
  int4 oldopindex;
  bool insertafter;		// true if inserted AFTER oldop
  OpCode opc;			// new opcode
  int4 numparams;
public:
  ConstraintNewOp(int4 newind,int4 oldind,OpCode oc,bool iafter,int4 num);
  virtual UnifyConstraint *clone(void) const { return (new ConstraintNewOp(newopindex,oldopindex,opc,insertafter,numparams))->copyid(this); }
  virtual int4 getBaseIndex(void) const { return newopindex; }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintNewUniqueOut : public UnifyConstraint {
  int4 opindex;
  int4 newvarindex;
  int4 sizevarindex;		// Negative is specific size, Positive is varnode index (for size)
public:
  ConstraintNewUniqueOut(int4 oind,int4 newvarind,int4 sizeind);
  virtual UnifyConstraint *clone(void) const { return (new ConstraintNewUniqueOut(opindex,newvarindex,sizevarindex))->copyid(this); }
  virtual int4 getBaseIndex(void) const { return newvarindex; }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintSetInput : public UnifyConstraint {
  int4 opindex;
  RHSConstant *slot;
  int4 varindex;
public:
  ConstraintSetInput(int4 oind,RHSConstant *sl,int4 varind) { opindex = oind; slot=sl; varindex = varind; maxnum = (opindex > varindex) ? opindex : varindex; }
  virtual ~ConstraintSetInput(void) { delete slot; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintSetInput(opindex,slot->clone(),varindex))->copyid(this); }
  virtual int4 getBaseIndex(void) const { return varindex; }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintSetInputConstVal : public UnifyConstraint {
  int4 opindex;
  RHSConstant *slot;
  RHSConstant *val;
  RHSConstant *exprsz;
public:
  ConstraintSetInputConstVal(int4 oind,RHSConstant *sl,RHSConstant *v,RHSConstant *sz) { opindex=oind; slot=sl; val=v; exprsz = sz; maxnum = opindex; }
  virtual ~ConstraintSetInputConstVal(void);
  virtual UnifyConstraint *clone(void) const;
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintRemoveInput : public UnifyConstraint {
  int4 opindex;
  RHSConstant *slot;
public:
  ConstraintRemoveInput(int4 oind,RHSConstant *sl) { opindex = oind; slot = sl; maxnum = opindex; }
  virtual ~ConstraintRemoveInput(void) { delete slot; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintRemoveInput(opindex,slot->clone()))->copyid(this); }
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class ConstraintSetOpcode : public UnifyConstraint {
  int4 opindex;
  OpCode opc;
public:
  ConstraintSetOpcode(int4 oind,OpCode oc) { opindex = oind; opc = oc; maxnum = opindex; }
  virtual UnifyConstraint *clone(void) const { return (new ConstraintSetOpcode(opindex,opc))->copyid(this); }
  virtual int4 getBaseIndex(void) const { return opindex; }
  virtual bool step(UnifyState &state);
  virtual void collectTypes(vector<UnifyDatatype> &typelist) const;
  virtual void print(ostream &s,UnifyCPrinter &printstate) const;
};

class UnifyState {
  ConstraintGroup *container;	// containing unifyer
  vector<UnifyDatatype> storemap;
  vector<TraverseConstraint *> traverselist;
  Funcdata *fd;
public:
  UnifyState(ConstraintGroup *uni);
  ~UnifyState(void);
  int4 numTraverse(void) const { return traverselist.size(); }
  void registerTraverseConstraint(TraverseConstraint *t) { traverselist.push_back(t); }
  UnifyDatatype &data(int4 slot) { return storemap[slot]; }
  TraverseConstraint *getTraverse(int4 slot) const { return traverselist[slot]; }
  Funcdata *getFunction(void) const { return fd; }
  OpBehavior *getBehavior(OpCode opc);
  void setFunction(Funcdata *f) { fd = f; }
  void initialize(int4 id,Varnode *vn);
  void initialize(int4 id,PcodeOp *op);
};

class UnifyCPrinter {
  vector<UnifyDatatype> storemap;
  vector<string> namemap;
  int4 depth;
  int4 printingtype;		// 0 = standard rule
  string classname;		// Name of the printed class
  int4 opparam;
  vector<OpCode> opcodelist;	// List of opcodes that are recognized by rule
  void initializeBase(ConstraintGroup *g);
  void printGetOpList(ostream &s);
  void printRuleHeader(ostream &s);
  ConstraintGroup *grp;
public:
  UnifyCPrinter(void) { grp = (ConstraintGroup *)0; opparam = -1; printingtype=0; }
  int4 getDepth(void) const { return depth; }
  void incDepth(void) { depth += 1; }
  void decDepth(void) { depth -= 1; }
  void printIndent(ostream &s) const { for(int4 i=0;i<depth+1;++i) s << "  "; }
  void printAbort(ostream &s);
  void popDepth(ostream &s,int4 newdepth);
  const string &getName(int4 id) const { return namemap[id]; }
  void initializeRuleAction(ConstraintGroup *g,int4 opparam,const vector<OpCode> &olist);
  void initializeBasic(ConstraintGroup *g);
  void setClassName(const string &nm) { classname = nm; }
  void addNames(const map<string,int4> &nmmap);
  void printVarDecls(ostream &s) const;
  void print(ostream &s);
	     
};

// Rule language
// Identifiers are strict C identifiers that start with either:
//    'o'  for a pcode op
//    'v'  for a varnode
//    '#'  for a constant
//    'b'  for a basic block
//
// constraints
//    oname ( "opname" )        constrain "oname" to a given opcode
//    oname <-(1) vname         define varnode "vname" as input 1 of "oname"
//    oname <-(1) #45           make sure input 1 to "oname" is the constant value 45
//    oname <-(1) #name         define '#name' as the constant input 1 to "oname"
//    oname -> vname            define "vname" as the output varnode of op "oname"
//    vname <-   oname          define op "oname" is the op which writes "vname"
//    vname ->   oname          define op "oname" as (one of) ops that reads "vname"
//    vname ->!  oname          define op "oname" as the lone ops that reads "vname"
//    oname <- vname != vname1  define "vname" as other input to binary op "oname" besides "vname1"

//    vname1( == vname2)        verify that vname1 and vname2 are the same varnode
//    vname1( != vname2)        verify that vname1 and vname2 are not the same varnode
//    oname1( == oname2)        verify that ops are the same
//    vname1 -> vname2          define vname2 as a copy of vname1
//    oname <- vname            vname is (one of) the inputs to oname

//    statements end with ;
//    Group construct
//    (  statement
//       statement ...
//    }
//    OR construct
//    [  statement  |
//       statement ...
//    ]



//  vhi1 -> oadd1(+) ;
//  [  ( oadd1 -> vadd1 ->! oadd2(+) -> vreshi;
//       oadd1 <- vhizext1( != vhi1);
//       oadd2 <- vhizext2( != vadd1); ) |
//     ( oadd1 <- vtmpvn( != vhi1) <- oadd2(+) <-(0) vhizext1;
//       oadd2 <-(1) vhizext2;
//       oadd1 -> vreshi; ) ]
//  [  ( vhizext1 <- ozext(ZEXT);
//       vhizext2 -> vhi2;             ) |
//     ( vhizext2 <- ozext(ZEXT);
//       vhizext1 -> vhi2;             ) ]
// ozext <-(0) vzextin <- olesseq(<=) <-(0) vlessin <- oneg(*) <-(1) #-1
// [ (oneg <-(0) vtmp (== vlo1);  olesseq <-(1) vlo2; ) |
//   (oneg <-(1) vtmp (== vlo2);  oneg <-(0) vlo2; ) ]
// vlo1 -> oloadd(+) <- vtmp( != vlo1)( == vlo2);
// oloadd -> vreslo;

} // End namespace ghidra
#endif
