/* ###
 * 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.
 */
#ifdef CPUI_RULECOMPILE
#include "unify.hh"

namespace ghidra {

UnifyDatatype::UnifyDatatype(uint4 tp)

{
  type = tp;
  switch(type) {
  case op_type:
  case var_type:
  case block_type:
    break;
  case const_type:
    storespot.cn = new uintb;
    break;
  default:
    throw LowlevelError("Bad unify datatype");
  }
}

UnifyDatatype::UnifyDatatype(const UnifyDatatype &op2)

{
  type = op2.type;
  switch(type) {
  case op_type:
  case var_type:
  case block_type:
    break;
  case const_type:
    storespot.cn = new uintb; // Copy needs its own memory
    break;
  default:
    throw LowlevelError("Bad unify datatype");
  }
}

UnifyDatatype &UnifyDatatype::operator=(const UnifyDatatype &op2)

{
  switch(type) {
  case op_type:
  case var_type:
  case block_type:
    break;
  case const_type:
    delete storespot.cn;
    break;
  default:
    throw LowlevelError("Bad unify datatype");
  }
  type = op2.type;
  switch(type) {
  case op_type:
  case var_type:
  case block_type:
    break;
  case const_type:
    storespot.cn = new uintb; // Copy needs its own memory
    break;
  default:
    throw LowlevelError("Bad unify datatype");
  }
  return *this;
}

UnifyDatatype::~UnifyDatatype(void)

{
  switch(type) {
  case op_type:
  case var_type:
  case block_type:
    break;
  case const_type:
    delete storespot.cn;
    break;
  default:
    break;
  }
}

void UnifyDatatype::setConstant(uintb val)

{
  *storespot.cn = val;
}

void UnifyDatatype::printVarDecl(ostream &s,int4 id,const UnifyCPrinter &cprinter) const

{
  cprinter.printIndent(s);
  switch(type) {
  case op_type:
    s << "PcodeOp *" << cprinter.getName(id) << ';' << endl;
    break;
  case var_type:
    s << "Varnode *" << cprinter.getName(id) << ';' << endl;
    break;
  case block_type:
    s << "BlockBasic *" << cprinter.getName(id) << ';' << endl;
    break;
  case const_type:
    s << "uintb " << cprinter.getName(id) << ';' << endl;
    break;
  default:
    throw LowlevelError("Bad unify datatype");
  }
}

string UnifyDatatype::getBaseName(void) const

{
  switch(type) {
  case op_type:
    return "op";
  case var_type:
    return "vn";
  case block_type:
    return "bl";
  case const_type:
    return "cn";
  default:
    throw LowlevelError("Bad unify datatype");
  }
}

uintb ConstantNamed::getConstant(UnifyState &state) const

{
  return state.data(constindex).getConstant();
}

void ConstantNamed::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << printstate.getName(constindex);
}

uintb ConstantAbsolute::getConstant(UnifyState &state) const

{
  return val;
}

void ConstantAbsolute::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << "(uintb)0x" << hex << val;
}

uintb ConstantNZMask::getConstant(UnifyState &state) const

{
  Varnode *vn = state.data(varindex).getVarnode();
  return vn->getNZMask();
}

void ConstantNZMask::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << printstate.getName(varindex) << "->getNZMask()";
}

uintb ConstantConsumed::getConstant(UnifyState &state) const

{
  Varnode *vn = state.data(varindex).getVarnode();
  return vn->getConsume();
}

void ConstantConsumed::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << printstate.getName(varindex) << "->getConsume()";
}

uintb ConstantOffset::getConstant(UnifyState &state) const

{
  Varnode *vn = state.data(varindex).getVarnode();
  return vn->getOffset();
}

void ConstantOffset::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << printstate.getName(varindex) << "->getOffset()";
}

uintb ConstantIsConstant::getConstant(UnifyState &state) const

{
  Varnode *vn = state.data(varindex).getVarnode();
  return vn->isConstant() ? (uintb)1 : (uintb)0;
}

void ConstantIsConstant::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << "(uintb)" << printstate.getName(varindex) << "->isConstant()";
}

uintb ConstantHeritageKnown::getConstant(UnifyState &state) const

{
  Varnode *vn = state.data(varindex).getVarnode();
  return (uintb)(vn->isHeritageKnown() ? 1 : 0);
}

void ConstantHeritageKnown::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << "(uintb)" << printstate.getName(varindex) << "->isHeritageKnown()";
}

uintb ConstantVarnodeSize::getConstant(UnifyState &state) const

{
  Varnode *vn = state.data(varindex).getVarnode();
  return (uintb)vn->getSize();	// The size is the actual value
}

void ConstantVarnodeSize::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  s << "(uintb)" << printstate.getName(varindex) << "->getSize()";
}

ConstantExpression::~ConstantExpression(void)

{
  delete expr1;
  if (expr2 != (RHSConstant *)0)
    delete expr2;
}

