/*****
* access.cc
* Andy Hammerlindl 2003/12/03
* Describes an "access," a representation of where a variable will be
* stored at runtime, so that read, write, and call instructions can be
* made.
*****/

#include "access.h"
#include "frame.h"
#include "coder.h"
#include "callable.h"

using vm::item;

namespace trans {

/* access */
access::~access()
{}

/* identAccess */
void identAccess::encode(action act, position pos, coder& e)
{
 if (act != CALL) {
   access::encode(act, pos, e);
 }
 // else - do nothing
}


/* bltinAccess */
static void bltinError(position pos)
{
 em.error(pos);
 em << "built-in functions cannot be modified";
}

void bltinAccess::encode(action act, position pos, coder &e)
{
 switch (act) {
   case READ:
     e.encode(inst::constpush,(item)(vm::callable*)new vm::bfunc(f));
     break;
   case WRITE:
     bltinError(pos);
     break;
   case CALL:
     e.encode(inst::builtin, f);
     break;
 }
}

void bltinAccess::encode(action act, position pos, coder &e, frame *)
{
 e.encode(inst::pop);
 encode(act, pos, e);
}

/* callableAccess */
void callableAccess::encode(action act, position pos, coder &e)
{
 switch (act) {
   case READ:
     e.encode(inst::constpush, (item)f);
     break;
   case WRITE:
     bltinError(pos);
     break;
   case CALL:
     this->encode(READ, pos, e);
     e.encode(inst::popcall);
     break;
 }
}

void callableAccess::encode(action act, position pos, coder &e, frame *)
{
 e.encode(inst::pop);
 encode(act, pos, e);
}


/* frameAccess */
void frameAccess::encode(action act, position pos, coder &e)
{
 if (act == READ) {
   if (!e.encode(f)) {
     em.compiler(pos);
     em << "encoding frame out of context";
   }
 }
 else
   access::encode(act, pos, e);
}

void frameAccess::encode(action act, position pos, coder &e, frame *top)
{
 if (act == READ) {
   if (!e.encode(f, top)) {
     em.compiler(pos);
     em << "encoding frame out of context";
   }
 }
 else
   access::encode(act, pos, e, top);
}

/* localAccess */
static void frameError(position pos) {
 // A local variable is being used when its frame is not active.
 em.error(pos);
 em << "static use of dynamic variable";
}

void localAccess::encode(action act, position pos, coder &e)
{
 // Get the active frame of the virtual machine.
 frame *active = e.getFrame();
 if (level == active) {
   e.encode(act == WRITE ? inst::varsave : inst::varpush,
            offset);
 }
 else if (e.encode(level)) {
   e.encode(act == WRITE ? inst::fieldsave : inst::fieldpush,
            offset);
 }
 else {
   frameError(pos);
 }

 if (act == CALL)
   e.encode(inst::popcall);
}

void localAccess::encode(action act, position pos, coder &e, frame *top)
{
 if (e.encode(level,top)) {
   e.encode(act == WRITE ? inst::fieldsave : inst::fieldpush,
            offset);
   if (act == CALL)
     e.encode(inst::popcall);
 }
 else {
   frameError(pos);
 }
}


void qualifiedAccess::encode(action act, position pos, coder &e)
{
 qualifier->encode(READ, pos, e);
 field->encode(act, pos, e, qualifierLevel);
}

void qualifiedAccess::encode(action act, position pos, coder &e, frame *top)
{
 qualifier->encode(READ, pos, e, top);
 field->encode(act, pos, e, qualifierLevel);
}


} // namespace trans