//----------------------------------------------------------------------------
//  QCompiler.cpp
//  Compile intermediate codes
//  $Date: 2003/01/17 19:37:43 $
//  $Revision: 1.3 $
//----------------------------------------------------------------------------
#ifdef __BORLANDC__
#include <vcl.h>
#pragma hdrstop
#endif //__BORLANDC__

#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include "QCompiler.h"
#include "QParseInfo.h"

//----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma package(smart_init)
#endif //__BORLANDC__

using namespace std;

//----------------------------------------------------------------------------
//  Syntax of intermediate code  (see also `QCompiler.h')
//----------------------------------------------------------------------------
const char  QCompiler::OP_BRA    = '(';
const char  QCompiler::OP_KET    = ')';
const char  QCompiler::QB_BRA    = '[';
const char  QCompiler::QB_KET    = ']';
const char  QCompiler::DELIM     = ',';

const QCompiler::QGATES QCompiler::qgates[] = {
  {"CNOT" ,2,{ at_qbit, at_qbit, at_null}},
  {"ROT"  ,2,{ at_qbit, at_real, at_null}},
  {"CROT" ,3,{ at_qbit, at_qbit, at_real}},
  {"H"    ,1,{ at_qbit, at_null, at_null}},
  {"MEASURE"    ,1,{ at_qbit, at_null, at_null}},
  {"SWAP" ,2,{ at_qbit, at_qbit, at_null}},
  {"PAULIX"    ,1,{ at_qbit, at_null, at_null}},
  {"PAULIY"    ,1,{ at_qbit, at_null, at_null}},
  {"PAULIZ"    ,1,{ at_qbit, at_null, at_null}},
  {"NOT"  ,1,{ at_qbit, at_null, at_null}},
  {"CCNOT",3,{ at_qbit, at_qbit, at_qbit}},
  {"INIT" ,1,{ at_real, at_null, at_null}}
};

const int QCompiler::opn_max = sizeof(QCompiler::qgates)/sizeof(QCompiler::QGATES);

//----------------------------------------------------------------------------
/**
 *  Constructor
 */
QCompiler::QCompiler(void) {
  mError = false;
  mState = false;
}
//----------------------------------------------------------------------------
/**
 *  Constructor with input stream
 */
QCompiler::QCompiler(std::istream &is) {
  mError = false;
  mState = false;
  ReadFromStream(is);
}
//----------------------------------------------------------------------------
/**
 *  Destructor
 */
QCompiler::~QCompiler() {
}
//----------------------------------------------------------------------------
/**
 *  Read intermediate code from stream
 */
void
QCompiler::ReadFromStream(std::istream &is) {
  char        c1;
  std::string st1    = "";
  int         nlcode = -1; // -1:unknown 0:CR+LF 1:LF+CR 2:CR 3:LF

  mLines.clear();
  mError = mState = false;

  while (!is.eof()) {
    is.read(&c1, 1);
    if (c1 == 0x0d || c1 == 0x0a) {
      switch (nlcode) {
      case -1: // unknown
        if (c1 == 0x0d) {
          if (0x0a == is.peek()) {
            nlcode = 0;
          } else {
            nlcode = 2;
            goto push;
          }
        } else  {// c1 == 0x0a
          if (0x0d == is.peek()) {
            nlcode = 1;
          } else {
            nlcode = 3;
            goto push;
          }
        }
      case 0:
      case 1:
        is.read(&c1, 1); // skip one char
      case 2:
      case 3:
push:
        mLines.push_back(st1);
        st1 = "";
        break;
      default:
        break;
      }
    } else {
      st1 += c1;
    }
  }
}
//----------------------------------------------------------------------------
/**
 *  Compile
 */
bool
QCompiler::Compile(void) {
  int         i, vec_size = mLines.size();
  for (i = 0; i < vec_size; i++) {
    if (mLines[i].length() == 0 || mLines[i][0] == '#') {
      // skip
      continue;
    }
    QParseInfo pinfo = ParseOneLine(mLines[i]);
    try {
      if (!CompileOneLine(pinfo)) throw pinfo;
    } catch (QParseInfo einfo) {
      mError = true;
      CatchError(einfo, i); // is abstract virtual member
      break;
    }
  }
  mState = true;
  return !mError;
}
//----------------------------------------------------------------------------
/**
 *  Parser
 */