RHSConstant *ConstantExpression::clone(void)

{
  RHSConstant *ecopy1 = expr1->clone();
  RHSConstant *ecopy2 = (RHSConstant *)0;
  if (expr2 != (RHSConstant *)0)
    ecopy2 = expr2->clone();
  return new ConstantExpression(ecopy1,ecopy2,opc);
}

uintb ConstantExpression::getConstant(UnifyState &state) const

{
  OpBehavior *behavior = state.getBehavior(opc);
  if (behavior->isSpecial())
    throw LowlevelError("Cannot evaluate special operator in constant expression");
  uintb res;
  if (behavior->isUnary()) {
    uintb ourconst1 = expr1->getConstant(state);
    res = behavior->evaluateUnary(sizeof(uintb),sizeof(uintb),ourconst1);
  }
  else {
    uintb ourconst1 = expr1->getConstant(state);
    uintb ourconst2 = expr2->getConstant(state);
    res = behavior->evaluateBinary(sizeof(uintb),sizeof(uintb),ourconst1,ourconst2);
  }
  return res;
}

void ConstantExpression::writeExpression(ostream &s,UnifyCPrinter &printstate) const

{
  int4 type;			// 0=binary 1=unarypre 2=unarypost 3=func
  string name;			// name of operator
  switch(opc) {
  case CPUI_INT_ADD:
    type=0;
    name = " + ";
    break;
  case CPUI_INT_SUB:
    type=0;
    name = " - ";
    break;
  case CPUI_INT_AND:
    type=0;
    name = " & ";
    break;
  case CPUI_INT_OR:
    type=0;
    name = " | ";
    break;
  case CPUI_INT_XOR:
    type=0;
    name = " ^ ";
    break;
  case CPUI_INT_MULT:
    type = 0;
    name = " * ";
    break;
  case CPUI_INT_DIV:
    type = 0;
    name = " / ";
    break;
  case CPUI_INT_EQUAL:
    type = 0;
    name = " == ";
    break;
  case CPUI_INT_NOTEQUAL:
    type = 0;
    name = " != ";
    break;
    //  case CPUI_INT_SLESS:
    //  case CPUI_INT_SLESSEQUAL:
  case CPUI_INT_LESS:
    type = 0;
    name = " < ";
    break;
  case CPUI_INT_LESSEQUAL:
    type = 0;
    name = " <= ";
    break;
    //  case CPUI_INT_ZEXT:
    //  case CPUI_INT_SEXT:
    //  case CPUI_INT_CARRY:
    //  case CPUI_INT_SCARRY:
    //  case CPUI_INT_SBORROW:
  case CPUI_INT_LEFT:
    type = 0;
    name = " << ";
    break;
  case CPUI_INT_RIGHT:
    type = 0;
    name = " >> ";
    break;
    //  case CPUI_INT_SRIGHT:
  default:
    throw LowlevelError("Unable to generate C for this expression element");
  }
  if (type==0) {
    s << '(';
    expr1->writeExpression(s,printstate);
    s << name;
    expr2->writeExpression(s,printstate);
    s << ')';
  }
  else if (type==1) {
    s << '(' << name;
    expr1->writeExpression(s,printstate);
    s << ')';
  }
  else if (type==2) {
    s << '(';
    expr1->writeExpression(s,printstate);
    s << name << ')';
  }
  else {
    s << name << '(';
    expr1->writeExpression(s,printstate);
    s << ')';
  }
}

void UnifyConstraint::initialize(UnifyState &state)

{				// Default initialization (with only 1 state)
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  traverse->initialize(1);	// Initialize with only one state
}

void UnifyConstraint::buildTraverseState(UnifyState &state)

{				// Build the default boolean traversal state
  if (uniqid != state.numTraverse())
    throw LowlevelError("Traverse id does not match index");
  TraverseConstraint *newt = new TraverseCountState(uniqid);
  state.registerTraverseConstraint(newt);
}

bool ConstraintBoolean::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  uintb ourconst = expr->getConstant(state);
  if (istrue)
    return (ourconst != 0);
  return (ourconst == 0);
}

void ConstraintBoolean::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (";
  expr->writeExpression(s,printstate);
  if (istrue)
    s << "== 0)";		// If false abort
  else
    s << "!= 0)";		// If true abort
  s << endl;
  printstate.printAbort(s);
}

ConstraintVarConst::~ConstraintVarConst(void)

{
  delete expr;
  if (exprsz != (RHSConstant *)0)
    delete exprsz;
}

UnifyConstraint *ConstraintVarConst::clone(void) const

{
  UnifyConstraint *res;
  RHSConstant *newexprsz = (RHSConstant *)0;
  if (exprsz != (RHSConstant *)0)
    newexprsz = exprsz->clone();
  res = (new ConstraintVarConst(varindex,expr->clone(),newexprsz))->copyid(this);
  return res;
}

