/*****
* program.cc
* Tom Prince
*
* The list of instructions used by the virtual machine.
*****/

#include <iostream>
#include "util.h"
#include "callable.h"
#include "program.h"


namespace vm {

static const char* opnames[] = {
#define OPCODE(name, type) #name,
#include "opcodes.h"
#undef OPCODE
};
static const Int numOps = (Int)(sizeof(opnames)/sizeof(char *));

static const char optypes[] = {
#define OPCODE(name, type) type,
#include "opcodes.h"
#undef OPCODE
};

#ifdef DEBUG_BLTIN
mem::map<bltin,string> bltinRegistry;

void registerBltin(bltin b, string s) {
 bltinRegistry[b] = s;
}
string lookupBltin(bltin b) {
 return bltinRegistry[b];
}
#endif


ostream& operator<< (ostream& out, const item& i)
{
 if (i.empty())
   return out << "empty";

 if (isdefault(i))
   return out << "default";

#if COMPACT
 // TODO: Try to guess the type from the value.
 Int n = get<Int>(i);
 double x = get<double>(i);
 void *p = get<void *>(i);

 if (n == BoolTruthValue)
   return out << "true";
 if (n == BoolFalseValue)
   return out << "false";

 if (std::abs(n) < 1000000)
   return out << n;

 if (fabs(x) < 1e30 and fabs(x) > 1e-30)
   return out << x;

 return out << "<item " << p << ">";
#else
 // TODO: Make a data structure mapping typeids to print functions.
 else if (i.type() == typeid(Int))
   out << "Int, value = " << get<Int>(i);
 else if (i.type() == typeid(double))
   out << "real, value = " << get<double>(i);
 else if (i.type() == typeid(string))
   out << "string, value = " << get<string>(i);
 else if (i.type() == typeid(callable))
   out << *(get<callable *>(i));
 else if (i.type() == typeid(vmFrame)) {
   out << "frame";
#  ifdef DEBUG_FRAME
   {
     vmFrame *f = get<vmFrame *>(i);
     if (f)
       out << " " << f->getName();
     else
       out << " <null>";
   }
#  endif
 }
 else
   out << "type " << demangle(i.type().name());
#endif

 return out;
}

void printInst(ostream& out, const program::label& code,
              const program::label& base)
{
 out.width(4);
 out << offset(base,code) << " ";

 Int i = (Int)code->op;

 if (i < 0 || i >= numOps) {
   out << "<<invalid op>>" << i;
   return;
 }
 out << opnames[i];

 switch (optypes[i]) {
   case 'n':
   {
     out << " " << get<Int>(*code);
     break;
   }

   case 't':
   {
     item c = code->ref;
     out << " " << c;
     break;
   }

   case 'b':
   {
#ifdef DEBUG_BLTIN
     string s=lookupBltin(get<bltin>(*code));
     out << " " << (!s.empty() ? s : "<unnamed>") << " ";
#endif
     break;
   }

   case 'o':
   {
     char f = out.fill('0');
     out << " i";
     out.width(4);
     out << offset(base,get<program::label>(*code));
     out.fill(f);
     break;
   }

   case 'l':
   {
#ifdef DEBUG_FRAME
     out << " " << get<lambda*>(*code)->name << " ";
#endif
     break;
   }

   default: {
     /* nothing else to do */
     break;
   }
 };
}

void print(ostream& out, program *base)
{
 program::label code = base->begin();
 bool active = true;
 while (active) {
   if (code->op == inst::ret ||
       code->op < 0 || code->op >= numOps)
     active = false;
   printInst(out, code, base->begin());
   out << '\n';
   ++code;
 }
}

} // namespace vm