/**
 * $Id: compile.h 150 2005-03-01 04:54:42Z ko1 $
 * Create : K.S. 04/01/01 23:36:57
*/

#ifndef _COMPILER_H_INCLUDED_
#define _COMPILER_H_INCLUDED_

#include "version.h"

#if YARVDEBUG > CPDEBUG
#undef  CPDEBUG
#define CPDEBUG YARVDEBUG
#endif

/*  */
/**
 * debug function(macro) interface depend on CPDEBUG
 *
 * debug level:
 *  0: no debug output
 *  1: show node type
 *  2: show node important parameters
 *  ...
 *  5: show other parameters
 * 10: show every AST array
 */

//#define CPDEBUG 2

#if CPDEBUG > 0

#define debugp(header, value)                   \
  (debug_indent(0, CPDEBUG, gl_node_level * 2), \
   debug_value(0, CPDEBUG, header, value))

#define debugi(header, id)                      \
  (debug_indent(0, CPDEBUG, gl_node_level * 2), \
   debug_id(0, CPDEBUG, header, id))

#define debugp_param(header, value)             \
  (debug_indent(1, CPDEBUG, gl_node_level * 2), \
   debug_value(1, CPDEBUG, header, value))

#define debugp_verbose(header, value)           \
  (debug_indent(2, CPDEBUG, gl_node_level * 2), \
   debug_value(2, CPDEBUG, header, value))

#define debugp_verbose_node(header, value)       \
  (debug_indent(10, CPDEBUG, gl_node_level * 2), \
   debug_value(10, CPDEBUG, header, value))

#define debug_nodeprint(node)                    \
  debug_indent(-1, CPDEBUG, gl_node_level*2);    \
  printf("node: %s\n", node); \
  gl_node_level ++;

#define debug_nodeprint_close()  gl_node_level --;

#define debugs debug_indent(-1, CPDEBUG, gl_node_level*2), printf

#else

#define debugp(header, value)              value
#define debugi(header, id)                 id
#define debugp_verbose(header, value)      value
#define debugp_verbose_node(header, value) value
#define debugp_param(header, value)        value
#define debug_nodeprint(node)
#define debug_nodeprint_close()
#define debugs 
#endif


/* create empty sequence */
#define NEW_SEQ()                           \
  (GC_CHECK(), debugp_verbose_node("new_seq", -1),  \
   rb_ary_new())

/* create new label */
#define NEW_LABEL(l)                 \
  (GC_CHECK(), new_label(l))

/* create insns sequence with 1 insn */
#define NEW_INSN_SEQ(line, insn)     \
  (tmp = NEW_SEQ(),                  \
   ADD_INSN(tmp, line, insn),        \
   debugp_verbose_node("new_insn_seq", tmp))

/* create insns sequence with an insn and an operand */
#define NEW_INSN_SEQ1(line, insn, op1)  \
  (tmp = NEW_SEQ(),                     \
   ADD_INSN1(tmp, line, insn, op1),     \
   debugp_verbose_node("new_insn_seq1", tmp))

#define iseq_filename(iseq) \
  (((struct iseq_object*)DATA_PTR(iseq))->file_name)

#define NEW_ISEQOBJ(node, name, type)       \
  NEW_CHILD_ISEQOBJ(node, name, 0, type)


#define NEW_CHILD_ISEQOBJ(node, name, parent, type)       \
  (lv            = gl_node_level,                         \
   gl_node_level = 0,                                     \
   args[0]       = (VALUE)node,                           \
   args[1]       = name,                                  \
   args[2]       = iseq_filename(self),                   \
   args[3]       = parent,                                \
   args[4]       = type,                                  \
   gl_tmp        = rb_class_new_instance(5, args,         \
                                         cYarvISeq),      \
   gl_node_level = lv,                                    \
   gl_tmp)

/* add instructions */
#define ADD_SEQ(seq, seq2)    \
  (rb_ary_push(seq, seq2),  \
   debugp_verbose_node("add_seq", seq))