bool ConstraintVarConst::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  uintb ourconst = expr->getConstant(state);
  Funcdata *fd = state.getFunction();
  int4 sz;
  if (exprsz != (RHSConstant *)0)
    sz = (int4)exprsz->getConstant(state);
  else
    sz = (int4)sizeof(uintb);
  ourconst &= calc_mask(sz);
  Varnode *vn = fd->newConstant( sz, ourconst );
  state.data(varindex).setVarnode(vn);
  return true;
}

void ConstraintVarConst::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintVarConst::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(varindex) << " = data.newConstant(";
  if (exprsz != (RHSConstant *)0)
    exprsz->writeExpression(s,printstate);
  else
    s << dec << (int4)sizeof(uintb);
  s << ',';
  expr->writeExpression(s,printstate);
  s << " & calc_mask(";
  exprsz->writeExpression(s,printstate);
  s << "));" << endl;
}

bool ConstraintNamedExpression::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  uintb ourconst = expr->getConstant(state);
  state.data(constindex).setConstant(ourconst);
  return true;
}

void ConstraintNamedExpression::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[constindex] = UnifyDatatype(UnifyDatatype::const_type);
}

void ConstraintNamedExpression::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(constindex) << " = ";
  expr->writeExpression(s,printstate);
  s << ';' << endl;
}

bool ConstraintOpCopy::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(oldopindex).getOp();
  state.data(newopindex).setOp(op);
  return true;
}

void ConstraintOpCopy::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[oldopindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[newopindex] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintOpCopy::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(newopindex) << " = " << printstate.getName(oldopindex) << ';' << endl;
}

bool ConstraintOpcode::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  for(int4 i=0;i<opcodes.size();++i)
    if (op->code() == opcodes[i]) return true;
  return false;
}

void ConstraintOpcode::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintOpcode::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (";
  if (opcodes.size()==1) {
    s << printstate.getName(opindex) << "->code() != CPUI_" << get_opname(opcodes[0]);
  }
  else {
    s << '(' << printstate.getName(opindex) << "->code() != CPUI_" << get_opname(opcodes[0]) << ')';
    for(int4 i=1;i<opcodes.size();++i) {
      s << "&&";
      s << '(' << printstate.getName(opindex) << "->code() != CPUI_" << get_opname(opcodes[i]) << ')';
    }
  }
  s << ')' << endl;
  printstate.printAbort(s);
}

bool ConstraintOpCompare::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op1 = state.data(op1index).getOp();
  PcodeOp *op2 = state.data(op2index).getOp();
  return ((op1==op2) == istrue);
}

void ConstraintOpCompare::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[op1index] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[op2index] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintOpCompare::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (" << printstate.getName(op1index);
  if (istrue)
    s << " != ";
  else
    s << " == ";
  s << printstate.getName(op2index) << ')' << endl;
  printstate.printAbort(s);
}

bool ConstraintOpInput::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = op->getIn(slot);
  state.data(varnodeindex).setVarnode(vn);
  return true;
}

void ConstraintOpInput::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varnodeindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintOpInput::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(varnodeindex) << " = " << printstate.getName(opindex);
  s << "->getIn(" << dec << slot << ");" << endl;
}

void ConstraintOpInputAny::initialize(UnifyState &state)

{				// Default initialization (with only 1 state)
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  PcodeOp *op = state.data(opindex).getOp();
  traverse->initialize(op->numInput());	// Initialize total number of inputs
}

bool ConstraintOpInputAny::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = op->getIn(traverse->getState());
  state.data(varnodeindex).setVarnode(vn);
  return true;
}

void ConstraintOpInputAny::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varnodeindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintOpInputAny::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "for(int4 i" << dec << printstate.getDepth() << "=0;i"<<printstate.getDepth()<< '<';
  s << printstate.getName(opindex) << "->numInput();++i" << printstate.getDepth() << ") {" << endl;
  printstate.incDepth();	// A permanent increase in depth
  printstate.printIndent(s);
  s << printstate.getName(varnodeindex) << " = " << printstate.getName(opindex) << "->getIn(i";
  s << (printstate.getDepth()-1) << ");" << endl;
}

bool ConstraintOpOutput::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = op->getOut();
  state.data(varnodeindex).setVarnode(vn);
  return true;
}

void ConstraintOpOutput::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varnodeindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintOpOutput::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(varnodeindex) << " = " << printstate.getName(opindex) << "->getOut();" << endl;
}

bool ConstraintParamConstVal::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = op->getIn(slot);
  if (!vn->isConstant()) return false;
  if (vn->getOffset() != (val&calc_mask(vn->getSize()))) return false;
  return true;
}

void ConstraintParamConstVal::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintParamConstVal::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (!" << printstate.getName(opindex) << "->getIn(" << dec << slot << ")->isConstant())" << endl;
  printstate.printAbort(s);
  printstate.printIndent(s);
  s << "if (" << printstate.getName(opindex) << "->getIn(" << dec << slot << ")->getOffset() != 0x";
  s << hex << val << " & calc_mask(" << printstate.getName(opindex) << "->getIn(" << dec;
  s << slot << ")->getSize()))" << endl;
  printstate.printAbort(s);
}

