// joycub V2 - an interpeter for the programming language Joy
// (c) 2004-2006 Sergey Zubkov 
// program.cc -
static char RCSID[] ="$Id: program.cc,v 1.10 2006/02/27 18:44:01 cubbi Exp $";

#include "program.h"
#include "object.h"
#include "bool.h"
#include "int.h"
#include "op.h"
#include "float.h"
#include "char.h"
#include "string.h"
#include "set.h"
#include "list.h"
#include "symtab.h"
#include "exceptions.h"

extern JC_symtab* SYMTAB;

JC_Program::JC_Program(JC_list & jl) : JC_list(jl) {};
JC_Program::JC_Program() : JC_list() {};
JC_Program::JC_Program(JC_objref* jr) : JC_list(jr->to_agg()) {};
JC_Program::~JC_Program() {}

// Execute the program with a given input stack.
// operators are actually executed, via symtab
// literals are simply pushed to stack
void JC_Program::execute( JC_Stack & STACK) {
//cerr << "executing " << to_string() << endl;
// const char* debug_s = to_string().data();
 if( contents.size() <1 ) return;
    for( vector<JC_objref*>::iterator it = contents.begin();
                    it != contents.end(); it++ ) {
		if( (*it)->get_type() == OPERATOR )
				SYMTAB->execute((*it)->get_opnum(),STACK);
		else
			   	STACK.push_back(new JC_objref(**it));
	}
}

void JC_Program::concat(JC_Program & p) {
	   	JC_list::concat(p);
}

bool JC_Program::has_dot(void) const {
	for(vector<JC_objref*>::const_iterator it = contents.begin();
			it!=contents.end(); it++) {
		if( (*it)->key() == "." ) return true;
	}
	return false;
}

