// $Id: BRbReadNode.cpp,v 1.79 2003/10/08 05:19:53 yuya Exp $

#ifdef BRB_READER

////////////////////////////////////////////////////////////////////////////////

#include "BRbReadNode.h"
#include "BRbLevelCounter.h"

////////////////////////////////////////////////////////////////////////////////

extern "C" {
	NODE *logop(enum node_type type, NODE* left, NODE* right); // parse.c
	void top_local_init();                                     // parse.c
	void top_local_setup();                                    // parse.c
	void local_push(int top);                                  // parse.c
	void local_pop();                                          // parse.c
	int local_cnt(ID id);                                      // parse.c
	ID* local_tbl();                                           // parse.c
	struct RVarmap* dyna_push();                               // parse.c
	void dyna_pop(struct RVarmap* vars);                       // parse.c
	NODE* block_append(NODE *head, NODE *tail);                // parse.c
}

////////////////////////////////////////////////////////////////////////////////

BRbReadNode::BRbReadNode(BRbInputBuffer& buffer, BRbDebug& debug)
	: BRbNode(), m_buffer(buffer), m_debug(debug), m_strtbl(debug)
{
}

BRbReadNode::~BRbReadNode()
{
}

////////////////////////////////////////////////////////////////////////////////

NODE*
BRbReadNode::read()
{
	BRbHeader header = read_header();

	if ( header.signature != BRB_FILE_SIGNATURE ) {
		m_debug.raise("invalid signature");
	}

	if ( header.file_version != BRB_FILE_VERSION ) {
		m_debug.raise("invalid version");
	}

	m_strtbl.load(m_buffer);

#ifdef DEBUG
	m_debug.printf("============================================================\n");
#endif

	top_local_init();
	NODE* node = read_node_opt();
	top_local_setup();

#ifdef DEBUG
	m_debug.printf("============================================================\n");
	m_debug.dumpnode(node);
	m_debug.printf("============================================================\n");
#endif

	return node;
}

////////////////////////////////////////////////////////////////////////////////

BRbHeader
BRbReadNode::read_header()
{
	BRbHeader header;
	m_buffer.read_bytes((void*)&header, sizeof(header));
	return header;
}