bool ConstraintParamConst::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = op->getIn(slot);
  if (!vn->isConstant()) return false;
  state.data(constindex).setConstant(vn->getOffset());
  return true;
}

void ConstraintParamConst::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[constindex] = UnifyDatatype(UnifyDatatype::const_type);
}

void ConstraintParamConst::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (!" << printstate.getName(opindex) << "->getIn(" << dec << slot << ")->isConstant())" << endl;
  printstate.printAbort(s);
  printstate.printIndent(s);
  s << printstate.getName(constindex) << " = ";
  s << printstate.getName(opindex) << "->getIn(" << dec << slot << ")->getOffset();" << endl;
}

bool ConstraintVarnodeCopy::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Varnode *vn = state.data(oldvarindex).getVarnode();
  state.data(newvarindex).setVarnode(vn);
  return true;
}

void ConstraintVarnodeCopy::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[oldvarindex] = UnifyDatatype(UnifyDatatype::var_type);
  typelist[newvarindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintVarnodeCopy::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(newvarindex) << " = " << printstate.getName(oldvarindex) << ';' << endl;
}

bool ConstraintVarCompare::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Varnode *vn1 = state.data(var1index).getVarnode();
  Varnode *vn2 = state.data(var2index).getVarnode();
  return ((vn1 == vn2)==istrue);
}

void ConstraintVarCompare::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[var1index] = UnifyDatatype(UnifyDatatype::var_type);
  typelist[var2index] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintVarCompare::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (" << printstate.getName(var1index);
  if (istrue)
    s << " != ";
  else
    s << " == ";
  s << printstate.getName(var2index) << ')' << endl;
  printstate.printAbort(s);
}

bool ConstraintDef::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Varnode *vn = state.data(varindex).getVarnode();
  if (!vn->isWritten()) return false;
  PcodeOp *op = vn->getDef();
  state.data(opindex).setOp(op);
  return true;
}

void ConstraintDef::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintDef::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (!" << printstate.getName(varindex) << "->isWritten())" << endl;
  printstate.printAbort(s);
  printstate.printIndent(s);
  s << printstate.getName(opindex) << " = " << printstate.getName(varindex) << "->getDef();" << endl;
}

void ConstraintDescend::buildTraverseState(UnifyState &state)

{
  if (uniqid != state.numTraverse())
    throw LowlevelError("Traverse id does not match index");
  TraverseConstraint *newt = new TraverseDescendState(uniqid);
  state.registerTraverseConstraint(newt);
}

void ConstraintDescend::initialize(UnifyState &state)

{
  TraverseDescendState *traverse = (TraverseDescendState *)state.getTraverse(uniqid);
  Varnode *vn = state.data(varindex).getVarnode();
  traverse->initialize(vn);
}

bool ConstraintDescend::step(UnifyState &state)

{
  TraverseDescendState *traverse = (TraverseDescendState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = traverse->getCurrentOp();
  state.data(opindex).setOp(op);
  return true;
}

void ConstraintDescend::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintDescend::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "list<PcodeOp *>::const_iterator iter" << dec << printstate.getDepth() << ",enditer" << printstate.getDepth() << ';' << endl;
  printstate.printIndent(s);
  s << "iter" << printstate.getDepth() << " = " << printstate.getName(varindex) << "->beginDescend();" << endl;
  printstate.printIndent(s);
  s << "enditer" << printstate.getDepth() << " = " << printstate.getName(varindex) << "->endDescend();" << endl;
  printstate.printIndent(s);
  s << "while(iter" << printstate.getDepth() << " != enditer" << printstate.getDepth() << ") {" << endl;
  printstate.incDepth();	// permanent increase in depth
  printstate.printIndent(s);
  s << printstate.getName(opindex) << " = *iter" << (printstate.getDepth()-1) << ';' << endl;
  printstate.printIndent(s);
  s << "++iter" << (printstate.getDepth()-1) << endl;
}

bool ConstraintLoneDescend::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Varnode *vn = state.data(varindex).getVarnode();
  PcodeOp *res = vn->loneDescend();
  if (res == (PcodeOp *)0) return false;
  state.data(opindex).setOp(res);
  return true;
}

void ConstraintLoneDescend::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintLoneDescend::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(opindex) << " = " << printstate.getName(varindex) << "->loneDescend();" << endl;
  printstate.printIndent(s);
  s << "if (" << printstate.getName(opindex) << " == (PcodeOp *)0)" << endl;
  printstate.printAbort(s);
}

bool ConstraintOtherInput::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = state.data(varindex_in).getVarnode();
  Varnode *res = op->getIn(1-op->getSlot(vn)); // Get the "other" input
  state.data(varindex_out).setVarnode(res);
  return true;
}

