/*****
 * newexp.cc
 * Andy Hammerlindl 2003/07/28
 *
 * Handles the abstract syntax for expressions the create new objects,
 * such as record, array, and function constructors.
 *****/

#include "newexp.h"
#include "stm.h"
#include "runtime.h"
#include "runarray.h"
#include "coenv.h"
#include "inst.h"

using namespace types;
using trans::coder;
using trans::coenv;
using vm::inst;

namespace absyntax {

void printFrame(frame *f) {
  if (f == 0) {
    cerr << '0';
  }
  else {
    cerr << f << " of ";
    printFrame(f->getParent());
  }
}

void newRecordExp::prettyprint(ostream &out, Int indent) {
  prettyname(out, "newRecordExp", indent, getPos());
}

types::ty *newRecordExp::transFromTyEntry(position pos, coenv &e,
                                          trans::tyEntry *ent)
{
  types::ty *t = ent->t;
  if (t->kind == ty_error)
    return t;
  else if (t->kind != ty_record) {
    em.error(pos);
    em << "type '" << *t << "' is not a structure";
    return primError();
  }

  // Put the enclosing frame on the stack.
  if (!e.c.encodeParent(pos, ent)) {
    em.error(pos);
    em << "allocation of struct '" << *t << "' is not in a valid scope";
    return primError();
  }

  record *r = dynamic_cast<record *>(t);
  assert(r);

  // Encode the allocation.
  e.c.encode(inst::makefunc,r->getInit());
  e.c.encode(inst::popcall);

  return t;
}

types::ty *newRecordExp::trans(coenv &e)
{
  return transFromTyEntry(getPos(), e, result->transAsTyEntry(e, 0));
}

types::ty *newRecordExp::getType(coenv &e)
{
  types::ty *t;
  { // Suppress errors while probing the record type.
    auto modeGuard = em.modeGuard(ErrorMode::SUPPRESS);
    t = result->trans(e);
  }
  if (t->kind != ty_error && t->kind != ty_record)
    return primError();
  else
    return t;
}

void newArrayExp::prettyprint(ostream &out, Int indent)
{
  prettyname(out,"newArrayExp",indent, getPos());

  celltype->prettyprint(out, indent+1);
  if (dimexps) dimexps->prettyprint(out, indent+1);
  if (dims) dims->prettyprint(out, indent+1);
  if (ai) ai->prettyprint(out, indent+1);
}

types::ty *newArrayExp::trans(coenv &e)
{
  types::ty *c = celltype->trans(e);
  if (c->kind == ty_void) {
    em.error(getPos());
    em << "cannot declare array of type void";
    return primError();
  }

  if (dims)
    c = dims->truetype(c);

  if (ai) {
    ai->transToType(e, c);
    return c;
  } else if (dimexps || dims) {
    if (dimexps) {
      for (size_t i = 0; i < dimexps->size(); ++i) {
        (*dimexps)[i]->transToType(e, types::primInt());
        c = new types::array(c);
      }
    }
    if (dims) {
      for (size_t i = 0; i < dims->size(); ++i) {
        e.c.encode(inst::intpush,0);
      }
    }
    e.c.encode(inst::intpush,
               (Int) ((dimexps ? dimexps->size():0)
                      + (dims ? dims->size():0)));
    e.c.encode(inst::builtin, run::newDeepArray);

    return c;
  } else {
    em.compiler(getPos());
    em << "new array expression must have either dims or dimexps";
    return primError();
  }
}

types::ty *newArrayExp::getType(coenv &e)
{
  types::ty *c = celltype->trans(e);
  if (c->kind == ty_void) {
    return primError();
  }
  if (dims)
    c = dims->truetype(c);

  if (dimexps) {
    Int depth = (Int)dimexps->size();
    while (depth > 0) {
      c = new types::array(c);
      depth--;
    }
  }

  return c;
}


} // namespace absyntax