QParseInfo
QCompiler::ParseOneLine(const std::string &strline) const {
  int                         i, j;   // tmp
  std::string                 s1, s2; // tmp
  std::vector<std::string>    args;
  int                         comint;
  std::vector<int>            targets;
  double                      rotation = 0;

  // Com(Arg)
  if (!ExtractComArg(strline, OP_BRA, OP_KET, s1, s2)) {
    return QParseInfo(QParseInfo::er_syntax_error);
  }

  // Extract aa, bb, and cc from "aa,bb,cc"
  if (!ExtractField(s2, DELIM, args)) {
    // discard s2
    return QParseInfo(QParseInfo::er_syntax_error);
  }

  // Get command number from command string
  if ((comint = GetComint(s1)) == -1) {
    // discard s1
    return QParseInfo(QParseInfo::er_unknown_operation);
  }

  j = args.size();
  if (j < qgates[comint].arg_num) {
    return QParseInfo(QParseInfo::er_lack_of_arguments);
  }
  if (j > qgates[comint].arg_num) {
    return QParseInfo(QParseInfo::er_too_many_arguments);
  }

  for (i = 0; i < j; i++) {
    switch (qgates[comint].arg_types[i]) {
    case at_qbit:
      if (ExtractComArg(args[i], QB_BRA, QB_KET, s1, s2) && s1 == "q" && s2 != "") {
        targets.push_back(std::atoi(s2.c_str()));
      } else {
        return QParseInfo(QParseInfo::er_invalid_arguments);
      }
      break;
    case at_real:
      rotation = std::atof(args[i].c_str());
      targets.push_back(std::atof(args[i].c_str()));
    case at_oprt:
      break;
    default:
      break;
    }
  }
  return QParseInfo(comint, targets, rotation);
}
//----------------------------------------------------------------------------
/**
 *  ex) bra = '[', ket = ']', strline = "q[20]" --> get "q" and "20" as string
 *      bra = '(', ket = ')', strline = CNOT(q[0],q[1])
 *       --> get "CNOT" and "q[0],q[1]" as string
 */
bool
QCompiler::ExtractComArg(const std::string &strline, const char bra, const char ket,
                         std::string &Com,
                         std::string &Arg) const {
  int pos_bra = strline.find(bra);
  int pos_ket = strline.find(ket);
  int length  = strline.length();

  Com = Arg = "";
  if (pos_bra < 1 || pos_ket - pos_bra < 1 || pos_ket != length - 1) {
    return false;
  }
  Com = strline.substr(0, pos_bra);
  if (pos_ket - pos_bra >= 2) {
    Arg = strline.substr(pos_bra + 1, pos_ket - pos_bra - 1);
  }
  return true;
}
//----------------------------------------------------------------------------
/**
 *  Null field is not allowd, but "" (zero string) is OK.
 *
 *  ex) delim = ',', strline = "q[0],q[4],10"
 *       --> get "q[0]", "q[4]" and "10" as vector<string>, and return true
 *      delim = ',', strline = "aa,,bb" (having null field between aa and bb)
 *       --> return false
 *      delim = ',', strline = ""
 *       --> return true
 */
bool
QCompiler::ExtractField(const std::string &strline, const char delim,
                        std::vector<std::string> &Args) const {
  int i;
  int fpos = 0;
  int length = strline.length();
  std::string s1 = "";

  Args.clear();
  for (i = 0; i < length; i++) {
    if (strline.at(i) == delim) {
      if (i > fpos) {
        Args.push_back(s1);
        s1 = "";
        fpos = i + 1;
      } else {
        return false;
      }
    } else {
      s1 += strline.at(i);
    }
  }
  if (s1.length() > 0) {
    Args.push_back(s1);
  }
  return true;
}
//----------------------------------------------------------------------------
/**
 *  Get command number from command string
 */
int
QCompiler::GetComint(const std::string &_str) const {
  for (int i = 0; i < opn_max; i++)if (_str == qgates[i].opstr) return i;
  return -1;
}
//----------------------------------------------------------------------------