void ConstraintOtherInput::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varindex_in] = UnifyDatatype(UnifyDatatype::var_type);
  typelist[varindex_out] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintOtherInput::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(varindex_out) << " = " << printstate.getName(opindex) << "->getIn(1 - ";
  s << printstate.getName(opindex) << "->getSlot(" << printstate.getName(varindex_in) << "));" << endl;
}

bool ConstraintConstCompare::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  uintb c1 = state.data(const1index).getConstant();
  uintb c2 = state.data(const2index).getConstant();
  // This only does operations with boolean result
  OpBehavior *behavior = state.getBehavior(opc);
  uintb res = behavior->evaluateBinary(1,sizeof(uintb),c1,c2);
  return (res != 0);
}

void ConstraintConstCompare::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[const1index] = UnifyDatatype(UnifyDatatype::const_type);
  typelist[const2index] = UnifyDatatype(UnifyDatatype::const_type);
}

void ConstraintConstCompare::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "if (";
  switch(opc) {
  case CPUI_INT_EQUAL:
    s << printstate.getName(const1index) << " != " << printstate.getName(const2index);
    break;
  case CPUI_INT_NOTEQUAL:
    s << printstate.getName(const1index) << " == " << printstate.getName(const2index);
    break;
  default:
    s << "/* unimplemented constant operation */" ;
    break;
  }
  s << ')' << endl;
  printstate.printAbort(s);
}

ConstraintGroup::ConstraintGroup(void)

{
  maxnum = -1;
}

ConstraintGroup::~ConstraintGroup(void)

{
  for(uint4 i=0;i<constraintlist.size();++i)
    delete constraintlist[i];
  // We do not own the traverselist objects
}

void ConstraintGroup::addConstraint(UnifyConstraint *c)

{
  constraintlist.push_back(c);

  if (c->getMaxNum() > maxnum)
    maxnum = c->getMaxNum();
}

void ConstraintGroup::deleteConstraint(int4 slot)

{
  vector<UnifyConstraint *>::iterator iter = constraintlist.begin();
  iter = iter + slot;
  UnifyConstraint *mydel = *iter;
  constraintlist.erase(iter);
  delete mydel;
}

void ConstraintGroup::mergeIn(ConstraintGroup *b)

{ // Merge all the subconstraints from -b- into this
  for(int4 i=0;i<b->constraintlist.size();++i)
    addConstraint(b->constraintlist[i]);
  b->constraintlist.clear();	// Constraints are no longer controlled by -b-
  delete b;
}

UnifyConstraint *ConstraintGroup::clone(void) const

{
  ConstraintGroup *res = new ConstraintGroup();
  for(int4 i=0;i<constraintlist.size();++i) {
    UnifyConstraint *subconst = constraintlist[i]->clone();
    res->constraintlist.push_back(subconst);
  }
  res->copyid(this);
  return res;
}

void ConstraintGroup::initialize(UnifyState &state)

{
  TraverseGroupState *traverse = (TraverseGroupState *)state.getTraverse(uniqid);
  traverse->setState(-1);
}

bool ConstraintGroup::step(UnifyState &state)

{
  TraverseGroupState *traverse = (TraverseGroupState *)state.getTraverse(uniqid);

  UnifyConstraint *subconstraint;
  TraverseConstraint *subtraverse;
  int4 subindex;
  int4 stateint;
  int4 max = constraintlist.size();
  do {
    stateint = traverse->getState();
    if (stateint == 0) {		// Attempt a step at current constraint
      subindex = traverse->getCurrentIndex();
      subtraverse = traverse->getSubTraverse(subindex);
      subconstraint = constraintlist[subindex];
      if (subconstraint->step(state)) {
	traverse->setState(1);	// Now try a push
	subindex += 1;
	traverse->setCurrentIndex(subindex);
      }
      else {
	subindex -= 1;
	if (subindex < 0) return false;	// Popped off the top
	traverse->setCurrentIndex(subindex);
	traverse->setState(0);	// Try a step next
      }
    }
    else if (stateint == 1) {	// Push
      subindex = traverse->getCurrentIndex();
      subtraverse = traverse->getSubTraverse(subindex);
      subconstraint = constraintlist[subindex];
      subconstraint->initialize(state);
      traverse->setState(0);	// Try a step next
    }
    else {			// Very first time through
      traverse->setCurrentIndex(0);
      subindex = 0;
      subtraverse = traverse->getSubTraverse(subindex);
      subconstraint = constraintlist[subindex];
      subconstraint->initialize(state);	// Initialize the very first subcontraint
      traverse->setState(0);	// Now try a step
    }
  } while(subindex < max);
  subindex -= 1;
  traverse->setCurrentIndex(subindex);
  traverse->setState(0);	// Have full solution, do step next, to get to next solution
  return true;
}

void ConstraintGroup::collectTypes(vector<UnifyDatatype> &typelist) const

{
  for(int4 i=0;i<constraintlist.size();++i)
    constraintlist[i]->collectTypes(typelist);
}