NODE*
BRbReadNode::read_node()
{
	byte type = read_byte();

#ifdef DEBUG
	BRbLevelCounter counter(type);
	m_debug.printf("R:%02i:%s\n", counter.level(), BRbNode::get_node_name(type));
#endif

	switch ( type ) {
		case NODE_ALIAS:
		case NODE_VALIAS:
		{
			ID new_id = read_id();
			ID old_id = read_id();
			switch ( type ) {
			case NODE_ALIAS:  return NEW_ALIAS(new_id, old_id);
			case NODE_VALIAS: return NEW_VALIAS(new_id, old_id);
			}
		}
		case NODE_AND:
		case NODE_OR:
		{
			NODE* n1st = read_node();
			NODE* n2nd = read_node();
			switch ( type ) {
			case NODE_AND: return logop(NODE_AND, n1st, n2nd);
			case NODE_OR:  return logop(NODE_OR, n1st, n2nd);
			}
		}
		case NODE_ARGS:
		{
			dword cnt  = read_dword();
			dword rest = read_dword();
			NODE* opt  = read_node_opt();

			if ( opt && nd_type(opt) != NODE_BLOCK ) {
				opt = NEW_BLOCK(opt);
			}

			return NEW_ARGS(cnt, opt, rest);
		}
		case NODE_ARGSCAT:
		{
			NODE* head = read_node();
			NODE* body = read_node();
			return NEW_ARGSCAT(head, body);
		}
		case NODE_ARGSPUSH:
		{
			NODE* head = read_node();
			NODE* body = read_node();
			return NEW_ARGSPUSH(head, body);
		}
		case NODE_ARRAY:
		{
			word  n    = read_word();
			word  len  = read_word();
			NODE* node = NEW_ARRAY(read_node());
			NODE* cur  = node;
			node->nd_alen = len;
			for ( word i = 1; i < n; i++ ) {
				cur->nd_next = NEW_ARRAY(read_node());
				cur = cur->nd_next;
			}
			cur->nd_next = NULL;

			return node;
		}
		case NODE_ATTRSET:
		{
			m_debug.raise("NODE_ATTRSET is not implemented");
			break;
		}
		case NODE_BACK_REF:
		{
			byte nth = read_byte();
			int  cnt = read_dword();
			return ::rb_node_newnode(NODE_BACK_REF, 0, nth, cnt);
		}
		case NODE_BEGIN:
		{
			NODE* body = read_node();
			return NEW_BEGIN(body);
		}
		case NODE_BLOCK:
		{
			word len   = read_word();
			NODE *node = NULL;

			for ( word i = 0; i < len; i++ ) {
				node = ::block_append(node, read_node());
			}

			return node;
		}
		case NODE_BLOCK_ARG:
		{
			ID  name = read_id();
			int cnt  = read_dword();
			return ::rb_node_newnode(NODE_BLOCK_ARG, name, 0, cnt);
		}
		case NODE_BLOCK_PASS:
		{
			NODE* body = read_node();
			NODE* iter = read_node();
			return NEW_NODE(NODE_BLOCK_PASS, 0, body, iter);
		}
		case NODE_BMETHOD:
		{
			m_debug.raise("NODE_BMETHOD is not implemented");
			break;
		}
		case NODE_BREAK:
		{
			NODE* stts = read_node_opt();
			return NEW_BREAK(stts);
		}
		case NODE_CALL:
		{
			ID    name = read_id();
			NODE* recv = read_node();
			NODE* args = read_node_opt();
			return NEW_CALL(recv, name, args);
		}
		case NODE_FCALL:
		{
			ID    name = read_id();
			NODE* args = read_node_opt();
			return NEW_FCALL(name, args);
		}
		case NODE_CASE:
		{
			NODE* head = read_node();
			NODE* body = read_node();
			return NEW_CASE(head, body);
		}
		case NODE_WHEN:
		{
			byte  opt  = read_byte();
			NODE* head = read_node();
			NODE* body = read_node_opts(opt, 1);
			NODE* next = read_node_opts(opt, 2);
			return NEW_WHEN(head, body, next);
		}
		case NODE_CDECL:
		{
			ID    id    = read_id();
			NODE* value = read_node_opt();
			NODE* path  = read_node_opt();
			return NEW_CDECL(id, value, path);
		}
		case NODE_CLASS:
		{
			local_push(0);

			byte  opt   = read_byte();
			NODE* cpath = read_node();
			NODE* super = read_node_opts(opt, 1);
			NODE* body  = read_node_opts(opt, 2);

			NODE* node  = NEW_NODE(NODE_CLASS, cpath, body, super);

			local_pop();

			return node;
		}
		case NODE_COLON2:
		{
			ID    name = read_id();
			NODE* head = read_node_opt();
			return NEW_COLON2(head, name);
		}
		case NODE_COLON3:
		{
			ID name = read_id();
			return NEW_COLON3(name);
		}
		case NODE_CONST:
		{
			ID name = read_id();
			return NEW_CONST(name);
		}
		case NODE_CREF:
		{
			m_debug.raise("NODE_CREF is not implemented");
			break;
		}
		case NODE_CVAR:
		{
			ID name = read_id();
			return NEW_CVAR(name);
		}
		case NODE_CVDECL:
		{
			ID    name  = read_id();
			NODE* value = read_node();
			return NEW_CVDECL(name, value);
		}
		case NODE_CVASGN:
		{
			ID    name  = read_id();
			NODE* value = read_node();
			return NEW_CVASGN(name, value);
		}
		case NODE_DASGN:
		{
			ID    vid   = read_id();
			NODE* value = read_node_opt();
			return NEW_DASGN(vid, value);
		}
		case NODE_DASGN_CURR:
		{
			ID    name  = read_id();
			NODE* value = read_node_opt();
			return NEW_DASGN_CURR(name, value);
		}
		case NODE_DEFINED:
		{
			NODE* head = read_node();
			return NEW_DEFINED(head);
		}
		case NODE_DEFN:
		{
			local_push(0);

			ID    noex = read_dword();
			ID    mid  = read_id();
			NODE* defn = read_node();
			NODE* node = NEW_NODE(NODE_DEFN, noex, mid, defn);

			local_pop();

			return node;
		}
		case NODE_DEFS:
		{
			NODE* node = ::rb_node_newnode(NODE_DEFS, 0, 0, 0);
			node->nd_mid  = read_id();
			node->nd_recv = read_node();
			node->nd_defn = read_node();
			return node;
		}
		case NODE_DMETHOD:
		{
			m_debug.raise("NODE_DMETHOD is not implemented");
			break;
		}
		case NODE_DVAR:
		{
			ID name = read_id();
			return NEW_DVAR(name);
		}
		case NODE_DSTR:
		{
			NODE* node = NEW_DSTR(NULL);
			node->nd_lit  = read_literal();
			node->nd_next = read_node_opt();
			return node;
		}
		case NODE_DXSTR:
		{
			NODE* node = NEW_DXSTR(NULL);
			node->nd_lit  = read_literal();
			node->nd_next = read_node_opt();
			return node;
		}
		case NODE_DREGX:
		case NODE_DREGX_ONCE:
		{
			NODE* node = NEW_STR(NULL);
			nd_set_type(node, type);
			node->nd_lit  = read_literal();
			node->nd_next = read_node();
			return node;
		}
		case NODE_ENSURE:
		{
			NODE* head = read_node();
			NODE* ensr = read_node();
			return NEW_ENSURE(head, ensr);
		}
		case NODE_FALSE:
		{
			return NEW_FALSE();
		}
		case NODE_DOT2:
		case NODE_DOT3:
		case NODE_FLIP2:
		case NODE_FLIP3:
		{
			NODE* from = read_node();
			NODE* to   = read_node();
			switch ( type ) {
			case NODE_DOT2:  return NEW_DOT2(from, to);
			case NODE_DOT3:  return NEW_DOT3(from, to);
			case NODE_FLIP2: return NEW_NODE(NODE_FLIP2, from, to, 0);
			case NODE_FLIP3: return NEW_NODE(NODE_FLIP3, from, to, 0);
			}
		}
		case NODE_FBODY:
		{
			m_debug.raise("NODE_FBODY is not implemented");
			break;
		}
		case NODE_FOR:
		{
			NODE* iter = read_node();
			NODE* body = read_node();
			NODE* var  = read_node();
			return NEW_FOR(var, iter, body);
		}
		case NODE_ITER:
		{
			struct RVarmap* varmap = dyna_push();
			NODE* iter = read_node();
			NODE* body = read_node_opt();

			NODE* var;
			if ( read_byte() ) var = (NODE*)1;
			else               var = read_node_opt();

			NODE* node = NEW_ITER(var, iter, body);
			dyna_pop(varmap);
			return node;
		}
		case NODE_GASGN:
		{
			ID    name  = read_id();
			NODE* value = read_node_opt();
			return NEW_GASGN(name, value);
		}
		case NODE_GVAR:
		{
			ID name = read_id();
			return NEW_GVAR(name);
		}
		case NODE_HASH:
		{
			NODE* head = read_node_opt();
			return NEW_HASH(head);
		}
		case NODE_IASGN:
		{
			ID    name  = read_id();
			NODE* value = read_node_opt();
			return NEW_IASGN(name, value);
		}
		case NODE_IF:
		{
			byte  opt  = read_byte();
			NODE* cond = read_node();
			NODE* body = read_node_opts(opt, 1);
			NODE* els  = read_node_opts(opt, 2);
			return NEW_IF(cond, body, els);
		}
		case NODE_IVAR:
		{
			ID name = read_id();
			return NEW_IVAR(name);
		}
		case NODE_LASGN:
		{
			ID    name  = read_id();
			NODE* value = read_node_opt();
			return NEW_LASGN(name, value);
		}
		case NODE_EVSTR:
		{
			VALUE lit = read_literal();
			return ::rb_node_newnode(NODE_EVSTR, lit, 0, 0);
		}
		case NODE_XSTR:
		{
			VALUE lit = read_literal();
			return NEW_XSTR(lit);
		}
		case NODE_LIT:
		{
			VALUE lit = read_literal();
			return NEW_LIT(lit);
		}
		case NODE_LVAR:
		{
			ID name = read_id();
			return NEW_LVAR(name);
		}
		case NODE_MASGN:
		{
			byte  opt  = read_byte();
			NODE* node = NEW_MASGN(NULL, NULL);
			node->nd_head  = read_node_opts(opt, 1);
			node->nd_value = read_node_opts(opt, 2);
			if ( read_byte() ) node->nd_args = (NODE*)0xFFFFFFFF;
			else               node->nd_args = read_node_opt();
			return node;
		}
		case NODE_MATCH:
		{
			NODE* head = read_node();
			return NEW_MATCH(head);
		}
		case NODE_MATCH2:
		{
			NODE* recv  = read_node();
			NODE* value = read_node();
			return NEW_MATCH2(recv, value);
		}
		case NODE_MATCH3:
		{
			NODE* recv  = read_node();
			NODE* value = read_node();
			return NEW_MATCH3(recv, value);
		}
		case NODE_METHOD:
		{
			m_debug.raise("NODE_METHOD is not implemented");
			break;
		}
		case NODE_MODULE:
		{
			local_push(0);

			NODE* cpath = read_node();
			NODE* body  = read_node_opt();
			NODE* node  = NEW_MODULE(cpath, body);

			local_pop();

			return node;
		}
		case NODE_NEWLINE:
		{
			word  lineno = read_word();
			NODE* node = NEW_NEWLINE(read_node());
			node->nd_nth = lineno;
			return node;
		}
		case NODE_NEXT:
		{
			NODE* stts = read_node_opt();
			return NEW_NEXT(stts);
		}
		case NODE_NIL:
		{
			return NEW_NIL();
		}
		case NODE_NOT:
		{
			NODE* body = read_node();
			return NEW_NOT(body);
		}
		case NODE_NTH_REF:
		{
			byte nth = read_byte();
			int  cnt = read_dword();
			return ::rb_node_newnode(NODE_NTH_REF, 0, nth, cnt);
		}
		case NODE_OP_ASGN1:
		{
			NODE* recv = read_node();
			NODE* args = read_node();
			ID    mid;
			if ( read_byte() ) mid = read_byte();
			else               mid = read_id();
			return NEW_OP_ASGN1(recv, mid, args);
		}
		case NODE_OP_ASGN2:
		{
			NODE* recv  = read_node();
			NODE* value = read_node();
			ID    vid   = read_id();
			ID    aid   = read_id();
			ID    mid;
			if ( read_byte() ) mid = read_byte();
			else               mid = read_id();
			return NEW_NODE(NODE_OP_ASGN2, recv, value, NEW_NODE(NODE_OP_ASGN2, vid, mid, aid));
		}
		case NODE_OP_ASGN_AND:
		{
			NODE* head  = read_node();
			NODE* value = read_node();
			return NEW_OP_ASGN_AND(head, value);
		}
		case NODE_OP_ASGN_OR:
		{
			NODE* head  = read_node();
			NODE* value = read_node();
			return NEW_OP_ASGN_OR(head, value);
		}
		case NODE_OPT_N:
		{
			m_debug.raise("NODE_OPT_N is not implemented");
			break;
		}
		case NODE_POSTEXE:
		{
			return NEW_POSTEXE();
		}
		case NODE_REDO:
		{
			return NEW_REDO();
		}
		case NODE_RESCUE:
		{
			NODE* head = read_node();
			NODE* resq = read_node();
			NODE* els  = read_node_opt();
			return NEW_RESCUE(head, resq, els);
		}
		case NODE_RESBODY:
		{
			byte  opt  = read_byte();
			NODE* body = read_node_opts(opt, 1);
			NODE* args = read_node_opts(opt, 2);
			NODE* head = read_node_opts(opt, 3);
			return NEW_RESBODY(args, body, head);
		}
/*
		case NODE_RESTARGS:
		{
			NODE* head = read_node();
			return NEW_RESTARGS(head);
		}
*/
		case NODE_RETRY:
		{
			return NEW_RETRY();
		}
		case NODE_RETURN:
		{
			NODE* stts = read_node_opt();
			return NEW_RETURN(stts);
		}
		case NODE_SCLASS:
		{
			local_push(0);

			NODE* recv = read_node();
			NODE* body = read_node();
			NODE* node = NEW_SCLASS(recv, body);

			local_pop();

			return node;
		}
		case NODE_SCOPE:
		{
			local_push(0);

			word cnt = read_word();

			if ( cnt > 0 ) {
				for ( int i = 0; i < cnt; i++ ) {
					ID id = read_id();
					::local_cnt(id);
				}
			}

			NODE* next = read_node_opt();
			NODE* node = NEW_SCOPE(next);

			local_pop();

			return node;
		}
		case NODE_SELF:
		{
			return NEW_SELF();
		}
		case NODE_STR:
		{
			VALUE str = read_string();
			return NEW_STR(str);
		}
		case NODE_SUPER:
		{
			NODE* args = read_node_opt();
			return NEW_SUPER(args);
		}
		case NODE_TRUE:
		{
			return NEW_TRUE();
		}
		case NODE_UNDEF:
		{
			ID name = read_id();
			return NEW_UNDEF(name);
		}
		case NODE_UNTIL:
		{
			NODE* cond  = read_node();
			NODE* body  = read_node_opt();
			byte  state = read_byte();
			return NEW_UNTIL(cond, body, state);
		}
		case NODE_WHILE:
		{
			NODE* cond  = read_node();
			NODE* body  = read_node_opt();
			byte  state = read_byte();
			return NEW_WHILE(cond, body, state);
		}
		case NODE_VCALL:
		{
			ID name = read_id();
			return NEW_VCALL(name);
		}
		case NODE_YIELD:
		{
			NODE* stts  = read_node_opt();
			dword state = read_dword();
			return NEW_YIELD(stts, state);
		}
		case NODE_ZARRAY:
		{
			return NEW_ZARRAY();
		}
		case NODE_ZSUPER:
		{
			return NEW_ZSUPER();
		}

		case NODE_ATTRASGN:
		{
			ID    mid  = read_id();
			NODE* recv = read_node();
			NODE* args = read_node_opt();
			return NEW_ATTRASGN(recv, mid, args);
		}
		case NODE_DSYM:
		{
			m_debug.raise("NODE_DSYM is not implemented");
			break;
		}
		case NODE_RESTARY:
		{
			m_debug.raise("NODE_RESTARY is not implemented");
			break;
		}
		case NODE_RESTARY2:
		{
			NODE* head = read_node();
			return NEW_RESTARY2(head);
		}
		case NODE_SPLAT:
		{
			m_debug.raise("NODE_SPLAT is not implemented");
			break;
		}
		case NODE_SVALUE:
		{
			m_debug.raise("NODE_SVALUE is not implemented");
			break;
		}

		default:
		{
#ifdef DEBUG
			counter.print_stack();
#endif
			m_debug.raise("[BUG] Unknown node type -- 0x%02X(%i)\n", type, type);
			break;
		}
	}

	return NULL;
}


