/*****
* stack.h
* Andy Hammerlindl 2002/06/27
*
* The general stack machine  used to run compiled camp code.
*****/

#ifndef STACK_H
#define STACK_H

#include <iostream>

#include "errormsg.h"
#include "vm.h"
#include "item.h"
#include "absyn.h"

namespace vm {

struct func;
class program;
struct lambda;
class importInitMap;

struct bpinfo : public gc {
 fileinfo f;
 absyntax::runnable *r;

 bpinfo(const string& filename, size_t lineNum,
        absyntax::runnable *r=NULL) :
   f(fileinfo(filename,lineNum)), r(r) {}
};

inline bool operator == (const bpinfo& a, const bpinfo& b)
{
 return a.f == b.f;
}

extern mem::list<bpinfo> bplist;

class runnable;

extern bool indebugger;

class stack {
public:
 using vars_t = vmFrame*;

 struct importInitMap {
   virtual ~importInitMap() {}
   virtual lambda *operator[](string) = 0;
 };

private:
 // stack for operands
 using stack_t = mem::vector<item>;
 stack_t theStack;

 void draw(ostream& out);

 // The initializer functions for imports, indexed by name.
 importInitMap *initMap;

 // The stack stores a map of initialized imported modules by name, so that
 // each module is initialized only once and each import refers to the same
 // instance.
 using importInstanceMap = mem::map<const mem::string, vmFrame*>;
 importInstanceMap instMap;

 // One can associate an environment to embedded code while running.
 trans::coenv *e;

 // Debugger variables:
 char debugOp;
 position lastPos, breakPos;
 bool newline;

 // Move arguments from stack to frame.
 void marshall(size_t args, stack::vars_t vars);

public:
 stack() : e(0), debugOp(0), lastPos(nullPos),
           breakPos(nullPos), newline(false) {};

 virtual ~stack() {};

 void setInitMap(importInitMap *i) {
   initMap=i;
 }

 void setEnvironment(trans::coenv *e) {
   this->e=e;
 }

 trans::coenv *getEnvironment() {
   return e;
 }

 // Runs a lambda.  If vars is non-null, it is used to store the variables of
 // the lambda.  Otherwise, the method allocates a closure only if needed.
 void runWithOrWithoutClosure(lambda *l, vars_t vars, vars_t parent);

 // Executes a function on top of the stack.
 void run(func *f);

 void breakpoint(absyntax::runnable *r=NULL);
 void debug();

 // Put an import (indexed by filename and optional template
 // parameter signature) on top of the stack, initializing it if necessary.
 void loadModule(string index, Int numPushedParents = 0);

 // These are so that built-in functions can easily manipulate the stack
 void push(item next) {
   theStack.push_back(next);
 }
 template <typename T>
 void push(T next) {
   push((item)next);
 }
 item top() {
   return theStack.back();
 }
 item pop() {
   item ret = theStack.back();
   theStack.pop_back();
   return ret;
 }
 template <typename T>
 T pop()
 {
   return get<T>(pop());
 }
};

inline item pop(stack* s)
{
 return s->pop();
}

template <typename T>
inline T pop(stack* s)
{
 return get<T>(pop(s));
}

template <typename T>
inline T pop(stack* s, T defval)
{
 item it=pop(s);
 return isdefault(it) ? defval : get<T>(it);
}

class interactiveStack : public stack {
 vars_t globals;
 size_t globals_size;
public:
 interactiveStack();

 // Run a codelet, a small piece of code that uses globals as its frame.
 void run(lambda *codelet);
};

} // namespace vm

#endif // STACK_H