void ConstraintGroup::buildTraverseState(UnifyState &state)

{
  if (uniqid != state.numTraverse())
    throw LowlevelError("Traverse id does not match index");
  TraverseGroupState *basetrav = new TraverseGroupState(uniqid);
  state.registerTraverseConstraint(basetrav);

  for(int4 i=0;i<constraintlist.size();++i) {
    UnifyConstraint *subconstraint = constraintlist[i];
    subconstraint->buildTraverseState(state);
    TraverseConstraint *subtraverse = state.getTraverse(subconstraint->getId());
    basetrav->addTraverse(subtraverse);
  }
}

void ConstraintGroup::setId(int4 &id)

{
  UnifyConstraint::setId(id);
  for(int4 i=0;i<constraintlist.size();++i)
    constraintlist[i]->setId(id);
}

void ConstraintGroup::print(ostream &s,UnifyCPrinter &printstate) const

{
  for(int4 i=0;i<constraintlist.size();++i)
    constraintlist[i]->print(s,printstate);
}

void ConstraintGroup::removeDummy(void)

{ // Remove any dummy constraints within us
  vector<UnifyConstraint *> newlist;

  for(int4 i=0;i<constraintlist.size();++i) {
    UnifyConstraint *cur = constraintlist[i];
    if (cur->isDummy()) {
      delete cur;
    }
    else {
      cur->removeDummy();
      newlist.push_back(cur);
    }
  }
  constraintlist = newlist;
}

UnifyConstraint *ConstraintOr::clone(void) const

{
  ConstraintOr *res = new ConstraintOr();
  for(int4 i=0;i<constraintlist.size();++i) {
    UnifyConstraint *subconst = constraintlist[i]->clone();
    res->constraintlist.push_back(subconst);
  }
  res->copyid(this);
  return res;
}

void ConstraintOr::initialize(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  traverse->initialize(constraintlist.size());
}

bool ConstraintOr::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  int4 stateind = traverse->getState();
  UnifyConstraint *cur;
  if (stateind == -1) { // First time through
    if (!traverse->step()) return false;
    stateind = traverse->getState();
    cur = getConstraint(stateind);
    cur->initialize(state);
  }
  else
    cur = getConstraint(stateind);
  for(;;) {
    if (cur->step(state)) return true;
    if (!traverse->step()) break;
    stateind = traverse->getState();
    cur = getConstraint(stateind);
    cur->initialize(state);
  }
  return false;
}

void ConstraintOr::buildTraverseState(UnifyState &state)

{
  if (uniqid != state.numTraverse())
    throw LowlevelError("Traverse id does not match index in or");
  TraverseCountState *trav = new TraverseCountState(uniqid);
  state.registerTraverseConstraint(trav);

  for(int4 i=0;i<constraintlist.size();++i) {
    UnifyConstraint *subconstraint = constraintlist[i];
    subconstraint->buildTraverseState(state);
  }
}

void ConstraintOr::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "for(i" << dec << printstate.getDepth() << "=0;i" << printstate.getDepth() << '<';
  s << (int4)constraintlist.size() << ";++i" << printstate.getDepth() << ") {" << endl;
  printstate.incDepth();	// permanent increase in depth
  for(int4 i=0;i<constraintlist.size();++i) {
    printstate.printIndent(s);
    if (i != 0)
      s << "else ";
    if (i != constraintlist.size()-1)
      s << "if (i" << printstate.getDepth()-1 << " == " << dec << i << ") ";
    s << '{' << endl;
    int4 olddepth = printstate.getDepth();
    printstate.incDepth();
    constraintlist[i]->print(s,printstate);
    printstate.popDepth(s,olddepth);
  }
}

ConstraintNewOp::ConstraintNewOp(int4 newind,int4 oldind,OpCode oc,bool iafter,int4 num)

{
  newopindex = newind;
  oldopindex = oldind;
  opc = oc;
  insertafter = iafter;
  numparams = num;
  maxnum = (newind > oldind) ? newind : oldind;
}

bool ConstraintNewOp::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Funcdata *fd = state.getFunction();
  PcodeOp *op = state.data(oldopindex).getOp();
  PcodeOp *newop = fd->newOp(numparams,op->getAddr());
  fd->opSetOpcode(newop,opc);
  if (insertafter)
    fd->opInsertAfter(newop,op);
  else
    fd->opInsertBefore(newop,op);
  return true;
}

void ConstraintNewOp::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[newopindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[oldopindex] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintNewOp::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(newopindex) << " = data.newOp(" << dec << numparams;
  s << ',' << printstate.getName(oldopindex) << "->getAddr());" << endl;
  printstate.printIndent(s);
  s << "data.opSetOpcode(" << printstate.getName(newopindex) << ",CPUI_" << get_opname(opc) << ");" << endl;
  s << "data.opInsert";
  if (insertafter)
    s << "After(";
  else
    s << "Before(";
  s << printstate.getName(newopindex) << ',' << printstate.getName(oldopindex) << ");" << endl;
}