inline NODE*
BRbReadNode::read_node_opt()
{
	byte has_node = read_byte();

	if ( has_node == 0xFC ) {
		return read_node();
	} else if ( has_node == 0xFD ) {
		return NULL;
	} else {
		m_debug.raise("[BUG] Unknown optional code -- 0x%02", has_node);
		return NULL;
	}
}

inline NODE*
BRbReadNode::read_node_opts(byte opt, int n)
{
	const byte flag = 0xF0 | (1 << (n - 1));

	if ( (opt & flag) == flag ) {
		return read_node();
	} else {
		return NULL;
	}
}

inline byte
BRbReadNode::read_byte()
{
	return m_buffer.read_byte();
}

inline word
BRbReadNode::read_word()
{
	return m_buffer.read_word();
}

inline dword
BRbReadNode::read_dword()
{
	return m_buffer.read_dword();
}

inline const char*
BRbReadNode::read_cstr()
{
	return m_strtbl.get(read_word());
}

inline const char*
BRbReadNode::read_cstr(word* len)
{
	return m_strtbl.get(read_word(), len);
}

inline VALUE
BRbReadNode::read_string()
{
	word len = 0;
	const char* cstr = read_cstr(&len);
	VALUE rstr = ::rb_str_new(cstr, len);
	return rstr;
}

inline ID
BRbReadNode::read_id()
{
	return ::rb_intern(read_cstr());
}

inline VALUE
BRbReadNode::read_literal()
{
	byte klass = read_byte();
	switch ( klass ) {
		case 0: return read_dword();
		case 1: return ID2SYM(read_id());
		case 2: return read_string();
		case 3: return read_object();
		default: m_debug.raise("[BUG] Unknown literal class -- 0x%02X", klass); return Qnil;
	}
}

inline VALUE
BRbReadNode::read_object()
{
	static VALUE mod = ::rb_eval_string("::Marshal");
	static ID    mid = ::rb_intern("load");
	return ::rb_funcall(mod, mid, 1, read_string());
}

////////////////////////////////////////////////////////////////////////////////

#endif

////////////////////////////////////////////////////////////////////////////////