// Parse states
const int PARSE_LIBRA_N	= 0; // inside LIBRA, expecting name
const int PARSE_LIBRA_E	= 1; // inside LIBRA, expecting "=="
const int PARSE_LIBRA_B	= 2; // inside LIBRA, expecting body
const int PARSE_DEF_N	= 3; // after DEFINE, expecting name
const int PARSE_DEF_E	= 4; // after DEFINE, expecting "=="
const int PARSE_DEF_B	= 5; // after DEFINE, expecting body
const int PARSE_IN_SET	= 6; // after {
const int PARSE_IN_LIST	= 7; // after [
const int PARSE_IN_SET_IN_LIST	= 8; // after { inside a list
const int PARSE_NORMAL	= 9; // initial state
typedef int parse_type;
// Build the program out of the tokens
JC_Program::JC_Program(Token_list& tl) {
	parse_type state=PARSE_NORMAL;

	// array of ints for the growing set
	vector<JC_objref*> * set_target = new vector<JC_objref*>;

	// array of objects for the growing list
	vector<JC_objref*> * list_target = new vector<JC_objref*>;
	// array of lists for recursion
	vector< vector<JC_objref*>* > stack_of_targets;
	vector<JC_objref*> * finished_target;

	string define_name;
	string define_body;

//cerr << "new parse " << endl;
	for(vector<pair<obj_type,string> >::const_iterator it = tl.begin();
					it != tl.end(); it++) {
//cerr << "parsing " << (*it).second << " state="<< state << endl;
		switch(state) {
			case PARSE_LIBRA_N: 
				if( (*it).second == "HIDE" ) break;
				if( (*it).second == "END" ) break;
				if( (*it).first!= OPERATOR )
					throw runtime_error("Expecting a name.");
				define_name = (*it).second;
				state=PARSE_LIBRA_E;
				break;
			case PARSE_LIBRA_E:
				if( (*it).second != "==" )
					throw runtime_error("Expecting == ");
				state=PARSE_LIBRA_B;
				break;
			case PARSE_LIBRA_B:
				if( (*it).second == "." ) { // end LIBRA
					SYMTAB->new_userdef(define_name, define_body); 
					define_name="";
					define_body="";
					state=PARSE_NORMAL;
				} else if( (*it).second == ";" 
								|| (*it).second == "IN" 
								) { // end definition
					SYMTAB->new_userdef(define_name, define_body); 
					define_name="";
					define_body="";
					state=PARSE_LIBRA_N;
				} else {	// add one more word to the def body
					switch( (*it).first ) {
						case STRING:
							define_body += " \"" + (*it).second + "\"";
							break;
						case CHAR:
							define_body += " \'" + (*it).second;
							break;
						default:
							define_body += " " + (*it).second;
							break;
					} // switch
				} // if it=";"
				break;
			case PARSE_DEF_N: 
				if( (*it).first!= OPERATOR )
						throw runtime_error("Expecting a name.");
				define_name = (*it).second;
				state=PARSE_DEF_E;
				break;
			case PARSE_DEF_E:
				if( (*it).second != "==" )
					throw runtime_error("Expecting == ");
				state=PARSE_DEF_B;
				break;
			case PARSE_DEF_B:
				if( (*it).second == "." ) { // end definition
					SYMTAB->new_userdef(define_name, define_body); 
					define_name="";
					define_body="";
					state=PARSE_NORMAL;
				} else {	// add one more word to the def body
					switch( (*it).first ) {
						case STRING:
							define_body += " \"" + (*it).second + "\"";
							break;
						case CHAR:
							define_body += " \'" + (*it).second;
							break;
						default:
							define_body += " " + (*it).second;
							break;
					} // switch
				} // if it="."
				break;
			case PARSE_IN_SET:
				switch( (*it).first ) {
					case INT: // add the number to the set
						set_target->push_back(
							new JC_objref(new JC_int((*it).second)));
						break;
					case SET: // found a { or a }
						if ((*it).second=="}") {
							contents.push_back(
								new JC_objref(new JC_set(*set_target)));
							state=PARSE_NORMAL;
							set_target->erase(
								set_target->begin(), set_target->end());
							//delete set_target;
							//set_target=new vector<JC_objref*>;
						} else {
							throw runtime_error("Cannot nest sets");
						}
						break;
					default:
						throw runtime_error("Sets can only contain integers");
   						break;
				} // switch over *it.first 
				break;
			case PARSE_IN_SET_IN_LIST:
				switch( (*it).first ) {
					case INT: // add the number to the set
						set_target->push_back(
							new JC_objref(new JC_int((*it).second)));
						break;
					case SET: // found a { or a }
						if ((*it).second=="}") {
							list_target->push_back(
								new JC_objref(new JC_set(*set_target)));
							state=PARSE_IN_LIST;
							set_target->erase(
								set_target->begin(), set_target->end());
							//delete set_target;
							//set_target=new vector<JC_objref*>;
						} else {
							throw runtime_error("Cannot nest sets");
						}
						break;
					default:
						throw runtime_error("Sets can only contain integers");
   						break;
				} // switch over *it.first 
				break;
			case PARSE_IN_LIST:
				switch( (*it).first ) {
					case INT: list_target->push_back(
								new JC_objref(new JC_int((*it).second)));
							break;
					case CHAR: list_target->push_back(
								new JC_objref(new JC_char((*it).second)));
							break;
					case FLOAT: list_target->push_back(
								new JC_objref(new JC_float((*it).second)));
							break;
					case STRING: list_target->push_back(
								new JC_objref(new JC_string((*it).second)));
							break;
					case SET: if ((*it).second=="{") {
								state=PARSE_IN_SET_IN_LIST;
							} else {
								throw runtime_error("Unbalanced sets.");
							}
							break;
					case LIST:
							if ((*it).second=="[") { // start new list
								state=PARSE_IN_LIST;
								stack_of_targets.push_back(list_target);
								list_target = new vector<JC_objref*>;
							} else if ((*it).second=="]") { // end current list
								finished_target=list_target;
								if(stack_of_targets.size()<1) {
									contents.push_back(new JC_objref(
											new JC_list(*finished_target)));
									state=PARSE_NORMAL;
								} else {
									list_target=stack_of_targets.back();
									stack_of_targets.pop_back();
									list_target->push_back(new JC_objref(
											new JC_list(*finished_target)));
								}
							finished_target->erase(finished_target->begin()
									,finished_target->end());
							}
							break;
					case OPERATOR:
							// have to hangle bool types now
							if( (*it).second=="true" ) {
								list_target->push_back(
									new JC_objref(new JC_bool(true)));
							} else if ( (*it).second=="false" ) {
								list_target->push_back(
									new JC_objref(new JC_bool(false)));
							} else {
								list_target->push_back(
									new JC_objref(new JC_op((*it).second)));
							}
							break;
					default:
							break;
				} // end switch over what to add to list
				break;
			case PARSE_NORMAL:
				switch( (*it).first ) {
					case INT:
						contents.push_back(
							new JC_objref(new JC_int((*it).second)));
					break;
					case CHAR:
						contents.push_back(
							new JC_objref(new JC_char((*it).second)));
						break;
					case FLOAT:
						contents.push_back(
							new JC_objref(new JC_float((*it).second)));
						break;
					case STRING:
						contents.push_back(
							new JC_objref(new JC_string((*it).second)));
						break;
					case SET:
						if ((*it).second=="{") {
							state=PARSE_IN_SET;
						} else {
								throw runtime_error("Unbalanced sets.");
						}
						break;
					case LIST:
						if ((*it).second=="[") {
							state=PARSE_IN_LIST;
						} else {
							throw runtime_error("Unbalanced lists.");
						}
						break;
					case OPERATOR:
						if( (*it).second == "DEFINE" ) {
							state=PARSE_DEF_N;
						} else if ( (*it).second == "LIBRA" ) {
							state=PARSE_LIBRA_N;
						} else {
							if( (*it).second=="true" ) {
								contents.push_back(
									new JC_objref(new JC_bool(true)));
							} else if ( (*it).second=="false" ) {
								contents.push_back(
									new JC_objref(new JC_bool(false)));
							} else {
								contents.push_back(
									new JC_objref(new JC_op((*it).second)));
							}
						}
						break;
					default:
						break;
				} // switch over what to add to contents
				break;
			default:
				break;
		} // switch over parse state
	} // loop over tokens
}