ConstraintNewUniqueOut::ConstraintNewUniqueOut(int4 oind,int4 newvarind,int4 sizeind)

{
  opindex = oind;
  newvarindex = newvarind;
  sizevarindex = sizeind;
  maxnum = (opindex > newvarindex) ? opindex : newvarindex;
  if (sizevarindex > maxnum)
    maxnum = sizevarindex;
}

bool ConstraintNewUniqueOut::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Funcdata *fd = state.getFunction();
  PcodeOp *op = state.data(opindex).getOp();
  int4 sz;
  if (sizevarindex < 0)
    sz = -sizevarindex;		// A specific size
  else {
    Varnode *sizevn = state.data(sizevarindex).getVarnode();
    sz = sizevn->getSize();
  }
  Varnode *newvn = fd->newUniqueOut(sz,op);
  state.data(newvarindex).setVarnode(newvn);
  return true;
}

void ConstraintNewUniqueOut::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[newvarindex] = UnifyDatatype(UnifyDatatype::var_type);
  if (sizevarindex >= 0)
    typelist[sizevarindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintNewUniqueOut::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << printstate.getName(newvarindex) << " = data.newUniqueOut(";
  if (sizevarindex < 0)
    s << dec << -sizevarindex;
  else
    s << printstate.getName(sizevarindex) << "->getSize()";
  s << ',' << printstate.getName(opindex) << ");" << endl;
}

bool ConstraintSetInput::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Funcdata *fd = state.getFunction();
  PcodeOp *op = state.data(opindex).getOp();
  Varnode *vn = state.data(varindex).getVarnode();
  int4 slt = (int4)slot->getConstant(state);
  fd->opSetInput(op,vn,slt);
  return true;
}

void ConstraintSetInput::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintSetInput::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "data.opSetInput(" << printstate.getName(opindex) << ',' << printstate.getName(varindex);
  s << ',';
  slot->writeExpression(s,printstate);
  s << ");" << endl;
}

ConstraintSetInputConstVal::~ConstraintSetInputConstVal(void)

{
  delete val;
  delete slot;
  if (exprsz != (RHSConstant *)0)
    delete exprsz;
}

UnifyConstraint *ConstraintSetInputConstVal::clone(void) const

{
  RHSConstant *newexprsz = (RHSConstant *)0;
  if (exprsz != (RHSConstant *)0)
    newexprsz = exprsz->clone();
  UnifyConstraint *res;
  res = (new ConstraintSetInputConstVal(opindex,slot->clone(),val->clone(),newexprsz))->copyid(this);
  return res;
}

bool ConstraintSetInputConstVal::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Funcdata *fd = state.getFunction();
  PcodeOp *op = state.data(opindex).getOp();
  uintb ourconst = val->getConstant(state);
  int4 sz;
  if (exprsz != (RHSConstant *)0)
    sz = (int4)exprsz->getConstant(state);
  else
    sz = (int4)sizeof(uintb);
  int4 slt = (int4)slot->getConstant(state);
  fd->opSetInput(op,fd->newConstant(sz,ourconst&calc_mask(sz)),slt);
  return true;
}

void ConstraintSetInputConstVal::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
  //  typelist[varindex] = UnifyDatatype(UnifyDatatype::var_type);
}

void ConstraintSetInputConstVal::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "data.opSetInput(" << printstate.getName(opindex) << ",data.newConstant(";
  if (exprsz != (RHSConstant *)0)
    exprsz->writeExpression(s,printstate);
  else
    s << dec << (int4)sizeof(uintb);
  s << ",calc_mask(";
  if (exprsz != (RHSConstant *)0)
    exprsz->writeExpression(s,printstate);
  else
    s << dec << (int4)sizeof(uintb);
  s << ")&";
  val->writeExpression(s,printstate);
  s << "),";
  slot->writeExpression(s,printstate);
  s << ");" << endl;
}

bool ConstraintRemoveInput::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Funcdata *fd = state.getFunction();
  PcodeOp *op = state.data(opindex).getOp();
  int4 slt = (int4)slot->getConstant(state);
  fd->opRemoveInput(op,slt);
  return true;
}

void ConstraintRemoveInput::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintRemoveInput::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "data.opRemoveInput(" << printstate.getName(opindex) << ',';
  slot->writeExpression(s,printstate);
  s << ");" << endl;
}

bool ConstraintSetOpcode::step(UnifyState &state)

{
  TraverseCountState *traverse = (TraverseCountState *)state.getTraverse(uniqid);
  if (!traverse->step()) return false;
  Funcdata *fd = state.getFunction();
  PcodeOp *op = state.data(opindex).getOp();
  fd->opSetOpcode(op,opc);
  return true;
}

void ConstraintSetOpcode::collectTypes(vector<UnifyDatatype> &typelist) const

{
  typelist[opindex] = UnifyDatatype(UnifyDatatype::op_type);
}