/* add an instruction */
#define ADD_INSN(seq, line, insn)                       \
  (rb_ary_push(seq, new_insn(BIN(insn), line, Qfalse)), \
   debugp_verbose_node("add_insn", seq))

/* add an instruction with label operand */
#define ADD_INSNL(seq, line, insn, label)         \
  (rb_ary_push(seq, new_insn(BIN(insn), line, rb_ary_new3(1, label))), \
   debugp_verbose_node("add_insnl", seq))

/* add an instruction with an operand */
#define ADD_INSN1(seq, line, insn, op1)                              \
  (rb_ary_push(seq, new_insn(BIN(insn), line, rb_ary_new3(1, op1))), \
   debugp_verbose_node("add_insn1", seq))

/* add an instruction with two operands */
#define ADD_INSN2(seq, line, insn, op1, op2)             \
  (rb_ary_push(seq, new_insn(BIN(insn), line,            \
                             rb_ary_new3(2, op1, op2))), \
   debugp_verbose_node("add_insn2", seq))

#define ADD_INSN3(seq, line, insn, op1, op2, op3)             \
  (rb_ary_push(seq, new_insn(BIN(insn), line,                 \
                             rb_ary_new3(3, op1, op2, op3))), \
   debugp_verbose_node("add_insn3", seq))

#define ADD_INSN5(seq, line, insn, o1, o2, o3, o4, o5)             \
  (rb_ary_push(seq, new_insn(BIN(insn), line,                      \
                             rb_ary_new3(5, o1, o2, o3, o4, o5))), \
   debugp_verbose_node("add_insn5", seq))


/* add label */
#define ADD_LABEL(seq, label) \
  (rb_ary_push(seq, label),   \
   debugp_verbose_node("add_label", seq))  \

#define ADD_CATCH_ENTRY(type, ls, le, iseq, sp, lc) \
  (tmp = rb_ary_new(),                              \
   rb_ary_push(tmp, type),                          \
   rb_ary_push(tmp, ls),                            \
   rb_ary_push(tmp, le),                            \
   rb_ary_push(tmp, iseq),                          \
   rb_ary_push(tmp, I2F(sp)),                       \
   rb_ary_push(tmp, lc),                            \
   rb_ary_push(iseqobj->catch_table_ary, tmp))


/* Specific Insn factory */

#define ADD_SEND(seq, line, id, argc) \
  ADD_SEND_R(seq, line, id, argc, Qfalse, I2F(0))

#define ADD_SEND_R(seq, line, id, argc, block, flag) \
  ADD_INSN5(seq, line, send, id, argc, block, flag, 0)


/* compile node */
#define COMPILE(desc, node) \
  (debugs("== " desc "\n"), \
   iseq_compile_each(self, node, 0))

/* compile node, this node's value will be poped */
#define COMPILE_POPED(desc, node)    \
  (debugs("== " desc "\n"),          \
   iseq_compile_each(self, node, 1))

/* compile node, which is poped when 'poped' is true */
#define COMPILE_(desc, node, poped)  \
  (debugs("== " desc "\n"),          \
   iseq_compile_each(self, node, poped))

#define OPERAND(insn_obj, i) \
  (RARRAY(((struct insn_object*)DATA_PTR(insn_obj))->operands)->ptr[i])

#define INSN_OF(insn) \
  (((struct insn_object*)DATA_PTR(insn))->insn_id)
     
#define I2F(x) INT2FIX(x)

static VALUE new_insn(int insn, int line_no, VALUE operands);



/* error */
#define COMPILE_ERROR(strs)                        \
{                                                  \
  VALUE tmp = ruby_errinfo;                        \
  if(CPDEBUG)rb_bug(strs);                         \
  ruby_errinfo = iseqobj->compile_data->err_info;  \
  rb_compile_error strs;                           \
  iseqobj->compile_data->err_info = ruby_errinfo;  \
  ruby_errinfo = tmp;                              \
  ret = 0;                                         \
  break;                                           \
}

#endif	// _COMPILER_H_INCLUDED_