void ConstraintSetOpcode::print(ostream &s,UnifyCPrinter &printstate) const

{
  printstate.printIndent(s);
  s << "data.opSetOpcode(" << printstate.getName(opindex) << ",CPUI_" << get_opname(opc) << ");" << endl;
}

UnifyState::UnifyState(ConstraintGroup *uni)

{
  container = uni;
  storemap.resize(container->getMaxNum()+1,UnifyDatatype());
  container->collectTypes(storemap);
  container->buildTraverseState(*this);
}

UnifyState::~UnifyState(void)

{
  for(int4 i=0;i<traverselist.size();++i)
    delete traverselist[i];
}

OpBehavior *UnifyState::getBehavior(OpCode opc)

{ // Get the behavior associated with a particular opcode
  Architecture *glb = fd->getArch();
  return glb->inst[opc]->getBehavior();
}

void UnifyState::initialize(int4 id,Varnode *vn)

{ // Enter an initial varnode (root) starting point
  storemap[id].setVarnode(vn);
}

void UnifyState::initialize(int4 id,PcodeOp *op)

{ // Enter an initial op (root) starting point
  storemap[id].setOp(op);
}

void UnifyCPrinter::initializeBase(ConstraintGroup *g)

{
  grp = g;
  depth = 0;
  namemap.clear();
  storemap.clear();
  opparam = -1;
  opcodelist.clear();
  int4 maxop = g->getMaxNum();
  storemap.resize(maxop+1,UnifyDatatype());
  
  g->collectTypes(storemap);

  for(int4 i=0;i<=maxop;++i) {
    ostringstream s;
    s << storemap[i].getBaseName() << dec << i;
    namemap.push_back(s.str());
  }
}

void UnifyCPrinter::printGetOpList(ostream &s)

{ // Print the getOpList method of the new rule
  s << "void " << classname << "::getOpList(vector<uint4> &oplist) const" << endl;
  s << endl;
  s << '{' << endl;
  for(int4 i=0;i<opcodelist.size();++i) {
    s << "  oplist.push_back(CPUI_" << get_opname(opcodelist[i]) << ");" << endl;
  }
  s << '}' << endl;
  s << endl;
}

void UnifyCPrinter::printRuleHeader(ostream &s)

{ // print the header for the applyOp method of the rule
  s << "int " << classname << "::applyOp(PcodeOp *" << namemap[opparam] << ",Funcdata &data)" << endl;
  s << endl;
  s << '{' << endl;
}

void UnifyCPrinter::printAbort(ostream &s)

{
  depth += 1;
  printIndent(s);
  if (depth >1)
    s << "continue;";
  else {
    if (printingtype == 0)
      s << "return 0;";
    else
      s << "return false;";
  }
  depth -= 1;
  s << endl;
}

void UnifyCPrinter::popDepth(ostream &s,int4 newdepth)

{
  while(depth != newdepth) {
    depth -= 1;
    printIndent(s);
    s << '}' << endl;
  }
}

void UnifyCPrinter::printVarDecls(ostream &s) const

{ // Print the variables declarations
  for(int4 i=0;i<namemap.size();++i) {
    if (i==opparam) continue;
    storemap[i].printVarDecl(s,i,*this);
  }
  if (namemap.size() != 0)
    s << endl;			// Extra blank line
}

void UnifyCPrinter::initializeRuleAction(ConstraintGroup *g,int4 opp,const vector<OpCode> &oplist)

{
  initializeBase(g);
  printingtype = 0;
  classname = "DummyRule";

  opparam = opp;
  opcodelist = oplist;
}

void UnifyCPrinter::initializeBasic(ConstraintGroup *g)

{
  initializeBase(g);
  printingtype = 1;
  opparam = -1;
}

void UnifyCPrinter::addNames(const map<string,int4> &nmmap)

{
  map<string,int4>::const_iterator iter;

  for(iter=nmmap.begin();iter!=nmmap.end();++iter) {
    int4 slot = (*iter).second;
    if (namemap.size() <= slot)
      throw LowlevelError("Name indices do not match constraint");
    namemap[slot] = (*iter).first;
  }
}

void UnifyCPrinter::print(ostream &s)

{
  if (printingtype == 0) {
    printGetOpList(s);
    s << endl;
    printRuleHeader(s);
    printVarDecls(s);
    grp->print(s,*this);
    printIndent(s);
    s << "return 1;" << endl;	// Found a complete match
    if (depth != 0) {
      popDepth(s,0);
      printIndent(s);
      s << "return 0;" << endl;	// Could never find a complete match
    }
    s << '}' << endl;
  }
  else if (printingtype == 1) {
    printVarDecls(s);
    grp->print(s,*this);
    printIndent(s);
    s << "return true;" << endl;
    if (depth != 0) {
      popDepth(s,0);
      printIndent(s);
      s << "return false;" << endl;
    }
    s << '}' << endl;
  }
}

} // End namespace ghidra
#endif
